Auto-cleaning file pool

This commit is contained in:
Jean Pierre Cimalando 2020-10-21 01:51:38 +02:00
parent e6ecbc6fcd
commit 0e24daf26d
3 changed files with 30 additions and 38 deletions

View file

@ -342,9 +342,9 @@ sfz::FileDataHolder sfz::FilePool::loadFile(const FileId& fileId) noexcept
} }
} }
sfz::FileDataHolder sfz::FilePool::getFilePromise(const FileId& fileId) noexcept sfz::FileDataHolder sfz::FilePool::getFilePromise(const std::shared_ptr<FileId>& fileId) noexcept
{ {
const auto preloaded = preloadedFiles.find(fileId); const auto preloaded = preloadedFiles.find(*fileId);
if (preloaded == preloadedFiles.end()) { if (preloaded == preloadedFiles.end()) {
DBG("[sfizz] File not found in the preloaded files: " << fileId); DBG("[sfizz] File not found in the preloaded files: " << fileId);
return {}; return {};
@ -381,14 +381,20 @@ void sfz::FilePool::loadingJob(QueuedFileData data) noexcept
{ {
raiseCurrentThreadPriority(); raiseCurrentThreadPriority();
std::shared_ptr<FileId> id = data.id.lock();
if (!id) {
// file ID was nulled, it means the region was deleted, ignore
return;
}
const auto loadStartTime = std::chrono::high_resolution_clock::now(); const auto loadStartTime = std::chrono::high_resolution_clock::now();
const auto waitDuration = loadStartTime - data.queuedTime; const auto waitDuration = loadStartTime - data.queuedTime;
const fs::path file { rootDirectory / data.id.filename() }; const fs::path file { rootDirectory / id->filename() };
std::error_code readError; std::error_code readError;
AudioReaderPtr reader = createAudioReader(file, data.id.isReverse(), &readError); AudioReaderPtr reader = createAudioReader(file, id->isReverse(), &readError);
if (readError) { if (readError) {
DBG("[sfizz] libsndfile errored for " << data.id << " with message " << readError.message()); DBG("[sfizz] libsndfile errored for " << *id << " with message " << readError.message());
return; return;
} }
@ -398,7 +404,7 @@ void sfz::FilePool::loadingJob(QueuedFileData data) noexcept
while (currentStatus == FileData::Status::Invalid) { while (currentStatus == FileData::Status::Invalid) {
// Spin until the state changes // Spin until the state changes
if (spinCounter > 1024) { if (spinCounter > 1024) {
DBG("[sfizz] " << data.id << " is stuck on Invalid? Leaving the load"); DBG("[sfizz] " << *id << " is stuck on Invalid? Leaving the load");
return; return;
} }
@ -418,13 +424,13 @@ void sfz::FilePool::loadingJob(QueuedFileData data) noexcept
const auto frames = static_cast<uint32_t>(reader->frames()); const auto frames = static_cast<uint32_t>(reader->frames());
streamFromFile(*reader, frames, oversamplingFactor, data.data->fileData, &data.data->availableFrames); streamFromFile(*reader, frames, oversamplingFactor, data.data->fileData, &data.data->availableFrames);
const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime; const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime;
logger.logFileTime(waitDuration, loadDuration, frames, data.id.filename()); logger.logFileTime(waitDuration, loadDuration, frames, id->filename());
data.data->status = FileData::Status::Done; data.data->status = FileData::Status::Done;
std::lock_guard<SpinMutex> guard { lastUsedMutex }; std::lock_guard<SpinMutex> guard { lastUsedMutex };
if (absl::c_find(lastUsedFiles, data.id) == lastUsedFiles.end()) if (absl::c_find(lastUsedFiles, *id) == lastUsedFiles.end())
lastUsedFiles.push_back(data.id); lastUsedFiles.push_back(*id);
} }
void sfz::FilePool::clear() void sfz::FilePool::clear()
@ -487,18 +493,13 @@ void sfz::FilePool::dispatchingJob() noexcept
{ {
QueuedFileData queuedData; QueuedFileData queuedData;
while (dispatchBarrier.wait(), dispatchFlag) { while (dispatchBarrier.wait(), dispatchFlag) {
if (emptyQueueFlag) {
while (filesToLoad.try_pop(queuedData)) {
// pass
}
semEmptyQueueFinished.post();
emptyQueueFlag = false;
continue;
}
std::lock_guard<std::mutex> guard { loadingJobsMutex }; std::lock_guard<std::mutex> guard { loadingJobsMutex };
if (filesToLoad.try_pop(queuedData)) { if (filesToLoad.try_pop(queuedData)) {
if (!queuedData.id.lock()) {
// file ID was nulled, it means the region was deleted, ignore
}
else
loadingJobs.push_back( loadingJobs.push_back(
threadPool->enqueue([this](const QueuedFileData& data) { loadingJob(data); }, queuedData)); threadPool->enqueue([this](const QueuedFileData& data) { loadingJob(data); }, queuedData));
} }
@ -521,17 +522,6 @@ void sfz::FilePool::garbageJob() noexcept
} }
} }
void sfz::FilePool::emptyFileLoadingQueues() noexcept
{
ASSERT(dispatchFlag);
emptyQueueFlag = true;
std::error_code ec;
dispatchBarrier.post(ec);
if (!ec)
semEmptyQueueFinished.wait();
}
void sfz::FilePool::waitForBackgroundLoading() noexcept void sfz::FilePool::waitForBackgroundLoading() noexcept
{ {
std::lock_guard<std::mutex> guard { loadingJobsMutex }; std::lock_guard<std::mutex> guard { loadingJobsMutex };

View file

@ -263,7 +263,7 @@ public:
* @param fileId the file to preload * @param fileId the file to preload
* @return FileDataHolder a file data handle * @return FileDataHolder a file data handle
*/ */
FileDataHolder getFilePromise(const FileId& fileId) noexcept; FileDataHolder getFilePromise(const std::shared_ptr<FileId>& fileId) noexcept;
/** /**
* @brief Change the preloading size. This will trigger a full * @brief Change the preloading size. This will trigger a full
* reload of all samples, so don't call it on the audio thread. * reload of all samples, so don't call it on the audio thread.
@ -296,7 +296,11 @@ public:
* method on the audio thread as it will spinlock. * method on the audio thread as it will spinlock.
* *
*/ */
void emptyFileLoadingQueues() noexcept; void emptyFileLoadingQueues() noexcept
{
// nothing to do in this implementation,
// deleting the region and its sample ID take care of it
}
/** /**
* @brief Wait for the background loading to finish for all promises * @brief Wait for the background loading to finish for all promises
* in the queue. * in the queue.
@ -331,9 +335,7 @@ private:
// Signals // Signals
volatile bool dispatchFlag { true }; volatile bool dispatchFlag { true };
volatile bool garbageFlag { true }; volatile bool garbageFlag { true };
volatile bool emptyQueueFlag { false };
RTSemaphore dispatchBarrier; RTSemaphore dispatchBarrier;
RTSemaphore semEmptyQueueFinished;
RTSemaphore semGarbageBarrier; RTSemaphore semGarbageBarrier;
// Structures for the background loaders // Structures for the background loaders
@ -341,13 +343,13 @@ private:
{ {
using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>; using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
QueuedFileData() = default; QueuedFileData() = default;
QueuedFileData(FileId id, FileData* data, TimePoint queuedTime) QueuedFileData(std::weak_ptr<FileId> id, FileData* data, TimePoint queuedTime)
: id(id), data(data), queuedTime(queuedTime) {} : id(id), data(data), queuedTime(queuedTime) {}
QueuedFileData(const QueuedFileData&) = default; QueuedFileData(const QueuedFileData&) = default;
QueuedFileData& operator=(const QueuedFileData&) = default; QueuedFileData& operator=(const QueuedFileData&) = default;
QueuedFileData(QueuedFileData&&) = default; QueuedFileData(QueuedFileData&&) = default;
QueuedFileData& operator=(QueuedFileData&&) = default; QueuedFileData& operator=(QueuedFileData&&) = default;
FileId id {}; std::weak_ptr<FileId> id;
FileData* data { nullptr }; FileData* data { nullptr };
TimePoint queuedTime {}; TimePoint queuedTime {};
}; };

View file

@ -94,7 +94,7 @@ void sfz::Voice::startVoice(Region* region, int delay, const TriggerEvent& event
} }
setupOscillatorUnison(); setupOscillatorUnison();
} else { } else {
currentPromise = resources.filePool.getFilePromise(*region->sampleId); currentPromise = resources.filePool.getFilePromise(region->sampleId);
if (!currentPromise) { if (!currentPromise) {
switchState(State::cleanMeUp); switchState(State::cleanMeUp);
return; return;