Add the spinlock mutex

This commit is contained in:
Jean Pierre Cimalando 2020-07-15 11:55:33 +02:00
parent b353703328
commit f889e9e754
6 changed files with 126 additions and 0 deletions

1
dpf.mk
View file

@ -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

View file

@ -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

View file

@ -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 <atomic_queue/defs.h>
#include <thread>
// 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();
}
}

View file

@ -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 <atomic>
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);
}

View file

@ -35,6 +35,7 @@ set(SFIZZ_TEST_SOURCES
SemaphoreT.cpp
SwapAndPopT.cpp
TuningT.cpp
ConcurrencyT.cpp
)
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})

51
tests/ConcurrencyT.cpp Normal file
View file

@ -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 <thread>
#include <mutex>
#include <condition_variable>
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<std::mutex> lock(ready_mtx);
ready_cv.wait(lock, [&]() -> bool { return ready; });
lock.unlock();
for (size_t i = 0; i < num_iterations; ++i) {
std::unique_lock<SpinMutex> lock(counter_mutex);
++counter;
}
};
for (unsigned i = 0; i < num_threads; ++i)
threads[i] = std::thread(thread_run);
std::unique_lock<std::mutex> 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);
}