diff --git a/emper/AbstractFiber.cpp b/emper/AbstractFiber.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0db3ee122eb1805c2f1019cde22f6234f178f78c --- /dev/null +++ b/emper/AbstractFiber.cpp @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2022 Florian Schmaus +#include "AbstractFiber.hpp" + +auto operator<<(std::ostream& strm, const AbstractFiber& fiber) -> std::ostream& { + fiber.printTo(strm, false); + return strm; +} + +auto operator<<=(std::ostream& strm, const AbstractFiber& fiber) -> std::ostream& { + fiber.printTo(strm, true); + return strm; +} diff --git a/emper/AbstractFiber.hpp b/emper/AbstractFiber.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ce028f5b2d4b334bb24cd7479cdf40181dd11bdf --- /dev/null +++ b/emper/AbstractFiber.hpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2022 Florian Schmaus +#pragma once + +#include <ostream> + +#include "emper-common.h" + +class AbstractFiber { + friend class Dispatcher; + + protected: + virtual ~AbstractFiber() = default; + + virtual void run() const = 0; + + public: + virtual auto isRunnable() const -> bool { return true; } + + virtual auto getAffinityBuffer() const -> workeraffinity_t* { return nullptr; } + + virtual void printTo(std::ostream& strm, bool withPtr = true) const = 0; + + friend auto operator<<(std::ostream& strm, const AbstractFiber& fiber) -> std::ostream&; + friend auto operator<<=(std::ostream& strm, const AbstractFiber& fiber) -> std::ostream&; +}; diff --git a/emper/Context.hpp b/emper/Context.hpp index 2a2e77b0372781b65de2ef437b1fd4f31201a193..0ee84b91d41bab29c2eedd96ae36719ebd2861e8 100644 --- a/emper/Context.hpp +++ b/emper/Context.hpp @@ -14,9 +14,9 @@ #include "Debug.hpp" // for LOGD, LogSubsystem, LogSubsystem::C, Logger #include "Emper.hpp" // for Emper::DEBUG +class AbstractFiber; class ContextManager; class Dispatcher; -class Fiber; extern "C" [[noreturn]] void switch_and_load_context(void** toTos); // *Not* marked as 'noreturn' because save_and_switch_context does @@ -35,7 +35,7 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { static thread_local Context* lastContextBeforeReturningToOriginalStack; - Fiber* currentFiber = nullptr; + AbstractFiber* currentFiber = nullptr; void* const tos; @@ -60,7 +60,7 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { friend ContextManager; - auto getFiber() -> Fiber* { return currentFiber; } + auto getFiber() -> AbstractFiber* { return currentFiber; } /** * The first function that a newly started context will @@ -75,7 +75,7 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { friend Dispatcher; - static void setCurrentFiber(Fiber* fiber) { + static void setCurrentFiber(AbstractFiber* fiber) { assert(currentContext); currentContext->currentFiber = fiber; @@ -124,7 +124,7 @@ class ALIGN_TO_CACHE_LINE Context : Logger<LogSubsystem::C> { // VALGRIND_STACK_DEREGISTER(valgrindStackId); } - static auto getCurrentFiber() -> Fiber* { + static auto getCurrentFiber() -> AbstractFiber* { assert(currentContext); return currentContext->currentFiber; diff --git a/emper/ContextManager.cpp b/emper/ContextManager.cpp index a25687af03dad82f05a787277462ec4fd5a22e68..367e99de2e31a3a7c0140367c169fca47bdf1d14 100644 --- a/emper/ContextManager.cpp +++ b/emper/ContextManager.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2020-2021 Florian Schmaus +// Copyright © 2020-2022 Florian Schmaus #include "ContextManager.hpp" #include <cassert> // for assert @@ -12,7 +12,7 @@ #include "emper-common.h" #include "emper-config.h" // // IWYU pragma: keep -class Fiber; +class AbstractFiber; ContextManager::ContextManager(Runtime& runtime) : MemoryManager(runtime), runtime(runtime) { auto newWorkerHook = [this](ATTR_UNUSED workerid_t workerId) { @@ -87,7 +87,7 @@ void ContextManager::discardAndResume(Context* context) { // 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(); + AbstractFiber* currentFiber = contextToFree->getFiber(); runtime.dispatcher.recycle(currentFiber); contextToFree->discardAndResume(context); diff --git a/emper/Dispatcher.hpp b/emper/Dispatcher.hpp index 78d75fff7e166d1212e7b8cf95a748e66dad3ddb..a090eba80c8e4a9640fc3b15ebe14ef1013cca6c 100644 --- a/emper/Dispatcher.hpp +++ b/emper/Dispatcher.hpp @@ -1,12 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2020 Florian Schmaus +// Copyright © 2020-2022 Florian Schmaus #pragma once -#include "Common.hpp" // for func_t -#include "Context.hpp" // for Context -#include "Debug.hpp" // for LOGD, LogSubsystem, LogSubsystem::DISP -#include "Fiber.hpp" // for Fiber -#include "emper-common.h" // for workeraffinity_t +#include "AbstractFiber.hpp" +#include "Common.hpp" +#include "Context.hpp" +#include "Debug.hpp" +#include "Fiber.hpp" +#include "emper-common.h" class Runtime; class ContextManager; @@ -24,20 +25,23 @@ 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(Fiber* fiber) { + inline void dispatch(AbstractFiber* fiber) { LOGD("executing fiber " << fiber); Context::setCurrentFiber(fiber); fiber->run(); } - static inline auto isRunnable(Fiber* fiber) -> bool { + static inline auto isRunnable(AbstractFiber* abstractFiber) -> bool { + auto* fiber = dynamic_cast<Fiber*>(abstractFiber); + if (!fiber) return true; + if (fiber->isMultiFiber()) { return fiber->setRunnableFalse(); } return true; } - static inline auto getAffinityBuffer(Fiber* fiber) -> workeraffinity_t* { + static inline auto getAffinityBuffer(AbstractFiber* fiber) -> workeraffinity_t* { return fiber->getAffinityBuffer(); } @@ -45,6 +49,15 @@ class Dispatcher : public Logger<LogSubsystem::DISP> { return fiber->doAtomicDecrRefCount(); } + void recycle(AbstractFiber* abstractFiber) { + auto* fiber = dynamic_cast<Fiber*>(abstractFiber); + + // We only recycle Fibers. + if (!fiber) return; + + recycle(fiber); + } + virtual void recycle(Fiber* fiber) { delete fiber; } public: @@ -52,12 +65,12 @@ class Dispatcher : public Logger<LogSubsystem::DISP> { virtual ~Dispatcher() = default; - static auto getCurrentFiber() -> Fiber& { - Fiber* fiber = getCurrentFiberPtr(); + static auto getCurrentFiber() -> AbstractFiber& { + AbstractFiber* fiber = getCurrentFiberPtr(); return *fiber; } - static auto getCurrentFiberPtr() -> Fiber* { return Context::getCurrentFiber(); } + static auto getCurrentFiberPtr() -> AbstractFiber* { return Context::getCurrentFiber(); } static auto isDispatchedControlFlow() -> bool { return getCurrentFiberPtr() != nullptr; } diff --git a/emper/Fiber.cpp b/emper/Fiber.cpp index 14a6dcc0507d418858b183a5988113db24596594..b5d6a3a1fce9d19c4db013defb4d34c92f7a64c8 100644 --- a/emper/Fiber.cpp +++ b/emper/Fiber.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2020 Florian Schmaus +// Copyright © 2020-2022 Florian Schmaus #include "Fiber.hpp" #include <iostream> // for operator<<, basic_ostream, ostream, basic_ostrea... @@ -13,19 +13,22 @@ void Fiber::run() const { function(arg); } -auto operator<<(std::ostream& strm, const Fiber& fiber) -> std::ostream& { - strm << "Fiber [ptr=" << &fiber << " func=" << (fiber.function.target<void(void*)>() != nullptr) - << " arg=" << fiber.arg; +auto Fiber::isRunnable() const -> bool { return runnable.load(std::memory_order_relaxed); } - if (fiber.affinity) { - strm << " aff=" << fiber.affinity; +void Fiber::printTo(std::ostream &strm, bool withPtr) const { + strm << "Fiber ["; + if (withPtr) { + strm << "ptr=" << this << " "; + } + // clang-format: off + strm << "func=" << (function.target<void(void *)>() != nullptr) << "arg=" << arg; + // clang-format: on + + if (affinity) { + strm << " aff=" << affinity; } else { strm << " aff=nullptr"; } strm << "]"; - - return strm; } - -void Fiber::print() const { std::cout << this << std::endl; } diff --git a/emper/Fiber.hpp b/emper/Fiber.hpp index 8fd61a78399f4143b4b79f33ffbf17325f76b47b..9da6f9ab1da69d091ce6e41e088a9dc00620235d 100644 --- a/emper/Fiber.hpp +++ b/emper/Fiber.hpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2020-2021 Florian Schmaus +// Copyright © 2020-2022 Florian Schmaus #pragma once #include <atomic> // for atomic_uint, atomic, __atomic_base, memory... @@ -9,6 +9,7 @@ #include <type_traits> // for remove_reference<>::type // IWYU pragma: keep #include <utility> +#include "AbstractFiber.hpp" #include "Common.hpp" // for ALIGN_TO_CACHE_LINE #include "Debug.hpp" // for LogSubsystem, LogSubsystem::F, Logger #include "emper-common.h" // for workeraffinity_t, UNUSED_ARG @@ -25,7 +26,7 @@ class MpscQueue; #define FIBER_FUN_TEMPLATE_ARG void(void*) #define FIBER_FUN0_TEMPLATE_ARG void(void) -class ALIGN_TO_CACHE_LINE Fiber : public Logger<LogSubsystem::F> { +class ALIGN_TO_CACHE_LINE Fiber : public AbstractFiber, public Logger<LogSubsystem::F> { public: using fiber_fun_t = std::function<void(void*)>; using fiber_fun0_t = std::function<void()>; @@ -125,9 +126,19 @@ class ALIGN_TO_CACHE_LINE Fiber : public Logger<LogSubsystem::F> { return *affinity; } - void print() const; + void printTo(std::ostream& strm, bool withPtr) const; - [[nodiscard]] auto isRunnable() const -> bool { return runnable; } + /** + * @brief check if this Fiber is runnable. + * + * Checks if this Fiber can be run, i.e. if it still needs to be + * run. Note that with multi Fibers, a positive result, i.e., if + * this function returns 'true', may be outdated immediately due to + * concurrency. + * + * @return 'true' if this fiber is runnable, 'false' otherwhise. + */ + [[nodiscard]] auto isRunnable() const -> bool; [[nodiscard]] auto isMultiFiber() const -> bool { return isMulti; } diff --git a/emper/NextFiberResult.hpp b/emper/NextFiberResult.hpp index 9b927e14f95870acca971bdc3de0dcfc8350aa82..b3d021d32d621169e30d33d1520ab8bbfb8c0a19 100644 --- a/emper/NextFiberResult.hpp +++ b/emper/NextFiberResult.hpp @@ -1,14 +1,14 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2021 Florian Schmaus +// Copyright © 2021-2022 Florian Schmaus #pragma once #include <cstdint> #include "FiberSource.hpp" -class Fiber; +class AbstractFiber; struct NextFiberResult { - Fiber* const fiber; + AbstractFiber* fiber; const emper::FiberSource source; }; diff --git a/emper/Scheduler.hpp b/emper/Scheduler.hpp index 57204e267655036ededee7b963fdf78132df6589..665064d8a838ff0ec155f7340b083a6291f80051 100644 --- a/emper/Scheduler.hpp +++ b/emper/Scheduler.hpp @@ -16,6 +16,7 @@ #include "emper-common.h" // for workeraffinity_t #include "lib/adt/LockedUnboundedQueue.hpp" +class AbstractFiber; class Runtime; class RuntimeStrategy; struct NextFiberResult; @@ -74,7 +75,7 @@ class Scheduler : public Logger<LogSubsystem::SCHED> { virtual void scheduleFromAnywhereInternal(Fiber& fiber) = 0; virtual void scheduleFromAnywhereInternal(Fiber** fibers, unsigned count) = 0; - void recycle(Fiber* fiber) { dispatcher.recycle(fiber); }; + void recycle(AbstractFiber* fiber) { dispatcher.recycle(fiber); }; virtual auto nextFiber() -> std::optional<NextFiberResult> = 0; diff --git a/emper/meson.build b/emper/meson.build index 4423f03b37b1b5adfc76f57f0d7201fcdf27ade9..876f76f4cc649b7468a1901680068ecfccadb3a2 100644 --- a/emper/meson.build +++ b/emper/meson.build @@ -13,6 +13,7 @@ nasm_gen = generator(nasm, emper_asm_objects = nasm_gen.process(emper_asm_sources) emper_cpp_sources = [ + 'AbstractFiber.cpp', 'CallerEnvironment.cpp', 'Runtime.cpp', 'Emper.cpp', diff --git a/emper/strategies/AbstractWorkStealingScheduler.cpp b/emper/strategies/AbstractWorkStealingScheduler.cpp index 2814a4173a3c64aece657d2239d915094a8c9e91..47a89a7a78b6973250895fc550986dfdde044e68 100644 --- a/emper/strategies/AbstractWorkStealingScheduler.cpp +++ b/emper/strategies/AbstractWorkStealingScheduler.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2021 Florian Schmaus, Florian Fischer +// Copyright © 2021-2022 Florian Schmaus, Florian Fischer #include "AbstractWorkStealingScheduler.hpp" #include <algorithm> @@ -9,6 +9,7 @@ #include <ostream> // for operator<<, basic_ostream<>::__ostream_type #include <vector> +#include "AbstractFiber.hpp" #include "CallerEnvironment.hpp" #include "Common.hpp" // for unlikely, likely #include "Debug.hpp" // for ABORT @@ -78,7 +79,7 @@ void AbstractWorkStealingScheduler::scheduleToMpscQueue(Fiber& fiber, workerid_t onNewWork<CallerEnvironment::EMPER>(emper::FiberHint{workerId, emper::FiberSource::mpscQueue}); } -auto AbstractWorkStealingScheduler::maybeRecycle(Fiber* fiber) -> bool { +auto AbstractWorkStealingScheduler::maybeRecycle(AbstractFiber* fiber) -> bool { if (fiber->isRunnable()) return false; recycle(fiber); @@ -145,7 +146,7 @@ auto AbstractWorkStealingScheduler::nextFiberViaAnywhereQueue() -> std::optional auto AbstractWorkStealingScheduler::tryStealFiberFrom(workerid_t victim) -> std::optional<NextFiberResult> { constexpr int maxRetries = emper::WAITFREE_WORK_STEALING ? 0 : -1; - Fiber* fiber; + AbstractFiber* fiber; popTop: StealingResult res = queues[victim]->popTop<maxRetries>(&fiber); if (res == StealingResult::Stolen) { @@ -170,7 +171,7 @@ popTop: auto AbstractWorkStealingScheduler::nextFiberResultViaWorkStealing() -> std::optional<NextFiberResult> { - Fiber* fiber; + AbstractFiber* fiber; popBottom: bool poped = queue.popBottom(&fiber); diff --git a/emper/strategies/AbstractWorkStealingScheduler.hpp b/emper/strategies/AbstractWorkStealingScheduler.hpp index 7dd3198bf0b5b70b958fbe67ad3be26e39f41c0a..b452edeff6070d515215e4d4e4b9252c90c47137 100644 --- a/emper/strategies/AbstractWorkStealingScheduler.hpp +++ b/emper/strategies/AbstractWorkStealingScheduler.hpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2021 Florian Schmaus +// Copyright © 2021-2022 Florian Schmaus #pragma once #include <cstddef> // for size_t @@ -17,15 +17,16 @@ #endif struct NextFiberResult; +class AbstractFiber; class Runtime; class RuntimeStrategy; class AbstractWorkStealingScheduler : public Scheduler { template <size_t SIZE> #ifdef EMPER_LOCKED_WS_QUEUE - using WsQueue = adt::LockedQueue<Fiber*, SIZE>; + using WsQueue = adt::LockedQueue<AbstractFiber*, SIZE>; #else - using WsQueue = adt::WsClQueue<Fiber*, SIZE>; + using WsQueue = adt::WsClQueue<AbstractFiber*, SIZE>; #endif using MpscQueue = adt::MpscQueue<Fiber>; @@ -46,7 +47,7 @@ class AbstractWorkStealingScheduler : public Scheduler { void scheduleViaWorkStealing(Fiber& fiber); void scheduleToMpscQueue(Fiber& fiber, workerid_t workerId); - auto maybeRecycle(Fiber* fiber) -> bool; + auto maybeRecycle(AbstractFiber* fiber) -> bool; // This method is static because it only uses the thread_local mpscQueue static auto nextFiberResultFromMpscQueue() -> std::optional<NextFiberResult>; diff --git a/emper/strategies/laws/LawsDispatcher.cpp b/emper/strategies/laws/LawsDispatcher.cpp index 7980f7c732cec4f466648c0645dfcff62e7f423a..47dfc389d1be685bb10c8da030b042ba8289b6a5 100644 --- a/emper/strategies/laws/LawsDispatcher.cpp +++ b/emper/strategies/laws/LawsDispatcher.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2020-2021 Florian Schmaus +// Copyright © 2020-2022 Florian Schmaus #include "LawsDispatcher.hpp" #include <optional> @@ -14,6 +14,8 @@ #include "emper-common.h" #include "strategies/laws/LawsWorkerStats.hpp" +class AbstractFiber; + void LawsDispatcher::recycle(Fiber* fiber) { // If the ref count has not reached zero yet, do not recycle the // fiber. But only if the fiber is a multi fiber, i.e. was placed @@ -33,7 +35,7 @@ void LawsDispatcher::dispatchLoop() { continue; } - Fiber* const fiber = next->fiber; + AbstractFiber* fiber = next->fiber; // The isRunnable() method performes an atomic swap on a boolean, // which was initialized to true, in order to check if this fiber @@ -78,6 +80,6 @@ void LawsDispatcher::dispatchLoop() { dispatch(fiber); } - recycle(fiber); + Dispatcher::recycle(fiber); } } diff --git a/emper/strategies/ws/WsDispatcher.cpp b/emper/strategies/ws/WsDispatcher.cpp index 7826f33a82d0628091dbccaf38c6cca90bcb61f9..90a16f6f6d6b8229e64d821c1c570144c28ef4a4 100644 --- a/emper/strategies/ws/WsDispatcher.cpp +++ b/emper/strategies/ws/WsDispatcher.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright © 2020-2022 Florian Schmaus Florian Fischer +// Copyright © 2020-2022 Florian Schmaus, Florian Fischer #include "WsDispatcher.hpp" #include <optional> @@ -7,7 +7,7 @@ #include "NextFiberResult.hpp" #include "Runtime.hpp" // for Runtime -class Fiber; +class AbstractFiber; void WsDispatcher::dispatchLoop() { while (true) { @@ -17,7 +17,7 @@ void WsDispatcher::dispatchLoop() { continue; } - Fiber* const fiber = next->fiber; + AbstractFiber* fiber = next->fiber; dispatch(fiber);