From f9fb39d383bb462293ea1104e29c5e6651e2d0e3 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 14 Apr 2014 00:26:23 +0200 Subject: [PATCH 1/5] Common: Add a 'Flag' class that is used to encapsulate a boolean flag manipulated from several threads --- Source/Core/Common/Flag.h | 48 +++++++++++++++++++++ Source/UnitTests/Common/CMakeLists.txt | 1 + Source/UnitTests/Common/FlagTest.cpp | 60 ++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 Source/Core/Common/Flag.h create mode 100644 Source/UnitTests/Common/FlagTest.cpp diff --git a/Source/Core/Common/Flag.h b/Source/Core/Common/Flag.h new file mode 100644 index 0000000000..9b8fa3e28e --- /dev/null +++ b/Source/Core/Common/Flag.h @@ -0,0 +1,48 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +// Abstraction for a simple flag that can be toggled in a multithreaded way. +// It exposes a very simple API: +// * Set(bool = true): sets the Flag +// * IsSet(): tests if the flag is set +// * Clear(): clears the flag (equivalent to Set(false)). + +#pragma once + +#include + +namespace Common { + +class Flag final +{ +public: + // Declared as explicit since we do not want "= true" to work on a flag + // object - it should be made explicit that a flag is *not* a normal + // variable. + explicit Flag(bool initial_value = false) : m_val(initial_value) {} + + void Set(bool val = true) + { + m_val.store(val); + } + + void Clear() + { + Set(false); + } + + bool IsSet() const + { + return m_val.load(); + } + +private: + // We are not using std::atomic_bool here because MSVC sucks as of VC++ + // 2013 and does not implement the std::atomic_bool(bool) constructor. + // + // Re-evaluate next time we upgrade that piece of shit. + std::atomic m_val; +}; + +} // namespace Common diff --git a/Source/UnitTests/Common/CMakeLists.txt b/Source/UnitTests/Common/CMakeLists.txt index 78f9938e1c..11fa2c57e0 100644 --- a/Source/UnitTests/Common/CMakeLists.txt +++ b/Source/UnitTests/Common/CMakeLists.txt @@ -2,4 +2,5 @@ add_dolphin_test(BitFieldTest BitFieldTest.cpp common) add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp common) add_dolphin_test(FifoQueueTest FifoQueueTest.cpp common) add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp common) +add_dolphin_test(FlagTest FlagTest.cpp common) add_dolphin_test(MathUtilTest MathUtilTest.cpp common) diff --git a/Source/UnitTests/Common/FlagTest.cpp b/Source/UnitTests/Common/FlagTest.cpp new file mode 100644 index 0000000000..ef73d27728 --- /dev/null +++ b/Source/UnitTests/Common/FlagTest.cpp @@ -0,0 +1,60 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Flag.h" + +using Common::Flag; + +TEST(Flag, Simple) +{ + Flag f; + EXPECT_FALSE(f.IsSet()); + + f.Set(); + EXPECT_TRUE(f.IsSet()); + + f.Clear(); + EXPECT_FALSE(f.IsSet()); + + f.Set(false); + EXPECT_FALSE(f.IsSet()); + + Flag f2(true); + EXPECT_TRUE(f2.IsSet()); +} + +TEST(Flag, MultiThreaded) +{ + Flag f; + int count = 0; + const int ITERATIONS_COUNT = 100000; + + auto setter = [&f]() { + for (int i = 0; i < ITERATIONS_COUNT; ++i) + { + while (f.IsSet()); + f.Set(); + } + }; + + auto clearer = [&f, &count]() { + for (int i = 0; i < ITERATIONS_COUNT; ++i) + { + while (!f.IsSet()); + count++; + f.Clear(); + } + }; + + std::thread setter_thread(setter); + std::thread clearer_thread(clearer); + + setter_thread.join(); + clearer_thread.join(); + + EXPECT_EQ(ITERATIONS_COUNT, count); +} From 6bdcbad3e4488c85ae402dcb44f65005769c64b9 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 14 Apr 2014 01:15:23 +0200 Subject: [PATCH 2/5] Common: Move the Event class to a separate file, and add tests for it. Fix includes everywhere to match this. --- Source/Core/AudioCommon/AOSoundStream.h | 4 +- Source/Core/AudioCommon/DSoundStream.cpp | 2 + Source/Core/AudioCommon/DSoundStream.h | 3 +- Source/Core/AudioCommon/OpenALStream.cpp | 2 + Source/Core/AudioCommon/OpenALStream.h | 3 +- Source/Core/AudioCommon/OpenSLESStream.h | 3 +- Source/Core/AudioCommon/XAudio2Stream.cpp | 1 + Source/Core/AudioCommon/XAudio2Stream.h | 2 +- Source/Core/AudioCommon/XAudio2_7Stream.cpp | 1 + Source/Core/AudioCommon/XAudio2_7Stream.h | 2 +- Source/Core/Common/Event.h | 59 +++++++++++++++++++++ Source/Core/Common/Thread.h | 39 -------------- Source/Core/Core/DSP/DSPCore.cpp | 2 +- Source/Core/Core/HW/CPU.cpp | 3 +- Source/Core/Core/HW/DSPLLE/DSPLLE.cpp | 4 +- Source/Core/Core/State.cpp | 4 +- Source/Core/DolphinWX/Debugger/CodeWindow.h | 2 +- Source/Core/DolphinWX/Frame.h | 2 +- Source/Core/DolphinWX/MainAndroid.cpp | 2 +- Source/Core/DolphinWX/MainNoGUI.cpp | 2 +- Source/UnitTests/Common/CMakeLists.txt | 1 + Source/UnitTests/Common/EventTest.cpp | 42 +++++++++++++++ 22 files changed, 132 insertions(+), 53 deletions(-) create mode 100644 Source/Core/Common/Event.h create mode 100644 Source/UnitTests/Common/EventTest.cpp diff --git a/Source/Core/AudioCommon/AOSoundStream.h b/Source/Core/AudioCommon/AOSoundStream.h index 5900a8c8b5..8d8cc5203e 100644 --- a/Source/Core/AudioCommon/AOSoundStream.h +++ b/Source/Core/AudioCommon/AOSoundStream.h @@ -5,7 +5,9 @@ #pragma once #include "AudioCommon/SoundStream.h" -#include "Common/Thread.h" +#include "Common/Event.h" +#include "Common/StdMutex.h" +#include "Common/StdThread.h" #if defined(HAVE_AO) && HAVE_AO #include diff --git a/Source/Core/AudioCommon/DSoundStream.cpp b/Source/Core/AudioCommon/DSoundStream.cpp index 2ffbe9794b..df96a9551f 100644 --- a/Source/Core/AudioCommon/DSoundStream.cpp +++ b/Source/Core/AudioCommon/DSoundStream.cpp @@ -9,6 +9,8 @@ #include "AudioCommon/AudioCommon.h" #include "AudioCommon/DSoundStream.h" +#include "Common/StdThread.h" +#include "Common/Thread.h" bool DSound::CreateBuffer() { diff --git a/Source/Core/AudioCommon/DSoundStream.h b/Source/Core/AudioCommon/DSoundStream.h index 331c4581d3..d885a17664 100644 --- a/Source/Core/AudioCommon/DSoundStream.h +++ b/Source/Core/AudioCommon/DSoundStream.h @@ -5,7 +5,8 @@ #pragma once #include "AudioCommon/SoundStream.h" -#include "Common/Thread.h" +#include "Common/Event.h" +#include "Common/StdThread.h" #ifdef _WIN32 #include diff --git a/Source/Core/AudioCommon/OpenALStream.cpp b/Source/Core/AudioCommon/OpenALStream.cpp index b2f820c453..1c1a802528 100644 --- a/Source/Core/AudioCommon/OpenALStream.cpp +++ b/Source/Core/AudioCommon/OpenALStream.cpp @@ -5,6 +5,8 @@ #include "AudioCommon/aldlist.h" #include "AudioCommon/DPL2Decoder.h" #include "AudioCommon/OpenALStream.h" +#include "Common/StdThread.h" +#include "Common/Thread.h" #if defined HAVE_OPENAL && HAVE_OPENAL diff --git a/Source/Core/AudioCommon/OpenALStream.h b/Source/Core/AudioCommon/OpenALStream.h index 0c3f932a38..940bb090f4 100644 --- a/Source/Core/AudioCommon/OpenALStream.h +++ b/Source/Core/AudioCommon/OpenALStream.h @@ -5,7 +5,8 @@ #pragma once #include "AudioCommon/SoundStream.h" -#include "Common/Thread.h" +#include "Common/Event.h" +#include "Common/StdThread.h" #include "Core/Core.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/SystemTimers.h" diff --git a/Source/Core/AudioCommon/OpenSLESStream.h b/Source/Core/AudioCommon/OpenSLESStream.h index 08deec6eb1..9ba190728d 100644 --- a/Source/Core/AudioCommon/OpenSLESStream.h +++ b/Source/Core/AudioCommon/OpenSLESStream.h @@ -5,7 +5,8 @@ #pragma once #include "AudioCommon/SoundStream.h" -#include "Common/Thread.h" +#include "Common/Event.h" +#include "Common/StdThread.h" class OpenSLESStream final : public SoundStream { diff --git a/Source/Core/AudioCommon/XAudio2Stream.cpp b/Source/Core/AudioCommon/XAudio2Stream.cpp index d5964f0e0e..81af045300 100644 --- a/Source/Core/AudioCommon/XAudio2Stream.cpp +++ b/Source/Core/AudioCommon/XAudio2Stream.cpp @@ -5,6 +5,7 @@ #include #include "AudioCommon/AudioCommon.h" #include "AudioCommon/XAudio2Stream.h" +#include "Common/Event.h" #ifndef XAUDIO2_DLL #error You are building this module against the wrong version of DirectX. You probably need to remove DXSDK_DIR from your include path. diff --git a/Source/Core/AudioCommon/XAudio2Stream.h b/Source/Core/AudioCommon/XAudio2Stream.h index be76d919e8..9083abdc3b 100644 --- a/Source/Core/AudioCommon/XAudio2Stream.h +++ b/Source/Core/AudioCommon/XAudio2Stream.h @@ -11,7 +11,7 @@ #include #include "AudioCommon/SoundStream.h" -#include "Common/Thread.h" +#include "Common/Event.h" #ifdef _WIN32 diff --git a/Source/Core/AudioCommon/XAudio2_7Stream.cpp b/Source/Core/AudioCommon/XAudio2_7Stream.cpp index 816d83d47b..8739e919f5 100644 --- a/Source/Core/AudioCommon/XAudio2_7Stream.cpp +++ b/Source/Core/AudioCommon/XAudio2_7Stream.cpp @@ -9,6 +9,7 @@ #include "AudioCommon/AudioCommon.h" #include "AudioCommon/XAudio2_7Stream.h" +#include "Common/Event.h" #ifdef HAVE_DXSDK #include diff --git a/Source/Core/AudioCommon/XAudio2_7Stream.h b/Source/Core/AudioCommon/XAudio2_7Stream.h index e1e3182be9..99c0ca690f 100644 --- a/Source/Core/AudioCommon/XAudio2_7Stream.h +++ b/Source/Core/AudioCommon/XAudio2_7Stream.h @@ -14,7 +14,7 @@ #include #include "AudioCommon/SoundStream.h" -#include "Common/Thread.h" +#include "Common/Event.h" #ifdef _WIN32 diff --git a/Source/Core/Common/Event.h b/Source/Core/Common/Event.h new file mode 100644 index 0000000000..1203eb38c0 --- /dev/null +++ b/Source/Core/Common/Event.h @@ -0,0 +1,59 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +// Multithreaded event class. This allows waiting in a thread for an event to +// be triggered in another thread. While waiting, the CPU will be available for +// other tasks. +// * Set(): triggers the event and wakes up the waiting thread. +// * Wait(): waits for the event to be triggered. +// * Reset(): tries to reset the event before the waiting thread sees it was +// triggered. Usually a bad idea. + +#pragma once + +#include "Common/StdConditionVariable.h" +#include "Common/StdMutex.h" + +namespace Common { + +class Event +{ +public: + Event() + : is_set(false) + {} + + void Set() + { + std::lock_guard lk(m_mutex); + if (!is_set) + { + is_set = true; + m_condvar.notify_one(); + } + } + + void Wait() + { + std::unique_lock lk(m_mutex); + m_condvar.wait(lk, [&]{ return is_set; }); + is_set = false; + } + + void Reset() + { + std::unique_lock lk(m_mutex); + // no other action required, since wait loops on + // the predicate and any lingering signal will get + // cleared on the first iteration + is_set = false; + } + +private: + volatile bool is_set; + std::condition_variable m_condvar; + std::mutex m_mutex; +}; + +} // namespace Common diff --git a/Source/Core/Common/Thread.h b/Source/Core/Common/Thread.h index d108359dcd..54045caf26 100644 --- a/Source/Core/Common/Thread.h +++ b/Source/Core/Common/Thread.h @@ -32,45 +32,6 @@ int CurrentThreadId(); void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); void SetCurrentThreadAffinity(u32 mask); -class Event -{ -public: - Event() - : is_set(false) - {} - - void Set() - { - std::lock_guard lk(m_mutex); - if (!is_set) - { - is_set = true; - m_condvar.notify_one(); - } - } - - void Wait() - { - std::unique_lock lk(m_mutex); - m_condvar.wait(lk, [&]{ return is_set; }); - is_set = false; - } - - void Reset() - { - std::unique_lock lk(m_mutex); - // no other action required, since wait loops on - // the predicate and any lingering signal will get - // cleared on the first iteration - is_set = false; - } - -private: - volatile bool is_set; - std::condition_variable m_condvar; - std::mutex m_mutex; -}; - // TODO: doesn't work on windows with (count > 2) class Barrier { diff --git a/Source/Core/Core/DSP/DSPCore.cpp b/Source/Core/Core/DSP/DSPCore.cpp index 520db9ac49..45063d4d47 100644 --- a/Source/Core/Core/DSP/DSPCore.cpp +++ b/Source/Core/Core/DSP/DSPCore.cpp @@ -24,10 +24,10 @@ ====================================================================*/ #include "Common/Common.h" +#include "Common/Event.h" #include "Common/FileUtil.h" #include "Common/Hash.h" #include "Common/MemoryUtil.h" -#include "Common/Thread.h" #include "Core/DSP/DSPAnalyzer.h" #include "Core/DSP/DSPCore.h" diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp index 613974b675..820617eb33 100644 --- a/Source/Core/Core/HW/CPU.cpp +++ b/Source/Core/Core/HW/CPU.cpp @@ -5,7 +5,8 @@ #include "AudioCommon/AudioCommon.h" #include "Common/Common.h" -#include "Common/Thread.h" +#include "Common/Event.h" +#include "Common/StdMutex.h" #include "Core/Core.h" #include "Core/DSPEmulator.h" diff --git a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp index 697e69586d..8ada666527 100644 --- a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp +++ b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp @@ -7,9 +7,11 @@ #include "Common/Common.h" #include "Common/CommonPaths.h" #include "Common/CPUDetect.h" +#include "Common/Event.h" #include "Common/IniFile.h" #include "Common/LogManager.h" -#include "Common/Thread.h" +#include "Common/StdMutex.h" +#include "Common/StdThread.h" #include "Core/ConfigManager.h" #include "Core/Core.h" diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 6d16cef7c5..f0060a4c70 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -5,8 +5,10 @@ #include #include "Common/Common.h" +#include "Common/Event.h" +#include "Common/StdMutex.h" +#include "Common/StdThread.h" #include "Common/StringUtil.h" -#include "Common/Thread.h" #include "Common/Timer.h" #include "Core/ConfigManager.h" diff --git a/Source/Core/DolphinWX/Debugger/CodeWindow.h b/Source/Core/DolphinWX/Debugger/CodeWindow.h index 90ddff938c..74977026ff 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindow.h +++ b/Source/Core/DolphinWX/Debugger/CodeWindow.h @@ -14,7 +14,7 @@ #include #include "Common/CommonTypes.h" -#include "Common/Thread.h" +#include "Common/Event.h" #include "DolphinWX/Globals.h" class CFrame; diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index 8f7e0732c7..2c98fda566 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -22,7 +22,7 @@ #include #include "Common/CommonTypes.h" -#include "Common/Thread.h" +#include "Common/Event.h" #include "DolphinWX/Globals.h" #include "InputCommon/GCPadStatus.h" diff --git a/Source/Core/DolphinWX/MainAndroid.cpp b/Source/Core/DolphinWX/MainAndroid.cpp index 4ce8ce0710..ab82e7def1 100644 --- a/Source/Core/DolphinWX/MainAndroid.cpp +++ b/Source/Core/DolphinWX/MainAndroid.cpp @@ -27,9 +27,9 @@ #include "Common/Common.h" #include "Common/CommonPaths.h" #include "Common/CPUDetect.h" +#include "Common/Event.h" #include "Common/FileUtil.h" #include "Common/LogManager.h" -#include "Common/Thread.h" #include "Core/BootManager.h" #include "Core/ConfigManager.h" #include "Core/Core.h" diff --git a/Source/Core/DolphinWX/MainNoGUI.cpp b/Source/Core/DolphinWX/MainNoGUI.cpp index c8f57f9ef6..cfd176e27a 100644 --- a/Source/Core/DolphinWX/MainNoGUI.cpp +++ b/Source/Core/DolphinWX/MainNoGUI.cpp @@ -10,8 +10,8 @@ #include #include "Common/Common.h" +#include "Common/Event.h" #include "Common/LogManager.h" -#include "Common/Thread.h" #include "Core/BootManager.h" #include "Core/ConfigManager.h" diff --git a/Source/UnitTests/Common/CMakeLists.txt b/Source/UnitTests/Common/CMakeLists.txt index 11fa2c57e0..42c0a8aaed 100644 --- a/Source/UnitTests/Common/CMakeLists.txt +++ b/Source/UnitTests/Common/CMakeLists.txt @@ -1,5 +1,6 @@ add_dolphin_test(BitFieldTest BitFieldTest.cpp common) add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp common) +add_dolphin_test(EventTest EventTest.cpp common) add_dolphin_test(FifoQueueTest FifoQueueTest.cpp common) add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp common) add_dolphin_test(FlagTest FlagTest.cpp common) diff --git a/Source/UnitTests/Common/EventTest.cpp b/Source/UnitTests/Common/EventTest.cpp new file mode 100644 index 0000000000..41949a893d --- /dev/null +++ b/Source/UnitTests/Common/EventTest.cpp @@ -0,0 +1,42 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Event.h" + +using Common::Event; + +TEST(Event, MultiThreaded) +{ + Event has_sent, can_send; + int shared_obj; + const int ITERATIONS_COUNT = 100000; + + auto sender = [&]() { + for (int i = 0; i < ITERATIONS_COUNT; ++i) + { + can_send.Wait(); + shared_obj = i; + has_sent.Set(); + } + }; + + auto receiver = [&]() { + for (int i = 0; i < ITERATIONS_COUNT; ++i) { + has_sent.Wait(); + EXPECT_EQ(i, shared_obj); + can_send.Set(); + } + }; + + std::thread sender_thread(sender); + std::thread receiver_thread(receiver); + + can_send.Set(); + + sender_thread.join(); + receiver_thread.join(); +} From e24cad078056a730f584b0c0ebb83c835c8c7dc6 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 14 Apr 2014 01:40:20 +0200 Subject: [PATCH 3/5] Common::Flag: Add support for TestAndSet + test by implementing basic spinlocks. --- Source/Core/Common/Flag.h | 20 +++++++++++++++++- Source/UnitTests/Common/FlagTest.cpp | 31 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/Source/Core/Common/Flag.h b/Source/Core/Common/Flag.h index 9b8fa3e28e..89b61c7805 100644 --- a/Source/Core/Common/Flag.h +++ b/Source/Core/Common/Flag.h @@ -3,10 +3,17 @@ // Refer to the license.txt file included. // Abstraction for a simple flag that can be toggled in a multithreaded way. -// It exposes a very simple API: +// +// Simple API: // * Set(bool = true): sets the Flag // * IsSet(): tests if the flag is set // * Clear(): clears the flag (equivalent to Set(false)). +// +// More advanced features: +// * TestAndSet(bool = true): sets the flag to the given value. If a change was +// needed (the flag did not already have this value) +// the function returns true. Else, false. +// * TestAndClear(): alias for TestAndSet(false). #pragma once @@ -37,6 +44,17 @@ public: return m_val.load(); } + bool TestAndSet(bool val = true) + { + bool expected = !val; + return m_val.compare_exchange_strong(expected, val); + } + + bool TestAndClear() + { + return TestAndSet(false); + } + private: // We are not using std::atomic_bool here because MSVC sucks as of VC++ // 2013 and does not implement the std::atomic_bool(bool) constructor. diff --git a/Source/UnitTests/Common/FlagTest.cpp b/Source/UnitTests/Common/FlagTest.cpp index ef73d27728..a3a0c3d929 100644 --- a/Source/UnitTests/Common/FlagTest.cpp +++ b/Source/UnitTests/Common/FlagTest.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include #include #include @@ -23,6 +24,9 @@ TEST(Flag, Simple) f.Set(false); EXPECT_FALSE(f.IsSet()); + EXPECT_TRUE(f.TestAndSet()); + EXPECT_TRUE(f.TestAndClear()); + Flag f2(true); EXPECT_TRUE(f2.IsSet()); } @@ -58,3 +62,30 @@ TEST(Flag, MultiThreaded) EXPECT_EQ(ITERATIONS_COUNT, count); } + +TEST(Flag, SpinLock) +{ + // Uses a flag to implement basic spinlocking using TestAndSet. + Flag f; + int count = 0; + const int ITERATIONS_COUNT = 5000; + const int THREADS_COUNT = 50; + + auto adder_func = [&]() { + for (int i = 0; i < ITERATIONS_COUNT; ++i) + { + // Acquire the spinlock. + while (!f.TestAndSet()); + count++; + f.Clear(); + } + }; + + std::array threads; + for (auto& th : threads) + th = std::thread(adder_func); + for (auto& th : threads) + th.join(); + + EXPECT_EQ(ITERATIONS_COUNT * THREADS_COUNT, count); +} From 48bd904028b1bc4bc89c76e18b57a95ad27a1184 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 14 Apr 2014 01:42:03 +0200 Subject: [PATCH 4/5] Common::Event: Implement in terms of Common::Flag to get rid of a volatile and optimize with early returns and atomic swaps --- Source/Core/Common/Event.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Source/Core/Common/Event.h b/Source/Core/Common/Event.h index 1203eb38c0..8499fdb1e8 100644 --- a/Source/Core/Common/Event.h +++ b/Source/Core/Common/Event.h @@ -12,6 +12,7 @@ #pragma once +#include "Common/Flag.h" #include "Common/StdConditionVariable.h" #include "Common/StdMutex.h" @@ -20,38 +21,35 @@ namespace Common { class Event { public: - Event() - : is_set(false) - {} - void Set() { - std::lock_guard lk(m_mutex); - if (!is_set) + if (m_flag.TestAndSet()) { - is_set = true; + std::lock_guard lk(m_mutex); m_condvar.notify_one(); } } void Wait() { + if (m_flag.TestAndClear()) + return; + std::unique_lock lk(m_mutex); - m_condvar.wait(lk, [&]{ return is_set; }); - is_set = false; + m_condvar.wait(lk, [&]{ return m_flag.IsSet(); }); + m_flag.Clear(); } void Reset() { - std::unique_lock lk(m_mutex); // no other action required, since wait loops on // the predicate and any lingering signal will get // cleared on the first iteration - is_set = false; + m_flag.Clear(); } private: - volatile bool is_set; + Flag m_flag; std::condition_variable m_condvar; std::mutex m_mutex; }; From 7074feacbebb1521a7c041c13237e799b7299d56 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 14 Apr 2014 02:30:40 +0200 Subject: [PATCH 5/5] Common::Event: Add a faster Windows specific implementation based on the concurrency runtime. --- Source/Core/Common/Event.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Source/Core/Common/Event.h b/Source/Core/Common/Event.h index 8499fdb1e8..b60a7cacd3 100644 --- a/Source/Core/Common/Event.h +++ b/Source/Core/Common/Event.h @@ -12,13 +12,20 @@ #pragma once +#ifdef _WIN32 +#include +#endif + #include "Common/Flag.h" #include "Common/StdConditionVariable.h" #include "Common/StdMutex.h" namespace Common { -class Event +// Windows uses a specific implementation because std::condition_variable has +// terrible performance for this kind of workload with MSVC++ 2013. +#ifndef _WIN32 +class Event final { public: void Set() @@ -53,5 +60,17 @@ private: std::condition_variable m_condvar; std::mutex m_mutex; }; +#else +class Event final +{ +public: + void Set() { m_event.set(); } + void Wait() { m_event.wait(); m_event.reset(); } + void Reset() { m_event.reset(); } + +private: + concurrency::event m_event; +}; +#endif } // namespace Common