diff --git a/emper/Runtime.hpp b/emper/Runtime.hpp index 1fc424eb4705ddc18d7c2832fcde05cb25308cbc..c1868e77f67aba1571b68660a4cd963acb0516a3 100644 --- a/emper/Runtime.hpp +++ b/emper/Runtime.hpp @@ -18,6 +18,7 @@ #include "CallerEnvironment.hpp" #include "Context.hpp" #include "Debug.hpp" +#include "Emper.hpp" #include "NextFiberResult.hpp" #include "Scheduler.hpp" // for Scheduler #include "Worker.hpp" @@ -96,7 +97,13 @@ class Runtime : public Logger<LogSubsystem::RUNTI> { template <CallerEnvironment callerEnvironment = CallerEnvironment::EMPER> inline void wakeupSleepingWorkers() { - workerSleepStrategy.notifyOne<callerEnvironment>(); + if constexpr (::emper::WORKER_WAKEUP_STRATEGY == ::emper::WorkerWakeupStrategy::one) { + workerSleepStrategy.notifyOne<callerEnvironment>(); + } else if constexpr (::emper::WORKER_WAKEUP_STRATEGY == ::emper::WorkerWakeupStrategy::all) { + workerSleepStrategy.notifyAll<callerEnvironment>(); + } else { + ABORT("Unknown CallerEnvironment"); + } } void maybeTerminateWorker() { diff --git a/emper/sleep_strategy/AbstractWorkerSleepStrategy.hpp b/emper/sleep_strategy/AbstractWorkerSleepStrategy.hpp index 335cc426f668772a70147e4d2a104516ba88d458..f0fbc4af9d8e5a6dfa227c98daed1a25b6684b09 100644 --- a/emper/sleep_strategy/AbstractWorkerSleepStrategy.hpp +++ b/emper/sleep_strategy/AbstractWorkerSleepStrategy.hpp @@ -5,6 +5,7 @@ #include <stdexcept> #include "CallerEnvironment.hpp" +#include "Emper.hpp" namespace emper::sleep_strategy { diff --git a/emper/sleep_strategy/SemaphoreWorkerSleepStrategy.hpp b/emper/sleep_strategy/SemaphoreWorkerSleepStrategy.hpp index 2a81e061b894f5c47aba4643e66208036d74e632..c49439e046481b04986ef4a6f21bb56965cf7240 100644 --- a/emper/sleep_strategy/SemaphoreWorkerSleepStrategy.hpp +++ b/emper/sleep_strategy/SemaphoreWorkerSleepStrategy.hpp @@ -5,8 +5,6 @@ #include <atomic> #include "CallerEnvironment.hpp" -#include "Debug.hpp" -#include "Emper.hpp" #include "Worker.hpp" #include "emper-common.h" #include "emper-config.h" @@ -102,15 +100,15 @@ class AbstractSemaphoreWorkerSleepStrategy } } - // The actual semaphore based worker sleep algorithm template <CallerEnvironment callerEnvironment> - void notifyInternal() { + [[nodiscard]] inline auto mustNotify() -> bool { typename Sem::CounterType skipWakeupThreshold; if constexpr (callerEnvironment == CallerEnvironment::ANYWHERE) { // On external work we always increment the semaphore unless we observe // that its value is > workerCount. // If we observe semValue > workerCount we are ensured that some worker will iterate // its dispatchLoop again and must observe the new work. + // TODO: Could it be >= workerCount ? skipWakeupThreshold = workerCount; } else { // For work from within emper we skip wakeup if we observe no one sleeping. @@ -121,40 +119,15 @@ class AbstractSemaphoreWorkerSleepStrategy // Note that sem_getvalue() is allowed to return 0 if there are // waiting workers, hence we need to set the threshold also to // 0. This has the disadvantage that we will perform one - // unnecessary sem_post. If we ever switch the wakeupSem - // implementation, then the skipWakeupThreshold value should be + // unnecessary sem_post. If we are sure the wakeupSem + // implementation does not return 0 with waiters, + // then the skipWakeupThreshold value should be // reviewed and potentially changed to '-1'. skipWakeupThreshold = 0; } auto semValue = wakeupSem.getValue(); - if (semValue > skipWakeupThreshold) { - return; - } - - if constexpr (::emper::WORKER_WAKEUP_STRATEGY == ::emper::WorkerWakeupStrategy::one) { - wakeupSem.notify(); - } else if constexpr (::emper::WORKER_WAKEUP_STRATEGY == ::emper::WorkerWakeupStrategy::all) { - // notify all we observed sleeping - // It is sound to increment the semaphore to much, thus this will only cause - // workers to iterate the dispatchLoop more often before actually sleeping - - // TODO: Switch to c++20 std::counting_semaphore, which has release(std::ptrdiff_t) - - // Reading the manpage explains the function. - // POSIX sem_getvalue is allowed to return 0 or a negative count if there are - // waiters. - // Linux sem_getvalue indeed does return 0 - // To notify all sleeping workers we increment the semaphore once for each worker. - if (semValue == 0) { - semValue = workerCount; - } - - // make sure that the amount to notify is always positive - wakeupSem.notify_many(semValue < 0 ? -semValue : semValue); - } else { - ABORT("Unknown CallerEnvironment"); - } + return !(semValue > skipWakeupThreshold); } public: @@ -174,19 +147,26 @@ class AbstractSemaphoreWorkerSleepStrategy template <CallerEnvironment callerEnvironment> inline void notifyOne() { - notifyInternal<callerEnvironment>(); + if (mustNotify<callerEnvironment>()) { + wakeupSem.notify(); + } } template <CallerEnvironment callerEnvironment> inline void notifyMany(unsigned count) { - for (unsigned i = 0; i < count; ++i) { - notifyInternal<callerEnvironment>(); + if (mustNotify<callerEnvironment>()) { + wakeupSem.notify_many(count); } } + // notify all workers + // It is sound to increment the semaphore to much, thus this will only cause + // workers to iterate the dispatchLoop more often before actually sleeping template <CallerEnvironment callerEnvironment> inline void notifyAll() { - notifyMany<callerEnvironment>(workerCount); + if (mustNotify<callerEnvironment>()) { + wakeupSem.notify_many(workerCount); + } } template <CallerEnvironment callerEnvironment>