From 8b13479806453d65c4e4a09e1080cafd75326dd4 Mon Sep 17 00:00:00 2001 From: Florian Fischer <florian.fl.fischer@fau.de> Date: Fri, 19 Feb 2021 21:54:06 +0100 Subject: [PATCH] [Actor] use AlarmFuture for Actor::waitUntilIdle if IO is enabled Using an AlarmFuture blocks the Fiber executing Actor::waitUntilIdle, freeing its current worker and thus preventing life-locks. Where all workers are sleeping except one. Which executes the Actor::waitUntilIdle Fiber causing a starvation of the actual Actor. --- emper/Actor.hpp | 34 ++++++++++++++++++++++---------- tests/AlarmActorTest.cpp | 2 +- tests/SimpleActorTest.cpp | 2 +- tests/UnblockOnMainActorTest.cpp | 2 +- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/emper/Actor.hpp b/emper/Actor.hpp index f2b1d57b..3b95c0d6 100644 --- a/emper/Actor.hpp +++ b/emper/Actor.hpp @@ -8,6 +8,7 @@ #include "CallerEnvironment.hpp" #include "Fiber.hpp" #include "UnboundedBlockingMpscQueue.hpp" +#include "io/Future.hpp" template <typename T> class Actor { @@ -77,17 +78,30 @@ class Actor { auto pendingMailboxItems() -> size_t { return queue.size(); } auto waitUntilIdle(long timeout) -> bool { - const auto start = std::chrono::steady_clock::now(); - const auto deadline = start + std::chrono::milliseconds(timeout); - while (!(queue.size() == 0 && state.load(std::memory_order_acquire) == Retrieving)) { - // TODO: The suppressed linter error below may be a false positive - // reported by clang-tidy. - // NOLINTNEXTLINE(modernize-use-nullptr) - if (std::chrono::steady_clock::now() > deadline) { - return false; + if constexpr (emper::IO) { + for (; timeout > 0; --timeout) { + emper::io::AlarmFuture::Timespec ts = {.tv_sec = 1, .tv_nsec = 0}; + emper::io::AlarmFuture alarm(ts); + alarm.submitAndWait(); + if (queue.size() == 0 && state.load(std::memory_order_acquire) == Retrieving) { + return true; + } } - } - return true; + return false; + } else { + const auto start = std::chrono::steady_clock::now(); + const auto deadline = start + std::chrono::seconds(timeout); + while (!(queue.size() == 0 && state.load(std::memory_order_acquire) == Retrieving)) { + // TODO: The suppressed linter error below may be a false positive + // reported by clang-tidy. + // NOLINTNEXTLINE(modernize-use-nullptr) + if (std::chrono::steady_clock::now() > deadline) { + return false; + } + } + + return true; + } } }; diff --git a/tests/AlarmActorTest.cpp b/tests/AlarmActorTest.cpp index a0f962b6..31c9c46a 100644 --- a/tests/AlarmActorTest.cpp +++ b/tests/AlarmActorTest.cpp @@ -61,7 +61,7 @@ auto main(int argc, char* argv[]) -> int { cps.wait(); // Wait for the actor to become idle. - bool actorIdle = alarmActor.waitUntilIdle(60 * 1000); + bool actorIdle = alarmActor.waitUntilIdle(60); if (!actorIdle) { std::cerr << "FAILURE: Actor did not went idle"; exit(EXIT_FAILURE); diff --git a/tests/SimpleActorTest.cpp b/tests/SimpleActorTest.cpp index 3415863c..76b4cfe3 100644 --- a/tests/SimpleActorTest.cpp +++ b/tests/SimpleActorTest.cpp @@ -63,7 +63,7 @@ static void mainFiber(void* runtime_ptr) { cps.wait(); // Wait for the actor to become idle. - bool actorIdle = sumActor.waitUntilIdle(60 * 1000); + bool actorIdle = sumActor.waitUntilIdle(60); if (!actorIdle) { std::cerr << "FAILURE: Actor did not went idle"; exit(EXIT_FAILURE); diff --git a/tests/UnblockOnMainActorTest.cpp b/tests/UnblockOnMainActorTest.cpp index 020db28f..6ae3883f 100644 --- a/tests/UnblockOnMainActorTest.cpp +++ b/tests/UnblockOnMainActorTest.cpp @@ -76,7 +76,7 @@ auto main(int argc, char* argv[]) -> int { cps.wait(); // Wait for the actor to become idle. - bool actorIdle = alarmActor.waitUntilIdle(60 * 1000); + bool actorIdle = alarmActor.waitUntilIdle(60); if (!actorIdle) { std::cerr << "FAILURE: Actor did not went idle"; quit = true; -- GitLab