From f889e9e754f6f1b243e1fd94ae9efe1be4424d7e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 15 Jul 2020 11:55:33 +0200 Subject: [PATCH] Add the spinlock mutex --- dpf.mk | 1 + src/CMakeLists.txt | 2 ++ src/sfizz/utility/SpinMutex.cpp | 43 +++++++++++++++++++++++++++ src/sfizz/utility/SpinMutex.h | 28 ++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/ConcurrencyT.cpp | 51 +++++++++++++++++++++++++++++++++ 6 files changed, 126 insertions(+) create mode 100644 src/sfizz/utility/SpinMutex.cpp create mode 100644 src/sfizz/utility/SpinMutex.h create mode 100644 tests/ConcurrencyT.cpp diff --git a/dpf.mk b/dpf.mk index 772dd364..ba5d18c3 100644 --- a/dpf.mk +++ b/dpf.mk @@ -101,6 +101,7 @@ SFIZZ_SOURCES = \ src/sfizz/simd/HelpersAVX.cpp \ src/sfizz/Synth.cpp \ src/sfizz/Tuning.cpp \ + src/sfizz/utility/SpinMutex.cpp \ src/sfizz/Voice.cpp \ src/sfizz/Wavetables.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8de6d8eb..23d35477 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,8 @@ set (SFIZZ_HEADERS sfizz/Config.h sfizz/Curve.h sfizz/Debug.h + sfizz/utility/SpinMutex.h + sfizz/utility/SpinMutex.cpp sfizz/effects/impl/ResonantArray.h sfizz/effects/impl/ResonantArrayAVX.h sfizz/effects/impl/ResonantArraySSE.h diff --git a/src/sfizz/utility/SpinMutex.cpp b/src/sfizz/utility/SpinMutex.cpp new file mode 100644 index 00000000..cb416081 --- /dev/null +++ b/src/sfizz/utility/SpinMutex.cpp @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SpinMutex.h" +#include +#include + +// based on Timur Doumler's implementation advice for spinlocks + +void SpinMutex::lock() noexcept +{ + for (int i = 0; i < 5; ++i) { + if (try_lock()) + return; + } + + for (int i = 0; i < 10; ++i) { + if (try_lock()) + return; + atomic_queue::spin_loop_pause(); + } + + for (;;) { + for (int i = 0; i < 3000; ++i) { + if (try_lock()) + return; + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + atomic_queue::spin_loop_pause(); + } + std::this_thread::yield(); + } +} diff --git a/src/sfizz/utility/SpinMutex.h b/src/sfizz/utility/SpinMutex.h new file mode 100644 index 00000000..5e1aa347 --- /dev/null +++ b/src/sfizz/utility/SpinMutex.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include + +class SpinMutex { +public: + void lock() noexcept; + bool try_lock() noexcept; + void unlock() noexcept; + +private: + std::atomic_flag flag_ = ATOMIC_FLAG_INIT; +}; + +inline bool SpinMutex::try_lock() noexcept +{ + return !flag_.test_and_set(std::memory_order_acquire); +} + +inline void SpinMutex::unlock() noexcept +{ + flag_.clear(std::memory_order_release); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 31e134c8..21bc72d1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,6 +35,7 @@ set(SFIZZ_TEST_SOURCES SemaphoreT.cpp SwapAndPopT.cpp TuningT.cpp + ConcurrencyT.cpp ) add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES}) diff --git a/tests/ConcurrencyT.cpp b/tests/ConcurrencyT.cpp new file mode 100644 index 00000000..9ec72cd5 --- /dev/null +++ b/tests/ConcurrencyT.cpp @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "sfizz/utility/SpinMutex.h" +#include "catch2/catch.hpp" +#include +#include +#include + +TEST_CASE("[SpinMutex] Basic synchronization") +{ + constexpr size_t num_threads = 8; + constexpr size_t num_iterations = 1000000; + + volatile size_t counter = 0; + SpinMutex counter_mutex; + + std::thread threads[num_threads]; + + volatile bool ready = false; + std::condition_variable ready_cv; + std::mutex ready_mtx; + + auto thread_run = [&]() + { + std::unique_lock lock(ready_mtx); + ready_cv.wait(lock, [&]() -> bool { return ready; }); + lock.unlock(); + + for (size_t i = 0; i < num_iterations; ++i) { + std::unique_lock lock(counter_mutex); + ++counter; + } + }; + + for (unsigned i = 0; i < num_threads; ++i) + threads[i] = std::thread(thread_run); + + std::unique_lock lock(ready_mtx); + ready = true; + ready_cv.notify_all(); + lock.unlock(); + + for (unsigned i = 0; i < num_threads; ++i) + threads[i].join(); + + REQUIRE(counter == num_threads * num_iterations); +}