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/Synth.cpp \
|
||||
src/sfizz/Tuning.cpp \
|
||||
src/sfizz/utility/SpinMutex.cpp \
|
||||
src/sfizz/Voice.cpp \
|
||||
src/sfizz/Wavetables.cpp
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,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
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ sfz::EQPool::EQPool(const MidiState& state, int numEQs)
|
|||
|
||||
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())
|
||||
return {};
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ size_t sfz::EQPool::getActiveEQs() const
|
|||
|
||||
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; });
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "SfzFilter.h"
|
||||
#include "EQDescription.h"
|
||||
#include "MidiState.h"
|
||||
#include "utility/SpinMutex.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
|
@ -115,7 +116,7 @@ public:
|
|||
*/
|
||||
void setSampleRate(float sampleRate);
|
||||
private:
|
||||
std::mutex eqGuard;
|
||||
SpinMutex eqGuard;
|
||||
float sampleRate { config::defaultSampleRate };
|
||||
const MidiState& midiState;
|
||||
std::vector<EQHolderPtr> eqs;
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept
|
|||
|
||||
void sfz::FilePool::tryToClearPromises()
|
||||
{
|
||||
const std::lock_guard<std::mutex> promiseLock { promiseGuard };
|
||||
const std::lock_guard<SpinMutex> promiseLock { promiseGuard };
|
||||
|
||||
for (auto& promise: promisesToClear) {
|
||||
if (promise->dataStatus != FilePromise::DataStatus::Wait)
|
||||
|
|
@ -427,7 +427,7 @@ void sfz::FilePool::clear()
|
|||
|
||||
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())
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "AudioSpan.h"
|
||||
#include "FileId.h"
|
||||
#include "SIMDHelpers.h"
|
||||
#include "utility/SpinMutex.h"
|
||||
#include "ghc/fs_std.hpp"
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <absl/types/optional.h>
|
||||
|
|
@ -288,7 +289,7 @@ private:
|
|||
std::vector<FilePromisePtr> emptyPromises;
|
||||
std::vector<FilePromisePtr> temporaryFilePromises;
|
||||
std::vector<FilePromisePtr> promisesToClear;
|
||||
std::mutex promiseGuard;
|
||||
SpinMutex promiseGuard;
|
||||
|
||||
// Preloaded data
|
||||
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)
|
||||
{
|
||||
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())
|
||||
return {};
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ size_t sfz::FilterPool::getActiveFilters() const
|
|||
|
||||
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; });
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "FilterDescription.h"
|
||||
#include "MidiState.h"
|
||||
#include "Defaults.h"
|
||||
#include "utility/SpinMutex.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
|
@ -120,7 +121,7 @@ public:
|
|||
*/
|
||||
void setSampleRate(float sampleRate);
|
||||
private:
|
||||
std::mutex filterGuard;
|
||||
SpinMutex filterGuard;
|
||||
float sampleRate { config::defaultSampleRate };
|
||||
const MidiState& midiState;
|
||||
std::vector<FilterHolderPtr> filters;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ sfz::Synth::Synth()
|
|||
|
||||
sfz::Synth::Synth(int numVoices)
|
||||
{
|
||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
parser.setListener(this);
|
||||
effectFactory.registerStandardEffectTypes();
|
||||
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
||||
|
|
@ -39,7 +39,7 @@ sfz::Synth::Synth(int numVoices)
|
|||
|
||||
sfz::Synth::~Synth()
|
||||
{
|
||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
|
|
@ -166,7 +166,7 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
|||
|
||||
void sfz::Synth::clear()
|
||||
{
|
||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
|
|
@ -396,7 +396,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
{
|
||||
clear();
|
||||
|
||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
|
||||
std::error_code 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();
|
||||
|
||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
parser.parseString(path, text);
|
||||
if (parser.getErrorCount() > 0)
|
||||
return false;
|
||||
|
|
@ -647,7 +647,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
|||
{
|
||||
ASSERT(samplesPerBlock < config::maxBlockSize);
|
||||
|
||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
|
||||
this->samplesPerBlock = samplesPerBlock;
|
||||
for (auto& voice : voices)
|
||||
|
|
@ -663,7 +663,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) 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;
|
||||
for (auto& voice : voices)
|
||||
|
|
@ -702,7 +702,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
if (resources.synthConfig.freeWheeling)
|
||||
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())
|
||||
return;
|
||||
|
||||
|
|
@ -801,7 +801,7 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
|
|||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
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())
|
||||
return;
|
||||
|
||||
|
|
@ -817,7 +817,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
|||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
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())
|
||||
return;
|
||||
|
||||
|
|
@ -970,7 +970,7 @@ void sfz::Synth::hdcc(int delay, int ccNumber, float normValue) noexcept
|
|||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
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())
|
||||
return;
|
||||
|
||||
|
|
@ -1281,7 +1281,7 @@ int sfz::Synth::getNumVoices() const noexcept
|
|||
void sfz::Synth::setNumVoices(int numVoices) noexcept
|
||||
{
|
||||
ASSERT(numVoices > 0);
|
||||
const std::lock_guard<std::mutex> disableCallback { callbackGuard };
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
|
||||
// fast path
|
||||
if (numVoices == this->numVoices)
|
||||
|
|
@ -1329,7 +1329,7 @@ void sfz::Synth::applySettingsPerVoice()
|
|||
|
||||
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
|
||||
if (factor == oversamplingFactor)
|
||||
|
|
@ -1352,7 +1352,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const 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
|
||||
if (preloadSize == resources.filePool.getPreloadSize())
|
||||
|
|
@ -1385,7 +1385,7 @@ void sfz::Synth::resetAllControllers(int delay) noexcept
|
|||
{
|
||||
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())
|
||||
return;
|
||||
|
||||
|
|
@ -1440,7 +1440,7 @@ void sfz::Synth::disableLogging() 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)
|
||||
voice->reset();
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
#include "AudioSpan.h"
|
||||
#include "parser/Parser.h"
|
||||
#include "VoiceStealing.h"
|
||||
#include "utility/SpinMutex.h"
|
||||
#include "absl/types/span.h"
|
||||
#include <absl/types/optional.h>
|
||||
#include <random>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
|
@ -748,7 +748,7 @@ private:
|
|||
// Distribution used to generate random value for the *rand opcodes
|
||||
std::uniform_real_distribution<float> randNoteDistribution { 0, 1 };
|
||||
|
||||
std::mutex callbackGuard;
|
||||
SpinMutex callbackGuard;
|
||||
|
||||
// Singletons passed as references to the voices
|
||||
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
|
||||
SwapAndPopT.cpp
|
||||
TuningT.cpp
|
||||
ConcurrencyT.cpp
|
||||
)
|
||||
|
||||
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