Merge pull request #293 from jpcima/scheduling

Scheduling
This commit is contained in:
JP Cimalando 2020-07-04 11:01:21 +02:00 committed by GitHub
commit 2f173f7209
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 23 deletions

View file

@ -99,6 +99,10 @@ namespace config {
static constexpr double amplitudeTriangle = 0.625; static constexpr double amplitudeTriangle = 0.625;
static constexpr double amplitudeSaw = 0.515; static constexpr double amplitudeSaw = 0.515;
static constexpr double amplitudeSquare = 0.515; static constexpr double amplitudeSquare = 0.515;
/**
Background file loading
*/
static constexpr int backgroundLoaderPthreadPriority = 50; // expressed in %
} // namespace config } // namespace config
} // namespace sfz } // namespace sfz

View file

@ -37,8 +37,14 @@
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <sndfile.hh>
#include <thread> #include <thread>
#include <system_error>
#include <sndfile.hh>
#if defined(_WIN32)
#include <windows.h>
#else
#include <pthread.h>
#endif
void readBaseFile(SndfileHandle& sndFile, sfz::FileAudioBuffer& output, uint32_t numFrames, bool reverse) void readBaseFile(SndfileHandle& sndFile, sfz::FileAudioBuffer& output, uint32_t numFrames, bool reverse)
{ {
@ -125,11 +131,16 @@ sfz::FilePool::~FilePool()
{ {
quitThread = true; quitThread = true;
std::error_code ec;
for (unsigned i = 0; i < threadPool.size(); ++i) { for (unsigned i = 0; i < threadPool.size(); ++i) {
std::error_code ec; ec = std::error_code();
workerBarrier.post(ec); workerBarrier.post(ec);
} }
ec = std::error_code();
semClearingRequest.post(ec);
for (auto& thread: threadPool) for (auto& thread: threadPool)
thread.join(); thread.join();
} }
@ -360,28 +371,36 @@ void sfz::FilePool::tryToClearPromises()
void sfz::FilePool::clearingThread() void sfz::FilePool::clearingThread()
{ {
while (!quitThread) { raiseCurrentThreadPriority();
RTSemaphore& request = semClearingRequest;
do {
request.wait();
if (quitThread)
return;
tryToClearPromises(); tryToClearPromises();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); } while (1);
}
} }
void sfz::FilePool::loadingThread() noexcept void sfz::FilePool::loadingThread() noexcept
{ {
raiseCurrentThreadPriority();
FilePromisePtr promise; FilePromisePtr promise;
while (!quitThread) { do {
workerBarrier.wait();
if (emptyQueue) { if (emptyQueue) {
while(promiseQueue.try_pop(promise)) { while (promiseQueue.try_pop(promise)) {
// We're just dequeuing // We're just dequeuing
} }
emptyQueue = false; emptyQueue = false;
semEmptyQueueFinished.post();
continue; continue;
} }
std::error_code ec; if (quitThread)
workerBarrier.wait(ec); return;
ASSERT(!ec);
if (!promiseQueue.try_pop(promise)) { if (!promiseQueue.try_pop(promise)) {
continue; continue;
@ -406,13 +425,11 @@ void sfz::FilePool::loadingThread() noexcept
threadsLoading--; threadsLoading--;
while (!filledPromiseQueue.try_push(promise)) { semFilledPromiseQueueAvailable.wait();
DBG("[sfizz] Error enqueuing the promise for " << promise->fileId << " in the filledPromiseQueue"); filledPromiseQueue.push(promise);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
promise.reset(); promise.reset();
} } while (1);
} }
void sfz::FilePool::clear() void sfz::FilePool::clear()
@ -438,12 +455,15 @@ void sfz::FilePool::cleanupPromises() noexcept
// Remove the promises from the filled queue and put them in a linear // Remove the promises from the filled queue and put them in a linear
// storage // storage
FilePromisePtr promise; FilePromisePtr promise;
while (filledPromiseQueue.try_pop(promise)) while (filledPromiseQueue.try_pop(promise)) {
semFilledPromiseQueueAvailable.post();
temporaryFilePromises.push_back(promise); temporaryFilePromises.push_back(promise);
}
auto promiseUsedOnce = [](FilePromisePtr& p) { return p.use_count() == 1; }; auto promiseUsedOnce = [](FilePromisePtr& p) { return p.use_count() == 1; };
auto moveToClear = [&](FilePromisePtr& p) { return promisesToClear.push_back(p); }; auto moveToClear = [&](FilePromisePtr& p) { return promisesToClear.push_back(p); };
swapAndPopAll(temporaryFilePromises, promiseUsedOnce, moveToClear); if (swapAndPopAll(temporaryFilePromises, promiseUsedOnce, moveToClear) > 0)
semClearingRequest.post();
} }
void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept
@ -474,12 +494,8 @@ uint32_t sfz::FilePool::getPreloadSize() const noexcept
void sfz::FilePool::emptyFileLoadingQueues() noexcept void sfz::FilePool::emptyFileLoadingQueues() noexcept
{ {
emptyQueue = true; emptyQueue = true;
std::error_code ec; workerBarrier.post();
workerBarrier.post(ec); semEmptyQueueFinished.wait();
ASSERT(!ec);
while (emptyQueue)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} }
void sfz::FilePool::waitForBackgroundLoading() noexcept void sfz::FilePool::waitForBackgroundLoading() noexcept
@ -496,3 +512,35 @@ void sfz::FilePool::waitForBackgroundLoading() noexcept
std::this_thread::sleep_for(std::chrono::microseconds(100)); std::this_thread::sleep_for(std::chrono::microseconds(100));
} }
} }
void sfz::FilePool::raiseCurrentThreadPriority() noexcept
{
#if defined(_WIN32)
HANDLE thread = GetCurrentThread();
const int priority = THREAD_PRIORITY_ABOVE_NORMAL; /*THREAD_PRIORITY_HIGHEST*/
if (!SetThreadPriority(thread, priority)) {
std::system_error error(GetLastError(), std::system_category());
DBG("[sfizz] Cannot set current thread priority: " << error.what());
}
#else
pthread_t thread = pthread_self();
int policy;
sched_param param;
if (pthread_getschedparam(thread, &policy, &param) != 0) {
DBG("[sfizz] Cannot get current thread scheduling parameters");
return;
}
policy = SCHED_RR;
const int minprio = sched_get_priority_min(policy);
const int maxprio = sched_get_priority_max(policy);
param.sched_priority = minprio +
config::backgroundLoaderPthreadPriority * (maxprio - minprio) / 100;
if (pthread_setschedparam(thread, policy, &param) != 0) {
DBG("[sfizz] Cannot set current thread scheduling parameters");
return;
}
#endif
}

View file

@ -259,6 +259,11 @@ public:
* in the queue. * in the queue.
*/ */
void waitForBackgroundLoading() noexcept; void waitForBackgroundLoading() noexcept;
/**
* @brief Assign the current thread a priority which is appropriate
* for background sample file processing.
*/
static void raiseCurrentThreadPriority() noexcept;
private: private:
Logger& logger; Logger& logger;
fs::path rootDirectory; fs::path rootDirectory;
@ -268,13 +273,16 @@ private:
atomic_queue::AtomicQueue2<FilePromisePtr, config::maxVoices> promiseQueue; atomic_queue::AtomicQueue2<FilePromisePtr, config::maxVoices> promiseQueue;
atomic_queue::AtomicQueue2<FilePromisePtr, config::maxVoices> filledPromiseQueue; atomic_queue::AtomicQueue2<FilePromisePtr, config::maxVoices> filledPromiseQueue;
RTSemaphore semFilledPromiseQueueAvailable { config::maxVoices };
uint32_t preloadSize { config::preloadSize }; uint32_t preloadSize { config::preloadSize };
Oversampling oversamplingFactor { config::defaultOversamplingFactor }; Oversampling oversamplingFactor { config::defaultOversamplingFactor };
// Signals // Signals
volatile bool quitThread { false }; volatile bool quitThread { false };
volatile bool emptyQueue { false }; volatile bool emptyQueue { false };
RTSemaphore semEmptyQueueFinished;
std::atomic<int> threadsLoading { 0 }; std::atomic<int> threadsLoading { 0 };
RTSemaphore workerBarrier; RTSemaphore workerBarrier;
RTSemaphore semClearingRequest;
// File promises data structures along with their guards. // File promises data structures along with their guards.
std::vector<FilePromisePtr> emptyPromises; std::vector<FilePromisePtr> emptyPromises;