From b1d7567226ccb374abfb757031fa1972339cbe1a Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh Date: Wed, 2 Dec 2020 07:14:51 +0800 Subject: [PATCH] win32: Add ComWorker to run all COM function on same thread --- meson.build | 1 + src/win32/ComWorker.cxx | 49 ++++++++++++++++++ src/win32/ComWorker.hxx | 110 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 src/win32/ComWorker.cxx create mode 100644 src/win32/ComWorker.hxx 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::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 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 +#include +#include +#include + +#include "thread/Future.hxx" +#include "thread/Mutex.hxx" +#include "thread/Thread.hxx" +#include "win32/WinEvent.hxx" +#include +#include + +// 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 &function) { + spsc_buffer.push(function); + event.Set(); + } + + boost::lockfree::spsc_queue> 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 + static auto Async(Function &&function, Args &&...args) { + using R = std::invoke_result_t, + std::decay_t...>; + auto promise = std::make_shared>(); + auto future = promise->get_future(); + thread->Push([function = std::forward(function), + args = std::make_tuple(std::forward(args)...), + promise = std::move(promise)]() mutable { + try { + if constexpr (std::is_void_v) { + std::apply(std::forward(function), + std::move(args)); + promise->set_value(); + } else { + promise->set_value(std::apply( + std::forward(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 thread; +}; + +#endif