Added atomic guards to clear promises

And a specific thread to avoid useless race conditions
This commit is contained in:
Paul Ferrand 2019-12-22 19:12:17 +01:00
parent d1e031e8e2
commit 528593fa76
2 changed files with 35 additions and 7 deletions

View file

@ -27,10 +27,10 @@
#include "Config.h" #include "Config.h"
#include "Debug.h" #include "Debug.h"
#include "Oversampler.h" #include "Oversampler.h"
#include "AtomicGuard.h"
#include "absl/types/span.h" #include "absl/types/span.h"
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <mutex>
#include <sndfile.hh> #include <sndfile.hh>
#include <thread> #include <thread>
using namespace std::chrono_literals; using namespace std::chrono_literals;
@ -149,12 +149,28 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept
this->preloadSize = preloadSize; this->preloadSize = preloadSize;
} }
void sfz::FilePool::tryToClearPromises()
{
AtomicDisabler disabler { canAddPromisesToClear };
while (addingPromisesToClear)
std::this_thread::sleep_for(1ms);
promisesToClear.clear();
}
void sfz::FilePool::clearingThread()
{
while (!quitThread) {
tryToClearPromises();
std::this_thread::sleep_for(50ms);
}
}
void sfz::FilePool::loadingThread() noexcept void sfz::FilePool::loadingThread() noexcept
{ {
FilePromisePtr promise; FilePromisePtr promise;
while (!quitThread) { while (!quitThread) {
promisesToClean.clear();
if (emptyQueue) { if (emptyQueue) {
while(promiseQueue.try_dequeue(promise)) { while(promiseQueue.try_dequeue(promise)) {
// We're just dequeuing // We're just dequeuing
@ -197,11 +213,16 @@ void sfz::FilePool::clear()
emptyFileLoadingQueues(); emptyFileLoadingQueues();
preloadedFiles.clear(); preloadedFiles.clear();
temporaryFilePromises.clear(); temporaryFilePromises.clear();
promisesToClean.clear(); promisesToClear.clear();
} }
void sfz::FilePool::cleanupPromises() noexcept void sfz::FilePool::cleanupPromises() noexcept
{ {
AtomicGuard guard { addingPromisesToClear };
if (!canAddPromisesToClear)
return;
FilePromisePtr promise; FilePromisePtr promise;
// Remove stuff from the filled queue and put them in a linear storage // Remove stuff from the filled queue and put them in a linear storage
while (filledPromiseQueue.try_dequeue(promise)) while (filledPromiseQueue.try_dequeue(promise))
@ -211,7 +232,7 @@ void sfz::FilePool::cleanupPromises() noexcept
auto sentinel = temporaryFilePromises.end() - 1; auto sentinel = temporaryFilePromises.end() - 1;
while (promiseIterator != temporaryFilePromises.end()) { while (promiseIterator != temporaryFilePromises.end()) {
if (promiseIterator->use_count() == 1) { if (promiseIterator->use_count() == 1) {
promisesToClean.push_back(*promiseIterator); promisesToClear.push_back(*promiseIterator);
std::iter_swap(promiseIterator, sentinel); std::iter_swap(promiseIterator, sentinel);
sentinel--; sentinel--;
temporaryFilePromises.pop_back(); temporaryFilePromises.pop_back();

View file

@ -26,6 +26,7 @@
#include "Defaults.h" #include "Defaults.h"
#include "LeakDetector.h" #include "LeakDetector.h"
#include "AudioBuffer.h" #include "AudioBuffer.h"
#include "AudioSpan.h"
#include "SIMDHelpers.h" #include "SIMDHelpers.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>
@ -61,7 +62,7 @@ struct FilePromise
AudioBufferPtr preloadedData {}; AudioBufferPtr preloadedData {};
std::unique_ptr<AudioBuffer<float>> fileData {}; std::unique_ptr<AudioBuffer<float>> fileData {};
float sampleRate { config::defaultSampleRate }; float sampleRate { config::defaultSampleRate };
std::atomic<int> availableFrames { 0 }; std::atomic_size_t availableFrames { 0 };
std::atomic<bool> dataReady { false }; std::atomic<bool> dataReady { false };
Oversampling oversamplingFactor { config::defaultOversamplingFactor }; Oversampling oversamplingFactor { config::defaultOversamplingFactor };
}; };
@ -92,6 +93,7 @@ public:
{ {
for (int i = 0; i < config::numBackgroundThreads; ++i) for (int i = 0; i < config::numBackgroundThreads; ++i)
fileLoadingThreadPool.emplace_back( &FilePool::loadingThread, this ); fileLoadingThreadPool.emplace_back( &FilePool::loadingThread, this );
fileLoadingThreadPool.emplace_back( &FilePool::clearingThread, this );
} }
~FilePool() ~FilePool()
@ -200,6 +202,8 @@ public:
private: private:
fs::path rootDirectory; fs::path rootDirectory;
void loadingThread() noexcept; void loadingThread() noexcept;
void clearingThread();
void tryToClearPromises();
moodycamel::BlockingConcurrentQueue<FilePromisePtr> promiseQueue { config::maxVoices }; moodycamel::BlockingConcurrentQueue<FilePromisePtr> promiseQueue { config::maxVoices };
moodycamel::BlockingConcurrentQueue<FilePromisePtr> filledPromiseQueue { config::maxVoices }; moodycamel::BlockingConcurrentQueue<FilePromisePtr> filledPromiseQueue { config::maxVoices };
@ -211,7 +215,10 @@ private:
std::atomic<int> threadsLoading { 0 }; std::atomic<int> threadsLoading { 0 };
std::vector<FilePromisePtr> temporaryFilePromises; std::vector<FilePromisePtr> temporaryFilePromises;
std::vector<FilePromisePtr> promisesToClean; std::vector<FilePromisePtr> promisesToClear;
std::atomic<bool> addingPromisesToClear;
std::atomic<bool> canAddPromisesToClear;
absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles; absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles;
std::vector<std::thread> fileLoadingThreadPool { }; std::vector<std::thread> fileLoadingThreadPool { };
LEAK_DETECTOR(FilePool); LEAK_DETECTOR(FilePool);