diff --git a/emper/Actor.hpp b/emper/Actor.hpp index 3b95c0d673c0b0c5075eb5478eda41eee10030f0..80fa5f7b25c42cc748ddb53838c21d92ee3f28f5 100644 --- a/emper/Actor.hpp +++ b/emper/Actor.hpp @@ -8,7 +8,7 @@ #include "CallerEnvironment.hpp" #include "Fiber.hpp" #include "UnboundedBlockingMpscQueue.hpp" -#include "io/Future.hpp" +#include "emper.hpp" template <typename T> class Actor { @@ -78,30 +78,22 @@ class Actor { auto pendingMailboxItems() -> size_t { return queue.size(); } auto waitUntilIdle(long timeout) -> bool { - 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; - } + 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)) { + if constexpr (emper::IO) { + emper::sleep(1); + } else { + emper::yield(); } - - 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; - } + // 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; } + + return true; } }; diff --git a/emper/Runtime.cpp b/emper/Runtime.cpp index e195d3e05f24a07876511c1beb6002b2cc56fc54..17ef6b9b0982a8d6921439403872736090a32f6c 100644 --- a/emper/Runtime.cpp +++ b/emper/Runtime.cpp @@ -14,7 +14,8 @@ #include <memory> // for __shared_ptr_access, shared_ptr #include <string> // for string -#include "Common.hpp" // for DIE_MSG_ERRNO, DIE, DIE_MSG +#include "Common.hpp" // for DIE_MSG_ERRNO, DIE, DIE_MSG +#include "Context.hpp" #include "ContextManager.hpp" // for ContextManager #include "Debug.hpp" // for DBG, ABORT, LOGD, LOGE #include "Emper.hpp" @@ -209,6 +210,15 @@ auto Runtime::workerLoop(Worker* worker) -> void* { return nullptr; } +void Runtime::yield() { + Context* context = Context::getCurrentContext(); + contextManager.saveAndStartNew([context, this] { + Fiber* resumeFiber = + Fiber::from([context, this] { this->contextManager.discardAndResume(context); }); + this->scheduleFromAnywhere(*resumeFiber); + }); +} + auto Runtime::nextFiber() -> NextFiberResult { if constexpr (emper::IO) { // Schedule all fibers waiting on completed IO diff --git a/emper/Runtime.hpp b/emper/Runtime.hpp index 803d935d38fb15e7a8a58ec24b8142985781373e..92487a336c3219860a1dafce3182c955df6113cd 100644 --- a/emper/Runtime.hpp +++ b/emper/Runtime.hpp @@ -153,6 +153,8 @@ class Runtime : public Logger<LogSubsystem::RUNTI> { inline void scheduleFromAnywhere(Fiber& fiber) { scheduler.scheduleFromAnywhere(fiber); } + void yield(); + // TODO: This should probably not be a public method of Runtime. auto nextFiber() -> NextFiberResult; diff --git a/emper/include/emper.hpp b/emper/include/emper.hpp index 82e1f259a784354baa923ddd31a3cb65887bb88b..c5691e232354e9803ab2c7e978de0f503b1c74f5 100644 --- a/emper/include/emper.hpp +++ b/emper/include/emper.hpp @@ -6,9 +6,11 @@ #include <functional> #include <utility> +#include "Common.hpp" #include "Fiber.hpp" #include "Runtime.hpp" #include "SynchronizedFiber.hpp" +#include "io/Future.hpp" void async(Fiber* fiber) { assert(fiber != nullptr); @@ -59,3 +61,22 @@ void spawn(Fiber::fiber_fun0_t function, workeraffinity_t* affinity, S& semaphor Fiber* fiber = SynchronizedFiber::from(function, affinity, semaphore); async(fiber); } + +namespace emper { +void yield() { + Runtime* runtime = Runtime::getRuntime(); + runtime->yield(); +} + +auto sleep(unsigned int seconds) -> bool { + if constexpr (!emper::IO) { + DIE_MSG("sleep requires emper::io"); + } + + emper::io::AlarmFuture::Timespec ts = {.tv_sec = seconds, .tv_nsec = 0}; + emper::io::AlarmFuture alarm(ts); + int32_t res = alarm.submitAndWait(); + + return res == -ETIME; +} +} // namespace emper diff --git a/tests/YieldToAnywhereTest.cpp b/tests/YieldToAnywhereTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee0fb8ef89552e30b4119c41e0017e25494e0a47 --- /dev/null +++ b/tests/YieldToAnywhereTest.cpp @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2020 Florian Schmaus +#include <cstdlib> + +#include "CountingPrivateSemaphore.hpp" +#include "emper.hpp" + +void emperTest() { + const unsigned fiberCount = 100; + CPS cps; + for (unsigned i = 0; i < fiberCount; ++i) { + spawn( + [] { + for (unsigned i = 0; i < 100; ++i) { + emper::yield(); + } + }, + cps); + } + cps.wait(); + exit(EXIT_SUCCESS); +} diff --git a/tests/meson.build b/tests/meson.build index 89518c6bf6099a3d023c4434af28831f333fbb22..52662f38a5bcdb2ce515401a27afcbf3f56911c0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -29,6 +29,12 @@ tests = { 'description': 'Test EMPER\'s C++ API', }, + 'YieldToAnywhereTest.cpp': + { + 'description': 'Test emper::yieldToAnywhere', + 'test_runner': 'emper', + }, + 'ReuseBpsTest.cpp': { 'description': 'Test resetting of BPSs',