Move the spsc queues to mpmc queues

This commit is contained in:
Paul Ferrand 2019-12-03 21:30:00 +01:00
parent e33ce85a6a
commit 19b475ed94
4 changed files with 47 additions and 29 deletions

View file

@ -36,7 +36,9 @@ namespace config {
constexpr int defaultSamplesPerBlock { 1024 }; constexpr int defaultSamplesPerBlock { 1024 };
constexpr int preloadSize { 8192 }; constexpr int preloadSize { 8192 };
constexpr int numChannels { 2 }; constexpr int numChannels { 2 };
constexpr int numBackgroundThreads { 4 };
constexpr int numVoices { 64 }; constexpr int numVoices { 64 };
constexpr int maxVoices { 256 };
constexpr int sustainCC { 64 }; constexpr int sustainCC { 64 };
constexpr int halfCCThreshold { 64 }; constexpr int halfCCThreshold { 64 };
constexpr int centPerSemitone { 100 }; constexpr int centPerSemitone { 100 };

View file

@ -164,7 +164,7 @@ sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) n
promise->preloadedData = preloaded->second.preloadedData; promise->preloadedData = preloaded->second.preloadedData;
promise->sampleRate = preloaded->second.sampleRate; promise->sampleRate = preloaded->second.sampleRate;
promise->oversamplingFactor = oversamplingFactor; promise->oversamplingFactor = oversamplingFactor;
promiseQueue.enqueue(promise); promiseQueue.try_enqueue(promise);
} }
return promise; return promise;
} }
@ -196,11 +196,12 @@ void sfz::FilePool::loadingThread() noexcept
continue; continue;
} }
if (!promiseQueue.try_dequeue(promise)) { if (!promiseQueue.wait_dequeue_timed(promise, 50ms)) {
std::this_thread::sleep_for(0.1ms);
continue; continue;
} }
// The voice abandoned the promise already we just don't care
if (promise.use_count() != 1) {
fs::path file { rootDirectory / std::string(promise->filename) }; fs::path file { rootDirectory / std::string(promise->filename) };
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str())); SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
if (sndFile.error() != 0) if (sndFile.error() != 0)
@ -210,14 +211,19 @@ void sfz::FilePool::loadingThread() noexcept
const uint32_t frames = sndFile.frames(); const uint32_t frames = sndFile.frames();
promise->fileData = readFromFile<float>(sndFile, frames, oversamplingFactor); promise->fileData = readFromFile<float>(sndFile, frames, oversamplingFactor);
promise->dataReady = true; promise->dataReady = true;
temporaryFilePromises.push_back(promise); }
while (!filledPromiseQueue.try_enqueue(promise)) {
DBG("Error enqueuing the file for " << promise->filename << " in the filledPromiseQueue");
std::this_thread::sleep_for(1ms);
}
promise.reset(); promise.reset();
} }
} }
void sfz::FilePool::clear() void sfz::FilePool::clear()
{ {
emptyFileLoadingQueue(); emptyFileLoadingQueues();
preloadedFiles.clear(); preloadedFiles.clear();
temporaryFilePromises.clear(); temporaryFilePromises.clear();
promisesToClean.clear(); promisesToClean.clear();
@ -225,19 +231,23 @@ void sfz::FilePool::clear()
void sfz::FilePool::cleanupPromises() noexcept void sfz::FilePool::cleanupPromises() noexcept
{ {
if (temporaryFilePromises.empty()) FilePromisePtr promise;
return; // Remove stuff from the filled queue and put them in a linear storage
while (filledPromiseQueue.try_dequeue(promise)) {
temporaryFilePromises.push_back(promise);
promise.reset();
}
auto promise = temporaryFilePromises.begin(); auto promiseIterator = temporaryFilePromises.begin();
auto sentinel = temporaryFilePromises.end() - 1; auto sentinel = temporaryFilePromises.end() - 1;
while (promise != temporaryFilePromises.end()) { while (promiseIterator != temporaryFilePromises.end()) {
if (promise->use_count() == 1) { if (promiseIterator->use_count() == 1) {
promisesToClean.push_back(*promise); promisesToClean.push_back(*promiseIterator);
std::iter_swap(promise, sentinel); std::iter_swap(promiseIterator, sentinel);
sentinel--; sentinel--;
temporaryFilePromises.pop_back(); temporaryFilePromises.pop_back();
} else { } else {
promise++; promiseIterator++;
} }
} }
} }
@ -267,7 +277,7 @@ uint32_t sfz::FilePool::getPreloadSize() const noexcept
return preloadSize; return preloadSize;
} }
void sfz::FilePool::emptyFileLoadingQueue() noexcept void sfz::FilePool::emptyFileLoadingQueues() noexcept
{ {
emptyQueue = true; emptyQueue = true;
while (emptyQueue) while (emptyQueue)

View file

@ -31,7 +31,7 @@
#include <absl/container/flat_hash_map.h> #include <absl/container/flat_hash_map.h>
#include <absl/types/optional.h> #include <absl/types/optional.h>
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "moodycamel/concurrentqueue.h" #include "moodycamel/blockingconcurrentqueue.h"
#include <thread> #include <thread>
#include <sndfile.hh> #include <sndfile.hh>
@ -77,12 +77,17 @@ using FilePromisePtr = std::shared_ptr<FilePromise>;
class FilePool { class FilePool {
public: public:
FilePool() { } FilePool()
{
for (int i = 0; i < config::numBackgroundThreads; ++i)
fileLoadingThreadPool.emplace_back( &FilePool::loadingThread, this );
}
~FilePool() ~FilePool()
{ {
quitThread = true; quitThread = true;
fileLoadingThread.join(); for (auto& thread: fileLoadingThreadPool)
thread.join();
} }
/** /**
* @brief Set the root directory from which to search for files to load * @brief Set the root directory from which to search for files to load
@ -136,12 +141,13 @@ public:
uint32_t getPreloadSize() const noexcept; uint32_t getPreloadSize() const noexcept;
void setOversamplingFactor(Oversampling factor) noexcept; void setOversamplingFactor(Oversampling factor) noexcept;
Oversampling getOversamplingFactor() const noexcept; Oversampling getOversamplingFactor() const noexcept;
void emptyFileLoadingQueue() noexcept; void emptyFileLoadingQueues() noexcept;
private: private:
fs::path rootDirectory; fs::path rootDirectory;
void loadingThread() noexcept; void loadingThread() noexcept;
moodycamel::ConcurrentQueue<FilePromisePtr> promiseQueue; moodycamel::BlockingConcurrentQueue<FilePromisePtr> promiseQueue { config::maxVoices };
moodycamel::BlockingConcurrentQueue<FilePromisePtr> filledPromiseQueue { config::maxVoices };
uint32_t preloadSize { config::preloadSize }; uint32_t preloadSize { config::preloadSize };
Oversampling oversamplingFactor { config::defaultOversamplingFactor }; Oversampling oversamplingFactor { config::defaultOversamplingFactor };
// Signals // Signals
@ -151,7 +157,7 @@ private:
std::vector<FilePromisePtr> temporaryFilePromises; std::vector<FilePromisePtr> temporaryFilePromises;
std::vector<FilePromisePtr> promisesToClean; std::vector<FilePromisePtr> promisesToClean;
absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles; absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles;
std::thread fileLoadingThread { &FilePool::loadingThread, this }; std::vector<std::thread> fileLoadingThreadPool { };
LEAK_DETECTOR(FilePool); LEAK_DETECTOR(FilePool);
}; };
} }

View file

@ -551,7 +551,7 @@ void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept
for (auto& voice: voices) for (auto& voice: voices)
voice->reset(); voice->reset();
resources.filePool.emptyFileLoadingQueue(); resources.filePool.emptyFileLoadingQueues();
resources.filePool.setOversamplingFactor(factor); resources.filePool.setOversamplingFactor(factor);
oversamplingFactor = factor; oversamplingFactor = factor;
} }