diff --git a/emper/Context.hpp b/emper/Context.hpp index e1bbd7ce279da074e14f529ce643afa2a2b20e71..3428ccc203e9baabdc5c0d780f0a4f7b3e16824f 100644 --- a/emper/Context.hpp +++ b/emper/Context.hpp @@ -14,6 +14,8 @@ #include "Debug.hpp" // for LOGD, LogSubsystem, LogSubsystem::C, Logger #include "Emper.hpp" // for Emper::DEBUG +class ContextManager; +class Dispatcher; class Fiber; extern "C" [[noreturn]] void switch_and_load_context(void** toTos); @@ -29,7 +31,7 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { static thread_local Context* currentContext; - const Fiber* fiber = nullptr; + Fiber* currentFiber = nullptr; void* const tos; @@ -49,6 +51,10 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { friend auto operator<<(std::ostream&, const Context&) -> std::ostream&; + friend ContextManager; + + auto getFiber() -> Fiber* { return currentFiber; } + /** * The first function that a newly started context will * execute. This function simply first runs the pre main hook and @@ -60,6 +66,20 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { context->mainFunction(); } + friend Dispatcher; + + static void setCurrentFiber(Fiber* fiber) { + assert(currentContext); + + currentContext->currentFiber = fiber; + } + + static auto getCurrentFiber() -> Fiber* { + assert(currentContext); + + return currentContext->currentFiber; + } + public: // cppcheck-suppress noExplicitConstructor selfInitialization Context(func_t mainFunction) @@ -108,8 +128,6 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { inline void setHook(func_t hook) { startAndResumeHook = std::move(hook); } - inline void setFiber(const Fiber* fiber) { this->fiber = fiber; } - [[nodiscard]] inline auto getTos() const -> const void* { return tos; } /** diff --git a/emper/ContextManager.cpp b/emper/ContextManager.cpp index 552b6ae066b1228ca88a5fc5e455a000a5e22362..6eec4d55cf0ca31d519b9e09a1f1e14858de4295 100644 --- a/emper/ContextManager.cpp +++ b/emper/ContextManager.cpp @@ -10,6 +10,8 @@ #include "Dispatcher.hpp" // for Dispatcher #include "Runtime.hpp" // for Runtime +class Fiber; + ContextManager::ContextManager(Runtime& runtime) : MemoryManager(runtime), runtime(runtime) { auto newWorkerHook = [this]() { for (unsigned int i = 0; i < CONTEXT_MANAGER_FIRST_LAYER_QUEUE_SIZE * 2; ++i) { @@ -78,5 +80,12 @@ void ContextManager::discardAndResume(Context* context) { LOGD("Freeing context " << contextToFree); putFreeContext(contextToFree); }); + + // Since we are going to discard this context, it will never reach + // the end of its dispatch loop, and hence we need to ensure that + // its fiber is recycled. + Fiber* currentFiber = contextToFree->getFiber(); + runtime.dispatcher.recycle(currentFiber); + contextToFree->discardAndResume(context); } diff --git a/emper/Dispatcher.cpp b/emper/Dispatcher.cpp index 0cc1fb33c7cdfb87d5aa5d942d04b64ff2c2e171..5d05163dcbe67987b6e9cf6868117d4f7db6ac1e 100644 --- a/emper/Dispatcher.cpp +++ b/emper/Dispatcher.cpp @@ -7,8 +7,6 @@ #include "Emper.hpp" #include "Runtime.hpp" // for Runtime -thread_local const Fiber* Dispatcher::currentFiber; - auto Dispatcher::getDispatchLoop() -> func_t { return [this] { dispatchLoop(); }; } diff --git a/emper/Dispatcher.hpp b/emper/Dispatcher.hpp index 6b33d732b9ec56e0350f48edc91664f8eabe07ed..175240c5b74fb933d08bcd4ad58efeebe4c172b8 100644 --- a/emper/Dispatcher.hpp +++ b/emper/Dispatcher.hpp @@ -2,12 +2,9 @@ // Copyright © 2020 Florian Schmaus #pragma once -#include <cassert> // for assert - #include "Common.hpp" // for func_t #include "Context.hpp" // for Context #include "Debug.hpp" // for LOGD, LogSubsystem, LogSubsystem::DISP -#include "Emper.hpp" // for Emper::DEBUG #include "Fiber.hpp" // for Fiber #include "emper-common.h" // for workeraffinity_t @@ -16,8 +13,6 @@ class ContextManager; class Dispatcher : public Logger<LogSubsystem::DISP> { protected: - static thread_local const Fiber* currentFiber; - Runtime& runtime; void dispatchLoopDoSleep(); @@ -29,14 +24,9 @@ class Dispatcher : public Logger<LogSubsystem::DISP> { // The dispatch() method could theoretically be made static in // non-debug builds. // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - inline void dispatch(const Fiber* fiber) { + inline void dispatch(Fiber* fiber) { LOGD("executing fiber " << fiber); - currentFiber = fiber; - - if constexpr (emper::DEBUG) { - Context::getCurrentContext()->setFiber(fiber); - } - + Context::setCurrentFiber(fiber); fiber->run(); } @@ -50,24 +40,21 @@ class Dispatcher : public Logger<LogSubsystem::DISP> { return fiber->doAtomicDecrRefCount(); } - static inline void recycle(const Fiber* fiber) { delete fiber; } + virtual void recycle(Fiber* fiber) { delete fiber; } void putRuntimeWorkerToSleep(); public: Dispatcher(Runtime& runtime) : runtime(runtime) {} - static auto getCurrentFiber() -> const Fiber& { - const Fiber* fiber = getCurrentFiberPtr(); + static auto getCurrentFiber() -> Fiber& { + Fiber* fiber = getCurrentFiberPtr(); return *fiber; } - static auto getCurrentFiberPtr() -> const Fiber* { - assert(currentFiber); - return currentFiber; - } + static auto getCurrentFiberPtr() -> Fiber* { return Context::getCurrentFiber(); } - static auto isDispatchedControlFlow() -> bool { return currentFiber != nullptr; } + static auto isDispatchedControlFlow() -> bool { return getCurrentFiberPtr() != nullptr; } friend ContextManager; }; diff --git a/emper/strategies/laws/LawsDispatcher.cpp b/emper/strategies/laws/LawsDispatcher.cpp index 2ece84a4461af5bb88a6e7fde22c63f21db99d44..6930783d69ccc5cb17079286d42e7717a91121ed 100644 --- a/emper/strategies/laws/LawsDispatcher.cpp +++ b/emper/strategies/laws/LawsDispatcher.cpp @@ -10,6 +10,14 @@ #include "LawsStrategy.hpp" // for LawsStrategy, LawsStrategy::FiberSource #include "Runtime.hpp" +void LawsDispatcher::recycle(Fiber* fiber) { + // If the ref count has not reached zero yet, do not recycle the + // fiber. + if (decreaseRefCount(fiber)) return; + + Dispatcher::recycle(fiber); +} + void LawsDispatcher::dispatchLoop() { while (true) { Fiber* const fiber = runtime.nextFiber(); @@ -51,8 +59,6 @@ void LawsDispatcher::dispatchLoop() { if (affinity) *affinity = Runtime::getWorkerId(); } - if (decreaseRefCount(fiber) == 0) { - recycle(fiber); - } + recycle(fiber); } } diff --git a/emper/strategies/laws/LawsDispatcher.hpp b/emper/strategies/laws/LawsDispatcher.hpp index 32b649955508c65e81f39d0604301e02dcf965a1..fec816a19903205450c0060f3d37987c97d59bc4 100644 --- a/emper/strategies/laws/LawsDispatcher.hpp +++ b/emper/strategies/laws/LawsDispatcher.hpp @@ -5,6 +5,7 @@ #include "Dispatcher.hpp" #include "emper-common.h" +class Fiber; class LawsStrategy; class Runtime; @@ -19,6 +20,8 @@ class LawsDispatcher : public Dispatcher { ; #pragma GCC diagnostic pop + void recycle(Fiber* fiber) override; + public: LawsDispatcher(Runtime& runtime, LawsStrategy& lawsStrategy) : Dispatcher(runtime), lawsStrategy(lawsStrategy) {} diff --git a/emper/strategies/ws/WsDispatcher.cpp b/emper/strategies/ws/WsDispatcher.cpp index 9e3394acd2c5ab66bd3bb8511bde2a16989d9fdc..74182792846b29c49b66c5f1ba544db760c4cbac 100644 --- a/emper/strategies/ws/WsDispatcher.cpp +++ b/emper/strategies/ws/WsDispatcher.cpp @@ -8,7 +8,7 @@ class Fiber; void WsDispatcher::dispatchLoop() { while (true) { - const Fiber* fiber = runtime.nextFiber(); + Fiber* fiber = runtime.nextFiber(); if (!fiber) { dispatchLoopDoSleep();