commit
443e89c45d
14 changed files with 156 additions and 27 deletions
1
dpf.mk
1
dpf.mk
|
|
@ -106,6 +106,7 @@ SFIZZ_SOURCES = \
|
||||||
src/sfizz/simd/HelpersAVX.cpp \
|
src/sfizz/simd/HelpersAVX.cpp \
|
||||||
src/sfizz/Synth.cpp \
|
src/sfizz/Synth.cpp \
|
||||||
src/sfizz/Tuning.cpp \
|
src/sfizz/Tuning.cpp \
|
||||||
|
src/sfizz/utility/SpinMutex.cpp \
|
||||||
src/sfizz/Voice.cpp \
|
src/sfizz/Voice.cpp \
|
||||||
src/sfizz/Wavetables.cpp
|
src/sfizz/Wavetables.cpp
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ set (SFIZZ_HEADERS
|
||||||
sfizz/Config.h
|
sfizz/Config.h
|
||||||
sfizz/Curve.h
|
sfizz/Curve.h
|
||||||
sfizz/Debug.h
|
sfizz/Debug.h
|
||||||
|
sfizz/utility/SpinMutex.h
|
||||||
|
sfizz/utility/SpinMutex.cpp
|
||||||
sfizz/effects/impl/ResonantArray.h
|
sfizz/effects/impl/ResonantArray.h
|
||||||
sfizz/effects/impl/ResonantArrayAVX.h
|
sfizz/effects/impl/ResonantArrayAVX.h
|
||||||
sfizz/effects/impl/ResonantArraySSE.h
|
sfizz/effects/impl/ResonantArraySSE.h
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ sfz::EQPool::EQPool(const MidiState& state, int numEQs)
|
||||||
|
|
||||||
sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, float velocity)
|
sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, float velocity)
|
||||||
{
|
{
|
||||||
const std::unique_lock<std::mutex> lock { eqGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { eqGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
@ -132,7 +132,7 @@ size_t sfz::EQPool::getActiveEQs() const
|
||||||
|
|
||||||
size_t sfz::EQPool::setnumEQs(size_t numEQs)
|
size_t sfz::EQPool::setnumEQs(size_t numEQs)
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> eqLock { eqGuard };
|
const std::lock_guard<SpinMutex> eqLock { eqGuard };
|
||||||
|
|
||||||
swapAndPopAll(eqs, [](sfz::EQHolderPtr& eq) { return eq.use_count() == 1; });
|
swapAndPopAll(eqs, [](sfz::EQHolderPtr& eq) { return eq.use_count() == 1; });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "SfzFilter.h"
|
#include "SfzFilter.h"
|
||||||
#include "EQDescription.h"
|
#include "EQDescription.h"
|
||||||
#include "MidiState.h"
|
#include "MidiState.h"
|
||||||
|
#include "utility/SpinMutex.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
@ -115,7 +116,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void setSampleRate(float sampleRate);
|
void setSampleRate(float sampleRate);
|
||||||
private:
|
private:
|
||||||
std::mutex eqGuard;
|
SpinMutex eqGuard;
|
||||||
float sampleRate { config::defaultSampleRate };
|
float sampleRate { config::defaultSampleRate };
|
||||||
const MidiState& midiState;
|
const MidiState& midiState;
|
||||||
std::vector<EQHolderPtr> eqs;
|
std::vector<EQHolderPtr> eqs;
|
||||||
|
|
|
||||||
|
|
@ -345,7 +345,7 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept
|
||||||
|
|
||||||
void sfz::FilePool::tryToClearPromises()
|
void sfz::FilePool::tryToClearPromises()
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> promiseLock { promiseGuard };
|
const std::lock_guard<SpinMutex> promiseLock { promiseGuard };
|
||||||
|
|
||||||
for (auto& promise: promisesToClear) {
|
for (auto& promise: promisesToClear) {
|
||||||
if (promise->dataStatus != FilePromise::DataStatus::Wait)
|
if (promise->dataStatus != FilePromise::DataStatus::Wait)
|
||||||
|
|
@ -427,7 +427,7 @@ void sfz::FilePool::clear()
|
||||||
|
|
||||||
void sfz::FilePool::cleanupPromises() noexcept
|
void sfz::FilePool::cleanupPromises() noexcept
|
||||||
{
|
{
|
||||||
const std::unique_lock<std::mutex> lock { promiseGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { promiseGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
#include "AudioSpan.h"
|
#include "AudioSpan.h"
|
||||||
#include "FileId.h"
|
#include "FileId.h"
|
||||||
#include "SIMDHelpers.h"
|
#include "SIMDHelpers.h"
|
||||||
|
#include "utility/SpinMutex.h"
|
||||||
#include "ghc/fs_std.hpp"
|
#include "ghc/fs_std.hpp"
|
||||||
#include <absl/container/flat_hash_map.h>
|
#include <absl/container/flat_hash_map.h>
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
|
|
@ -288,7 +289,7 @@ private:
|
||||||
std::vector<FilePromisePtr> emptyPromises;
|
std::vector<FilePromisePtr> emptyPromises;
|
||||||
std::vector<FilePromisePtr> temporaryFilePromises;
|
std::vector<FilePromisePtr> temporaryFilePromises;
|
||||||
std::vector<FilePromisePtr> promisesToClear;
|
std::vector<FilePromisePtr> promisesToClear;
|
||||||
std::mutex promiseGuard;
|
SpinMutex promiseGuard;
|
||||||
|
|
||||||
// Preloaded data
|
// Preloaded data
|
||||||
absl::flat_hash_map<FileId, FileDataHandle> preloadedFiles;
|
absl::flat_hash_map<FileId, FileDataHandle> preloadedFiles;
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ sfz::FilterPool::FilterPool(const MidiState& state, int numFilters)
|
||||||
|
|
||||||
sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity)
|
sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity)
|
||||||
{
|
{
|
||||||
const std::unique_lock<std::mutex> lock { filterGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { filterGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ size_t sfz::FilterPool::getActiveFilters() const
|
||||||
|
|
||||||
size_t sfz::FilterPool::setNumFilters(size_t numFilters)
|
size_t sfz::FilterPool::setNumFilters(size_t numFilters)
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> filterLock { filterGuard };
|
const std::lock_guard<SpinMutex> filterLock { filterGuard };
|
||||||
|
|
||||||
swapAndPopAll(filters, [](sfz::FilterHolderPtr& filter) { return filter.use_count() == 1; });
|
swapAndPopAll(filters, [](sfz::FilterHolderPtr& filter) { return filter.use_count() == 1; });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "FilterDescription.h"
|
#include "FilterDescription.h"
|
||||||
#include "MidiState.h"
|
#include "MidiState.h"
|
||||||
#include "Defaults.h"
|
#include "Defaults.h"
|
||||||
|
#include "utility/SpinMutex.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
@ -120,7 +121,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void setSampleRate(float sampleRate);
|
void setSampleRate(float sampleRate);
|
||||||
private:
|
private:
|
||||||
std::mutex filterGuard;
|
SpinMutex filterGuard;
|
||||||
float sampleRate { config::defaultSampleRate };
|
float sampleRate { config::defaultSampleRate };
|
||||||
const MidiState& midiState;
|
const MidiState& midiState;
|
||||||
std::vector<FilterHolderPtr> filters;
|
std::vector<FilterHolderPtr> filters;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ sfz::Synth::Synth()
|
||||||
|
|
||||||
sfz::Synth::Synth(int numVoices)
|
sfz::Synth::Synth(int numVoices)
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
parser.setListener(this);
|
parser.setListener(this);
|
||||||
effectFactory.registerStandardEffectTypes();
|
effectFactory.registerStandardEffectTypes();
|
||||||
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
||||||
|
|
@ -39,7 +39,7 @@ sfz::Synth::Synth(int numVoices)
|
||||||
|
|
||||||
sfz::Synth::~Synth()
|
sfz::Synth::~Synth()
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->reset();
|
voice->reset();
|
||||||
|
|
@ -166,7 +166,7 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
||||||
|
|
||||||
void sfz::Synth::clear()
|
void sfz::Synth::clear()
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->reset();
|
voice->reset();
|
||||||
|
|
@ -396,7 +396,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
fs::path realFile = fs::canonical(file, ec);
|
fs::path realFile = fs::canonical(file, ec);
|
||||||
|
|
@ -417,7 +417,7 @@ bool sfz::Synth::loadSfzString(const fs::path& path, absl::string_view text)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
parser.parseString(path, text);
|
parser.parseString(path, text);
|
||||||
if (parser.getErrorCount() > 0)
|
if (parser.getErrorCount() > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -647,7 +647,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||||
{
|
{
|
||||||
ASSERT(samplesPerBlock < config::maxBlockSize);
|
ASSERT(samplesPerBlock < config::maxBlockSize);
|
||||||
|
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
this->samplesPerBlock = samplesPerBlock;
|
this->samplesPerBlock = samplesPerBlock;
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
|
|
@ -663,7 +663,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||||
|
|
||||||
void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
this->sampleRate = sampleRate;
|
this->sampleRate = sampleRate;
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
|
|
@ -702,7 +702,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
if (resources.synthConfig.freeWheeling)
|
if (resources.synthConfig.freeWheeling)
|
||||||
resources.filePool.waitForBackgroundLoading();
|
resources.filePool.waitForBackgroundLoading();
|
||||||
|
|
||||||
const std::unique_lock<std::mutex> lock { callbackGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { callbackGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -801,7 +801,7 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||||
resources.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity);
|
resources.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity);
|
||||||
|
|
||||||
const std::unique_lock<std::mutex> lock { callbackGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { callbackGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -817,7 +817,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||||
resources.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity);
|
resources.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity);
|
||||||
|
|
||||||
const std::unique_lock<std::mutex> lock { callbackGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { callbackGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -970,7 +970,7 @@ void sfz::Synth::hdcc(int delay, int ccNumber, float normValue) noexcept
|
||||||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||||
resources.midiState.ccEvent(delay, ccNumber, normValue);
|
resources.midiState.ccEvent(delay, ccNumber, normValue);
|
||||||
|
|
||||||
const std::unique_lock<std::mutex> lock { callbackGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { callbackGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -1281,7 +1281,7 @@ int sfz::Synth::getNumVoices() const noexcept
|
||||||
void sfz::Synth::setNumVoices(int numVoices) noexcept
|
void sfz::Synth::setNumVoices(int numVoices) noexcept
|
||||||
{
|
{
|
||||||
ASSERT(numVoices > 0);
|
ASSERT(numVoices > 0);
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
// fast path
|
// fast path
|
||||||
if (numVoices == this->numVoices)
|
if (numVoices == this->numVoices)
|
||||||
|
|
@ -1329,7 +1329,7 @@ void sfz::Synth::applySettingsPerVoice()
|
||||||
|
|
||||||
void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
// fast path
|
// fast path
|
||||||
if (factor == oversamplingFactor)
|
if (factor == oversamplingFactor)
|
||||||
|
|
@ -1352,7 +1352,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept
|
||||||
|
|
||||||
void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept
|
void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
// fast path
|
// fast path
|
||||||
if (preloadSize == resources.filePool.getPreloadSize())
|
if (preloadSize == resources.filePool.getPreloadSize())
|
||||||
|
|
@ -1385,7 +1385,7 @@ void sfz::Synth::resetAllControllers(int delay) noexcept
|
||||||
{
|
{
|
||||||
resources.midiState.resetAllControllers(delay);
|
resources.midiState.resetAllControllers(delay);
|
||||||
|
|
||||||
const std::unique_lock<std::mutex> lock { callbackGuard, std::try_to_lock };
|
const std::unique_lock<SpinMutex> lock { callbackGuard, std::try_to_lock };
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -1440,7 +1440,7 @@ void sfz::Synth::disableLogging() noexcept
|
||||||
|
|
||||||
void sfz::Synth::allSoundOff() noexcept
|
void sfz::Synth::allSoundOff() noexcept
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->reset();
|
voice->reset();
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
#include "AudioSpan.h"
|
#include "AudioSpan.h"
|
||||||
#include "parser/Parser.h"
|
#include "parser/Parser.h"
|
||||||
#include "VoiceStealing.h"
|
#include "VoiceStealing.h"
|
||||||
|
#include "utility/SpinMutex.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <mutex>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -748,7 +748,7 @@ private:
|
||||||
// Distribution used to generate random value for the *rand opcodes
|
// Distribution used to generate random value for the *rand opcodes
|
||||||
std::uniform_real_distribution<float> randNoteDistribution { 0, 1 };
|
std::uniform_real_distribution<float> randNoteDistribution { 0, 1 };
|
||||||
|
|
||||||
std::mutex callbackGuard;
|
SpinMutex callbackGuard;
|
||||||
|
|
||||||
// Singletons passed as references to the voices
|
// Singletons passed as references to the voices
|
||||||
Resources resources;
|
Resources resources;
|
||||||
|
|
|
||||||
43
src/sfizz/utility/SpinMutex.cpp
Normal file
43
src/sfizz/utility/SpinMutex.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/sfizz/utility/SpinMutex.h
Normal file
28
src/sfizz/utility/SpinMutex.h
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -35,6 +35,7 @@ set(SFIZZ_TEST_SOURCES
|
||||||
SemaphoreT.cpp
|
SemaphoreT.cpp
|
||||||
SwapAndPopT.cpp
|
SwapAndPopT.cpp
|
||||||
TuningT.cpp
|
TuningT.cpp
|
||||||
|
ConcurrencyT.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})
|
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})
|
||||||
|
|
|
||||||
51
tests/ConcurrencyT.cpp
Normal file
51
tests/ConcurrencyT.cpp
Normal 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);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue