summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShen-Ta Hsieh <ibmibmibm.tw@gmail.com>2020-12-02 07:14:51 +0800
committerMax Kellermann <max@musicpd.org>2021-03-04 18:43:16 +0100
commitb1d7567226ccb374abfb757031fa1972339cbe1a (patch)
tree944f3ce257bed618c8abc27363dc5c63a72cd6d3
parent5103eb303977ea79d8f7b687272737adf9ffe237 (diff)
win32: Add ComWorker to run all COM function on same thread
-rw-r--r--meson.build1
-rw-r--r--src/win32/ComWorker.cxx49
-rw-r--r--src/win32/ComWorker.hxx110
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