diff options
author | Shen-Ta Hsieh <ibmibmibm.tw@gmail.com> | 2020-12-02 07:14:51 +0800 |
---|---|---|
committer | Max Kellermann <max@musicpd.org> | 2021-03-04 18:43:16 +0100 |
commit | b1d7567226ccb374abfb757031fa1972339cbe1a (patch) | |
tree | 944f3ce257bed618c8abc27363dc5c63a72cd6d3 | |
parent | 5103eb303977ea79d8f7b687272737adf9ffe237 (diff) |
win32: Add ComWorker to run all COM function on same thread
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/win32/ComWorker.cxx | 49 | ||||
-rw-r--r-- | src/win32/ComWorker.hxx | 110 |
3 files changed, 160 insertions, 0 deletions
diff --git a/meson.build b/meson.build index 0b0c78921..d72da267e 100644 --- a/meson.build +++ b/meson.build @@ -322,6 +322,7 @@ sources = [ if is_windows sources += [ 'src/win32/Win32Main.cxx', + 'src/win32/ComWorker.cxx', ] endif diff --git a/src/win32/ComWorker.cxx b/src/win32/ComWorker.cxx new file mode 100644 index 000000000..d708dffbe --- /dev/null +++ b/src/win32/ComWorker.cxx @@ -0,0 +1,49 @@ +/* + * Copyright 2020 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "ComWorker.hxx" +#include "Log.hxx" +#include "thread/Name.hxx" +#include "util/Domain.hxx" +#include "win32/Com.hxx" + +namespace { +static constexpr Domain com_worker_domain("com_worker"); +} + +Mutex COMWorker::mutex; +unsigned int COMWorker::reference_count = 0; +std::optional<COMWorker::COMWorkerThread> COMWorker::thread; + +void COMWorker::COMWorkerThread::Work() noexcept { + FormatDebug(com_worker_domain, "Working thread started"); + SetThreadName("COM Worker"); + COM com{true}; + while (true) { + if (!running_flag.test_and_set()) { + FormatDebug(com_worker_domain, "Working thread ended"); + return; + } + while (!spsc_buffer.empty()) { + std::function<void()> function; + spsc_buffer.pop(function); + function(); + } + event.Wait(200); + } +} diff --git a/src/win32/ComWorker.hxx b/src/win32/ComWorker.hxx new file mode 100644 index 000000000..cd3bd5db6 --- /dev/null +++ b/src/win32/ComWorker.hxx @@ -0,0 +1,110 @@ +/* + * Copyright 2020 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_WIN32_COM_WORKER_HXX +#define MPD_WIN32_COM_WORKER_HXX + +#include <boost/lockfree/spsc_queue.hpp> +#include <condition_variable> +#include <mutex> +#include <optional> + +#include "thread/Future.hxx" +#include "thread/Mutex.hxx" +#include "thread/Thread.hxx" +#include "win32/WinEvent.hxx" +#include <objbase.h> +#include <windows.h> + +// Worker thread for all COM operation +class COMWorker { +private: + class COMWorkerThread : public Thread { + public: + COMWorkerThread() : Thread{BIND_THIS_METHOD(Work)} {} + + private: + friend class COMWorker; + void Work() noexcept; + void Finish() noexcept { + running_flag.clear(); + event.Set(); + } + void Push(const std::function<void()> &function) { + spsc_buffer.push(function); + event.Set(); + } + + boost::lockfree::spsc_queue<std::function<void()>> spsc_buffer{32}; + std::atomic_flag running_flag = true; + WinEvent event{}; + }; + +public: + static void Aquire() { + std::unique_lock locker(mutex); + if (reference_count == 0) { + thread.emplace(); + thread->Start(); + } + ++reference_count; + } + static void Release() noexcept { + std::unique_lock locker(mutex); + --reference_count; + if (reference_count == 0) { + thread->Finish(); + thread->Join(); + thread.reset(); + } + } + + template <typename Function, typename... Args> + static auto Async(Function &&function, Args &&...args) { + using R = std::invoke_result_t<std::decay_t<Function>, + std::decay_t<Args>...>; + auto promise = std::make_shared<Promise<R>>(); + auto future = promise->get_future(); + thread->Push([function = std::forward<Function>(function), + args = std::make_tuple(std::forward<Args>(args)...), + promise = std::move(promise)]() mutable { + try { + if constexpr (std::is_void_v<R>) { + std::apply(std::forward<Function>(function), + std::move(args)); + promise->set_value(); + } else { + promise->set_value(std::apply( + std::forward<Function>(function), + std::move(args))); + } + } catch (...) { + promise->set_exception(std::current_exception()); + } + }); + return future; + } + +private: + static Mutex mutex; + static unsigned int reference_count; + static std::optional<COMWorkerThread> thread; +}; + +#endif |