diff --git a/emper/Runtime.cpp b/emper/Runtime.cpp index bb5e1ef835d075194ed832e2ef663bac30103b7e..c246bbc6e0fffd1bd43b7208b8180c249117aacd 100644 --- a/emper/Runtime.cpp +++ b/emper/Runtime.cpp @@ -72,7 +72,9 @@ RuntimeStrategyFactory& Runtime::DEFAULT_STRATEGY = using emper::io::GlobalIoContext; using emper::io::IoContext; -Runtime::Runtime(workerid_t workerCount, RuntimeStrategyFactory& strategyFactory, unsigned int seed) +Runtime::Runtime(workerid_t workerCount, const std::vector<NewWorkerHook>& newWorkerHooks, + bool pinWorkers, workerid_t pinningOffset, RuntimeStrategyFactory& strategyFactory, + unsigned int seed) : workerCount(workerCount), workerLatch(workerCount), firstWorkerThreadExitLatch(workerCount), @@ -116,6 +118,9 @@ Runtime::Runtime(workerid_t workerCount, RuntimeStrategyFactory& strategyFactory fromAnywhereStats = nullptr; } + // transfere newWorkerHooks + for (const auto& f : newWorkerHooks) this->newWorkerHooks.push_back(f); + // initialize the global IoContext if a completer is used if constexpr (emper::IO && emper::IO_COMPLETER_BEHAVIOR != emper::IoCompleterBehavior::none) { // The global io_uring needs at least workerCount entries in its SQ because @@ -130,21 +135,9 @@ Runtime::Runtime(workerid_t workerCount, RuntimeStrategyFactory& strategyFactory } } - bool pinWorkers = shouldPinWorkers(); - - // Core id we start the worker pinning - workerid_t pinningOffset = 0; - char* pinningOffsetEnv = std::getenv("EMPER_PINNING_OFFSET"); - if (pinningOffsetEnv) { - if (!pinWorkers) { - DIE_MSG("EMPER_PIN_WORKERS=false and EMPER_PINNING_OFFSET are mutual exclusive"); - } - - int pinningOffsetInt = std::stoi(pinningOffsetEnv); - if (pinningOffsetInt > UINT8_MAX) { - DIE_MSG("Pinning offset " << pinningOffsetInt << " to big for its datatype"); - } - pinningOffset = static_cast<workerid_t>(pinningOffsetInt); + // Check if the pinning settings are sound + if (pinningOffset && !pinWorkers) { + DIE_MSG("pinningOffset and not pinning workers are mutually exclusive"); } for (workerid_t i = 0; i < workerCount; ++i) { @@ -298,13 +291,9 @@ auto Runtime::workerLoop(Worker* worker) -> void* { } auto Runtime::getDefaultWorkerCount() -> workerid_t { - char* workerCountEnv = std::getenv("EMPER_WORKER_COUNT"); + auto workerCountEnv = emper::lib::env::getUnsignedFromEnv<workerid_t>("EMPER_WORKER_COUNT"); if (workerCountEnv) { - int workerCountInt = std::stoi(workerCountEnv); - if (workerCountInt > UINT8_MAX) { - DIE_MSG("Worker count " << workerCountInt << " to big for its datatype"); - } - return static_cast<workerid_t>(workerCountInt); + return workerCountEnv.value(); } // The CPU count reported by sysconf(_SC_NPROCESSORS_ONLN), sysconf(_SC_NPROCESSORS_CONF) diff --git a/emper/Runtime.hpp b/emper/Runtime.hpp index c3467559ac55ea41dcc3cfa3166c0b394a1078d3..14ae319cee699fb8e0b2d58260f8dc33a91df07c 100644 --- a/emper/Runtime.hpp +++ b/emper/Runtime.hpp @@ -31,6 +31,7 @@ #include "sleep_strategy/WorkerSleepStrategy.hpp" enum class LogSubsystem; +class RuntimeBuilder; class ContextManager; class Dispatcher; class Fiber; @@ -59,13 +60,16 @@ using emper::io::IoContext; using emper::sleep_strategy::WorkerSleepStrategy; class Runtime : public Logger<LogSubsystem::RUNTI> { + public: + using NewWorkerHook = std::function<void(workerid_t)>; + private: static std::mutex currentRuntimeMutex; static Runtime* currentRuntime; const workerid_t workerCount; - std::vector<std::function<void(workerid_t)>> newWorkerHooks; + std::vector<NewWorkerHook> newWorkerHooks; Latch workerLatch; Latch firstWorkerThreadExitLatch; @@ -110,6 +114,10 @@ class Runtime : public Logger<LogSubsystem::RUNTI> { return emper::lib::env::getBoolFromEnv("EMPER_PIN_WORKERS").value_or(true); } + static auto getDefaultPinningOffset() -> workerid_t { + return emper::lib::env::getUnsignedFromEnv<workerid_t>("EMPER_PINNING_OFFSET").value_or(0); + } + protected: void addNewWorkerHook(const std::function<void(workerid_t)>& hook) { newWorkerHooks.push_back(hook); @@ -157,7 +165,12 @@ class Runtime : public Logger<LogSubsystem::RUNTI> { : Runtime(getDefaultWorkerCount(), strategyFactory) {} Runtime(workerid_t workerCount, RuntimeStrategyFactory& strategyFactory, - unsigned int seed = std::random_device()()); + unsigned int seed = std::random_device()()) + : Runtime(workerCount, std::vector<NewWorkerHook>(), shouldPinWorkers(), + getDefaultPinningOffset(), strategyFactory, seed) {} + + Runtime(workerid_t workerCount, const std::vector<NewWorkerHook>& newWorkerHooks, bool pinWorkers, + workerid_t pinningOffset, RuntimeStrategyFactory& strategyFactory, unsigned int seed); ~Runtime(); @@ -216,6 +229,7 @@ class Runtime : public Logger<LogSubsystem::RUNTI> { friend class AbstractWorkStealingScheduler; template <LogSubsystem> friend class Blockable; + friend RuntimeBuilder; friend ContextManager; friend Scheduler; friend Dispatcher; diff --git a/emper/RuntimeBuilder.hpp b/emper/RuntimeBuilder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5e3e8b2e7c535f50140319b0e5ef24ce8a7c467a --- /dev/null +++ b/emper/RuntimeBuilder.hpp @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2021 Florian Fischer +#pragma once + +#include <optional> +#include <vector> + +#include "Runtime.hpp" +#include "emper-common.h" + +class RuntimeBuilder { + private: + workerid_t workerCount = 0; + + std::vector<Runtime::NewWorkerHook> newWorkerHooks; + + RuntimeStrategyFactory* strategyFactory = nullptr; + + std::optional<bool> pinWorkers = std::nullopt; + std::optional<workerid_t> pinningOffset = std::nullopt; + + std::optional<unsigned> seed = std::nullopt; + + public: + inline auto withWorkerCount(workerid_t workerCount) -> RuntimeBuilder& { + this->workerCount = workerCount; + return *this; + }; + + inline auto newWorkerHook(const Runtime::NewWorkerHook& hook) -> RuntimeBuilder& { + newWorkerHooks.push_back(hook); + return *this; + }; + + inline auto withPinWorkers(bool pinWorkers) -> RuntimeBuilder& { + this->pinWorkers = pinWorkers; + return *this; + }; + + inline auto withPinningOffset(workerid_t pinningOffset) -> RuntimeBuilder& { + this->pinningOffset = pinningOffset; + return *this; + }; + + inline auto withStrategyFactory(RuntimeStrategyFactory* factory) -> RuntimeBuilder& { + this->strategyFactory = factory; + return *this; + }; + + inline auto withSeed(unsigned seed) -> RuntimeBuilder& { + this->seed = seed; + return *this; + }; + + inline auto build() -> Runtime { + auto workerCount = + this->workerCount != 0 ? this->workerCount : Runtime::getDefaultWorkerCount(); + + auto* strategyFactory = + this->strategyFactory != nullptr ? this->strategyFactory : &Runtime::DEFAULT_STRATEGY; + + auto pinWorkers = this->pinWorkers.value_or(Runtime::shouldPinWorkers()); + + auto pinningOffset = this->pinningOffset.value_or(Runtime::getDefaultPinningOffset()); + + auto seed = this->seed.value_or(std::random_device()()); + + return Runtime(workerCount, newWorkerHooks, pinWorkers, pinningOffset, *strategyFactory, seed); + } +}; diff --git a/emper/lib/env.hpp b/emper/lib/env.hpp index 65b6c4fc2dcefe3421286f64ff27ea1114de4709..4d97aaecba060ff8227b19d3930a336a50495e02 100644 --- a/emper/lib/env.hpp +++ b/emper/lib/env.hpp @@ -2,6 +2,7 @@ // Copyright © 2021 Florian Fischer #pragma once +#include <cinttypes> #include <string> #include "Debug.hpp" @@ -27,4 +28,28 @@ static auto getBoolFromEnv(const std::string&& key) -> std::optional<bool> { DIE_MSG(key << " has invalid value: " << envStr << " (expected true or false)"); } +template <typename unsigned_type> +static auto getUnsignedFromEnv(const std::string&& key) -> std::optional<unsigned_type> { + DBG("parse " << key << " environment variable"); + char* envVar = std::getenv(key.c_str()); + if (!envVar) { + return std::nullopt; + } + + std::string envStr(envVar); + char* last; + + uintmax_t num = std::strtoumax(envStr.c_str(), &last, 10); + if (last != &envStr[0] + envStr.size()) { + DIE_MSG(key << " has invalid value: " << envStr << " (expected base-10 number)"); + } + + const unsigned_type t_max = std::numeric_limits<unsigned_type>::max(); + if (num > static_cast<uintmax_t>(t_max)) { + DIE_MSG(key << " is to big: " << envStr << " (type '" << typeid(t_max).name() + << "' max: " << t_max << ")"); + } + return static_cast<unsigned_type>(num); +} + } // namespace emper::lib::env