diff --git a/Source/Core/Common/Functional.h b/Source/Core/Common/Functional.h index 7f96664041b..bde566ae7c1 100644 --- a/Source/Core/Common/Functional.h +++ b/Source/Core/Common/Functional.h @@ -45,7 +45,13 @@ private: struct Func : FuncBase { explicit Func(F&& f) : func{std::forward(f)} {} - result_type Invoke(Args... args) override { return func(std::forward(args)...); } + result_type Invoke(Args... args) override + { + if constexpr (std::is_void_v) + func(std::forward(args)...); + else + return func(std::forward(args)...); + } std::decay_t func; }; diff --git a/Source/Core/Common/Mutex.h b/Source/Core/Common/Mutex.h index 22aebd08b1b..fbb7c1a4c7f 100644 --- a/Source/Core/Common/Mutex.h +++ b/Source/Core/Common/Mutex.h @@ -48,4 +48,18 @@ using AtomicMutex = detail::AtomicMutexBase; // Very fast to lock and unlock when uncontested (~3x faster than std::mutex). using SpinMutex = detail::AtomicMutexBase; +// This "mutex" class provides no actual thread synchronization. +// It has a std::shared_mutex interface only to be compatible with standard locks. +// Useful when template parameters can determine that a mutex is not actually needed. +struct DummyMutex +{ + constexpr void lock() {} + constexpr bool try_lock() { return true; } + constexpr void unlock() {} + + constexpr void lock_shared() {} + constexpr bool try_lock_shared() { return true; } + constexpr void unlock_shared() {} +}; + } // namespace Common diff --git a/Source/Core/Common/WorkQueueThread.h b/Source/Core/Common/WorkQueueThread.h index 8816ddda16d..e054540ddd2 100644 --- a/Source/Core/Common/WorkQueueThread.h +++ b/Source/Core/Common/WorkQueueThread.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,6 +11,8 @@ #include #include "Common/Event.h" +#include "Common/Functional.h" +#include "Common/Mutex.h" #include "Common/SPSCQueue.h" #include "Common/Thread.h" @@ -17,12 +20,10 @@ namespace Common { namespace detail { -template +template class WorkQueueThreadBase final { public: - using FunctionType = std::function; - WorkQueueThreadBase() = default; WorkQueueThreadBase(std::string name, FunctionType function) { @@ -99,7 +100,7 @@ public: } private: - using CommandFunction = std::function; + using CommandFunction = MoveOnlyFunction; // Blocking. void RunCommand(CommandFunction cmd) @@ -128,25 +129,13 @@ private: m_commands.Clear(); } - auto GetLockGuard() - { - struct DummyLockGuard - { - // Silences unused variable warning. - ~DummyLockGuard() { void(); } - }; - - if constexpr (IsSingleProducer) - return DummyLockGuard{}; - else - return std::lock_guard{m_mutex}; - } + auto GetLockGuard() { return std::lock_guard{m_mutex}; } bool IsRunning() { return m_thread.joinable(); } void ThreadLoop(const std::string& thread_name, const FunctionType& function) { - Common::SetCurrentThreadName(thread_name.c_str()); + SetCurrentThreadName(thread_name.c_str()); while (true) { @@ -173,35 +162,32 @@ private: } std::thread m_thread; - Common::WaitableSPSCQueue m_items; - Common::WaitableSPSCQueue m_commands; - Common::Event m_event; + WaitableSPSCQueue m_items; + WaitableSPSCQueue m_commands; + Event m_event; - using DummyMutex = std::type_identity; using ProducerMutex = std::conditional_t; ProducerMutex m_mutex; }; // A WorkQueueThread-like class that takes functions to invoke. -template