Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • flow/emper
  • aj46ezos/emper
  • i4/manycore/emper
3 results
Show changes
Showing
with 1495 additions and 145 deletions
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Schmaus
// Copyright © 2020-2022 Florian Schmaus
#pragma once
#include <cstdint>
#include "Common.hpp" // for func_t
#include "Debug.hpp" // for LogSubsystem, LogSubsystem::CM, Logger
#include "MemoryManager.hpp" // for MemoryManager
......@@ -24,11 +26,17 @@ class ContextManager
auto getFreeContext() -> Context*;
static void putFreeContext(Context* context);
void putFreeContext(Context* context);
void start();
[[noreturn]] void start();
[[noreturn]] static void switchToOriginalStack();
void saveAndStartNew(func_t freshContextHook);
[[noreturn]] void discardAndResume(Context* context);
// TODO: Adding [[noreturn]] leads to SEGFAULTs in fibril_join, because
// compiler thinks join never returns! Try to fix and add [[noreturn]].
static void tryResumeFibril(uintptr_t fibrilResumeValue);
};
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2022 Florian Schmaus
#include "Continuation.hpp"
void Continuation::printTo(std::ostream& strm) const {
// clang-format off
strm << "Continuation ["
<< "bp=" << bp
<< ", sp=" << sp
<< ", ip=" << ip
<< "]";
// clang-format on
}
auto operator<<(std::ostream& strm, const Continuation& continuation) -> std::ostream& {
continuation.printTo(strm);
return strm;
}
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020-2024 Nicolas Pfeiffer, Florian Schmaus
#pragma once
#include <cstdint>
#include <ostream>
#define membar(call) \
do { \
call; \
asm("nop" ::: "rbx", "r12", "r13", "r14", "r15", "memory"); \
} while (0);
class Continuation {
public:
void *bp;
void *sp;
void *ip;
inline __attribute__((always_inline)) Continuation() : ip(nullptr) {
asm volatile(
"mov %%rbp, %0;"
"mov %%rsp, %1;"
: "=rm"(bp), "=rm"(sp)
:
:);
};
inline __attribute__((always_inline, noreturn)) void execute(const void *_sp) {
asm("mov %0, %%rsp;"
"mov %1, %%rbp;"
"jmp *%2;"
:
: "r"(_sp), "r"(bp), "r"(ip)
: "memory");
__builtin_unreachable();
};
inline __attribute__((always_inline, returns_twice)) uintptr_t setJmp() {
auto set_rip = [](Continuation *c) __attribute__((noipa, noinline, hot, optimize(3))) {
c->ip = __builtin_return_address(0);
return 0;
};
uintptr_t res;
res = set_rip(this);
// Same clobber list as membar() but with rax added, which will be
// used to hold 'res' once we longjmp to this.
asm("nop" ::: "rax", "rbx", "r12", "r13", "r14", "r15", "memory");
return res;
};
inline __attribute__((always_inline, noreturn)) void longJmp(uintptr_t ret) {
asm("mov %0, %%rsp;"
"mov %1, %%rbp;"
"jmp *%2;"
:
: "r"(sp), "r"(bp), "r"(ip), "a"(ret)
: "memory");
__builtin_unreachable();
};
void printTo(std::ostream &strm) const;
friend auto operator<<(std::ostream &strm, const Continuation &continuation) -> std::ostream &;
};
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Schmaus
// Copyright © 2020-2022 Florian Schmaus
#include "CountingPrivateSemaphore.hpp"
#include <cassert> // for assert
#include <ostream>
#include "Context.hpp" // for Context
#include "Debug.hpp"
CountingPrivateSemaphore::CountingPrivateSemaphore() : CountingPrivateSemaphore(0) {}
......@@ -46,7 +48,7 @@ auto CountingPrivateSemaphore::signalInternal() -> Context* {
// returns nullptr. In this case the block() function will
// have won the race.
Context* context = blockedContext.exchange(nullptr);
assert(context > (Context*)4096);
EMPER_ASSERT_MSG(!context || context > (Context*)4096, "Unexpected context value: " << context);
return context;
}
......
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Schmaus
// Copyright © 2020-2022 Florian Schmaus, Florian Fischer
#pragma once
#include <iostream> // IWYU pragma: keep
#include <sstream> // IWYU pragma: keep
#include <string> // for string, operator<<
#include <atomic>
#include <cstddef>
#include <sstream> // IWYU pragma: keep
#include <string>
#include <string_view>
#include "emper-config.h" // IWYU pragma: keep
#ifdef EMPER_LOG_OFF
#define DBG(x)
#define WDBG(x)
#define LOGD(x)
#define LOGDD(x)
#else
#include "Emper.hpp"
#include "emper-config.h"
#include "log/log.hpp"
// If we apply clang-format to the following region, then clang-format
// will create multi-line macros. However clang-tidy's NOLINTNEXTLINE
......@@ -23,51 +18,82 @@
// clang-format off
// Hide the stringstream inside a lamba from the context where our LOG macros are used.
// The lamda should be fairly cheap/free because it will be inlined.
// The use of a lambda here is the ISO C++ equivalent to GCC statement expressions.
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define EMPER_BUILD_STR(x) [&]() -> std::string {std::stringstream sst; sst << x; return sst.str(); }()
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define DBG(x) do { std::stringstream sst; sst << x << std::endl; emper_log("", sst.str()); } while (false)
#define LOG(level, x, log_func) do { if constexpr (level <= EMPER_LOG_LEVEL) { log_func(EMPER_BUILD_STR(x)); } } while (false)
#define EMPER_ASSERT_MSG(expr, msg) do { if constexpr (emper::DEBUG) { if (!(expr)) { DIE_MSG(msg); } } } while (false)
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define WDBG(x) do { std::stringstream sst; sst << x; emper_log("", sst.str()); } while (false)
#define DBG(x) LOG(Debug, x, emper::log::log_no_prefix);
// To avoid "error: there are no arguments to ‘logD’ that depend on a
// template parameter, so a declaration of ‘logD’ must be available"
// we use "this->logD()" instead of simply "logD()" below.
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGD(x) do { std::stringstream sst; sst << x; this->logD(sst.str()); } while (false)
#define LOGD(x) LOG(Debug, x, this->logD);
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGDD(x) do { std::stringstream sst; sst << x; this->logDD(sst.str()); } while (false)
#define LOGDD(x) LOG(FineDebug, x, this->logDD);
#endif
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGI(x) LOG(Info, "Info: " << x, emper::log::log_no_prefix);
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGGER_LOGI(x) LOG(Info, "Info: " << x, this->logI);
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGI(x) do { std::cerr << "Info: " << x << std::endl; } while (false)
#define LOGW(x) LOG(Warning, "Warning: " << x, emper::log::log_no_prefix);
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGW(x) do { std::cerr << "Warning: " << x << std::endl; } while (false)
#define LOGGER_LOGW(x) LOG(Warning, "Warning: " << x, this->logW);
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGE(x) LOG(Error, "Error: " << x, emper::log::log_no_prefix);
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define LOGE(x) do {std::cerr << "Error: " << x << std::endl; } while (false)
#define LOGGER_LOGE(x) LOG(Error, "Error: " << x, emper::log::log_no_prefix);
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define ABORT(x) do { std::stringstream sst; sst << x; logI(sst.str()); abort(); } while (false)
#define ABORT(x) { LOGE(x); abort(); }
// clang-format on
#define PUSH_DIAGNOSTIC _Pragma("GCC diagnostic push")
#define IGNORE_UNUSED_FUNCTION \
_Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
PUSH_DIAGNOSTIC _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
// test for GCC > 11.1.0
#if __GNUC__ > 11 || \
(__GNUC__ == 11 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ >= 0)))
#define IGNORE_MAYBE_UNINITIALIZED \
PUSH_DIAGNOSTIC _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
#else
#define IGNORE_MAYBE_UNINITIALIZED PUSH_DIAGNOSTIC
#endif
#define IGNORE_UNUSED_RESULT PUSH_DIAGNOSTIC _Pragma("GCC diagnostic ignored \"-Wunused-result\"")
#define POP_DIAGNOSTIC _Pragma("GCC diagnostic pop")
enum class LogSubsystem {
PS,
F,
FIBRIL,
C,
CM,
DISP,
SCHED,
RUNTI,
SLEEP_S,
WAKE_S,
U_B_MPSC_Q,
IO,
};
enum LogLevel {
OFF,
Error,
Warning,
Info,
......@@ -78,15 +104,6 @@ enum LogLevel {
ALL,
};
// global log level
namespace emper {
extern enum LogLevel log_level;
}
void emper_add_timestamp_to(std::ostringstream& logMessage);
void emper_log(const std::string& prefix, const std::string& message);
template <LogSubsystem logSubsystem>
class Logger {
private:
......@@ -99,6 +116,8 @@ class Logger {
case LogSubsystem::DISP:
case LogSubsystem::SCHED:
case LogSubsystem::RUNTI:
case LogSubsystem::SLEEP_S:
case LogSubsystem::WAKE_S:
case LogSubsystem::U_B_MPSC_Q:
case LogSubsystem::IO:
default:
......@@ -106,12 +125,14 @@ class Logger {
}
}
static constexpr auto getTagFor(LogSubsystem system) -> char const* {
static constexpr auto getTagFor(LogSubsystem system) -> std::string_view {
switch (system) {
case LogSubsystem::PS:
return "PS ";
case LogSubsystem::F:
return "F ";
case LogSubsystem::FIBRIL:
return "FIBRI";
case LogSubsystem::C:
return "C ";
case LogSubsystem::CM:
......@@ -122,6 +143,10 @@ class Logger {
return "SCHED";
case LogSubsystem::RUNTI:
return "RUNTI";
case LogSubsystem::SLEEP_S:
return "SLEEP";
case LogSubsystem::WAKE_S:
return "WAKE ";
case LogSubsystem::U_B_MPSC_Q:
return "UBSCQ";
case LogSubsystem::IO:
......@@ -142,31 +167,29 @@ class Logger {
}
protected:
inline void log(LogLevel level, const std::string& string) const {
#ifdef EMPER_LOG_OFF
return;
#endif
template <enum LogLevel level>
inline void log(const std::string& string) const {
// check global level
if (level > emper::log_level) return;
if constexpr (level > EMPER_LOG_LEVEL) return;
// check subsystem level
if (level > getLevelFor(logSubsystem)) return;
if constexpr (level > getLevelFor(logSubsystem)) return;
std::string subSystemTag = getTagFor(logSubsystem);
constexpr std::string_view subSystemTag = getTagFor(logSubsystem);
std::ostringstream sst;
sst << subSystemTag;
if (shouldPrefixThis(logSubsystem)) {
if constexpr (shouldPrefixThis(logSubsystem)) {
sst << " " << this;
}
emper_log(sst.str(), string);
emper::log::log(sst.str(), string);
}
inline void logE(const std::string& string) const { log(Error, string); }
inline void logE(const std::string& string) const { log<Error>(string); }
inline void logI(const std::string& string) const { log(Info, string); }
inline void logI(const std::string& string) const { log<Info>(string); }
inline void logD(const std::string& string) const { log(Debug, string); }
inline void logD(const std::string& string) const { log<Debug>(string); }
inline void logDD(const std::string& string) const { log(FineDebug, string); }
inline void logDD(const std::string& string) const { log<FineDebug>(string); }
};
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Schmaus
// Copyright © 2020-2022 Florian Schmaus
#include "Dispatcher.hpp"
#include <pthread.h> // for pthread_yield
#include <sched.h>
#include <atomic>
#include <chrono>
#include "Emper.hpp"
#include "Runtime.hpp" // for Runtime
#include "WakeupStrategy.hpp"
#include "stats/Worker.hpp"
// Note that we initialize this with true to avoid counting workers
// that initially go to sleep as being woken up in vain.
thread_local bool Dispatcher::dispatchedWork = true;
auto Dispatcher::getDispatchLoop() -> func_t {
return [this] { dispatchLoop(); };
}
void Dispatcher::putRuntimeWorkerToSleep() { runtime.dispatchLoopSleep(); }
void Dispatcher::dispatchLoopDoSleep() {
runtime.maybeTerminateWorker();
if constexpr (emper::WORKER_SLEEP) {
putRuntimeWorkerToSleep();
dispatchLoopSleep();
} else {
pthread_yield();
sched_yield();
}
}
void Dispatcher::dispatchLoopSleep() {
auto& wakeupStrategy = runtime.wakeupStrategy;
auto& workerSleepStrategy = runtime.workerSleepStrategy;
auto& terminateWorkers = runtime.terminateWorkers;
using clock = std::chrono::system_clock;
std::chrono::time_point<clock> sleepStart;
bool workerSlept = false;
bool canWake;
do {
// Notify the wakeup strategy about our sleep attempt
// TODO: We could take stats how often the wakeupStrategy decided
// that the worker should not sleep, and how often this was the
// right decission (based on dispatchedWork and a new thread_local sleepDenied).
if (!wakeupStrategy.canSleep()) break;
if constexpr (emper::STATS_WORKER_SLEEP) {
if (!workerSlept) {
if (!dispatchedWork) emper::stats::worker->wakeupInVainCount++;
workerSlept = true;
sleepStart = clock::now();
}
}
workerSleepStrategy.sleep();
// We always wakeup if the runtime is terminating
canWake = wakeupStrategy.canWakeup() || terminateWorkers.load(std::memory_order_relaxed);
} while (!canWake);
if constexpr (emper::STATS_WORKER_SLEEP) {
if (workerSlept) {
auto sleepStop = clock::now();
emper::stats::worker->recordSleep(sleepStart, sleepStop);
// TODO: Not sure if we should set dispatchedWork to false regardless of the value of
// firstSleep
dispatchedWork = false;
}
}
}
// 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 <ostream>
#include "AbstractFiber.hpp"
#include "Common.hpp"
#include "Context.hpp"
#include "Debug.hpp"
#include "Emper.hpp"
#include "Fiber.hpp"
#include "emper-common.h"
class Runtime;
class ContextManager;
class Dispatcher : public Logger<LogSubsystem::DISP> {
private:
void dispatchLoopSleep();
/**
* Marker bool if the worker dispatched work, used for "worker sleep" stats.
*/
static thread_local bool dispatchedWork;
protected:
Runtime& runtime;
......@@ -24,15 +36,28 @@ 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) {
LOGD("executing fiber " << fiber);
inline void dispatch(AbstractFiber* fiber) {
LOGD("executing " <<= *fiber);
if constexpr (emper::STATS_WORKER_SLEEP) {
dispatchedWork = true;
}
Context::setCurrentFiber(fiber);
fiber->run();
}
static inline auto isRunnable(Fiber* fiber) -> bool { return fiber->setRunnableFalse(); }
static inline auto isRunnable(AbstractFiber* abstractFiber) -> bool {
auto* fiber = abstractFiber->asFiberIfPossible();
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();
}
......@@ -40,21 +65,37 @@ class Dispatcher : public Logger<LogSubsystem::DISP> {
return fiber->doAtomicDecrRefCount();
}
virtual void recycle(Fiber* fiber) { delete fiber; }
void recycle(AbstractFiber* abstractFiber) {
auto* fiber = abstractFiber->asFiberIfPossible();
// We only recycle Fibers.
if (!fiber) {
LOGD("not recyling non-fiber " << fiber)
return;
}
recycle(fiber);
}
void putRuntimeWorkerToSleep();
virtual void recycle(Fiber* fiber) {
LOGD("recyling fiber " << fiber)
delete fiber;
}
public:
Dispatcher(Runtime& runtime) : runtime(runtime) {}
static auto getCurrentFiber() -> Fiber& {
Fiber* fiber = getCurrentFiberPtr();
virtual ~Dispatcher() = default;
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; }
friend ContextManager;
friend class Scheduler;
};
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Schmaus
// Copyright © 2020-2022 Florian Schmaus
#include "Emper.hpp"
#include <asm/posix_types.h>
#include <cassert>
#include <cerrno>
#include <cstdint>
#include <ctime>
#include <stdexcept>
#include <utility>
#include "Common.hpp"
#include "Context.hpp"
#include "Fiber.hpp"
#include "Runtime.hpp"
#include "emper-common.h"
#include "emper-version.h"
#include "emper.hpp"
#include "io/Future.hpp"
#include "lib/LinuxVersion.hpp"
#include "lib/util.hpp"
namespace eu = emper::lib::util;
void async(Fiber* fiber) {
assert(fiber != nullptr);
Runtime* runtime = Runtime::getRuntime();
runtime->schedule(*fiber);
}
void async(Fiber::fiber_fun_t function, void* arg) {
Fiber* fiber = Fiber::from(std::move(function), arg);
async(fiber);
}
void async(const Fiber::fiber_fun0_t& function) {
Fiber* fiber = Fiber::from(function);
async(fiber);
}
void async(Fiber::fiber_fun_t function, void* arg, workeraffinity_t* affinity) {
Fiber* fiber = Fiber::from(std::move(function), arg, affinity);
async(fiber);
}
void async(const Fiber::fiber_fun0_t& function, workeraffinity_t* affinity) {
Fiber* fiber = Fiber::from(function, affinity);
async(fiber);
}
namespace emper {
auto operator<<(std::ostream& out, const ContinuationStealingMode& cont_stealing_mode)
-> std::ostream& {
switch (cont_stealing_mode) {
case ContinuationStealingMode::disabled:
out << "disabled";
break;
case ContinuationStealingMode::locked:
out << "locked";
break;
case ContinuationStealingMode::waitfree:
out << "waitfree";
break;
}
return out;
}
auto operator>>(std::istream& in, ContinuationStealingMode& cont_stealing_mode) -> std::istream& {
std::string token;
in >> token;
if (token == "disabled") {
cont_stealing_mode = ContinuationStealingMode::disabled;
} else if (token == "locked") {
cont_stealing_mode = ContinuationStealingMode::locked;
} else if (token == "waitfree") {
cont_stealing_mode = ContinuationStealingMode::waitfree;
} else {
in.setstate(std::ios_base::failbit);
}
return in;
}
auto operator<<(std::ostream& out,
const ContinuationStealingMadviseStack& cont_stealing_madvise_stack)
-> std::ostream& {
switch (cont_stealing_madvise_stack) {
case ContinuationStealingMadviseStack::no:
out << "no";
break;
case ContinuationStealingMadviseStack::dontneed:
out << "dontneed";
break;
case ContinuationStealingMadviseStack::free:
out << "free";
break;
}
return out;
}
auto operator>>(std::istream& in, ContinuationStealingMadviseStack& cont_stealing_madvise_stack)
-> std::istream& {
std::string token;
in >> token;
if (token == "no") {
cont_stealing_madvise_stack = ContinuationStealingMadviseStack::no;
} else if (token == "dontneed") {
cont_stealing_madvise_stack = ContinuationStealingMadviseStack::dontneed;
} else if (token == "free") {
cont_stealing_madvise_stack = ContinuationStealingMadviseStack::free;
} else {
in.setstate(std::ios_base::failbit);
}
return in;
}
void printInfo(std::ostream& strm) {
// clang-format off
strm << std::boolalpha << "Efficient Massive-Parallelism Execution Realm (EMPER)" << std::endl
<< "Version: " << getFullVersion() << std::endl
<< "Debug: " << DEBUG << std::endl
<< "IO: " << IO << std::endl
<< "IO Stealing: " << IO_STEALING << std::endl
<< "IO Lockless CQ: " << IO_LOCKLESS_CQ << std::endl
<< "Set Affinity on Block: " << SET_AFFINITY_ON_BLOCK << std::endl
<< "Work-Stealing Queue Default: " << TOSTRING(EMPER_WS_QUEUE_DEFAULT_TYPE) << std::endl
<< "Work-Stealing Queue Scheduler: " << TOSTRING(EMPER_WS_QUEUE_SCHEDULER_TYPE) << std::endl
<< "Continuation-Stealing: " << CONTINUATION_STEALING_MODE << std::endl
<< "Continuation-Stealing Madvise Stack: " << CONTINUATION_STEALING_MADVISE_STACK << std::endl
<< "Context Size: " << eu::bytesToHumanReadableString(Context::CONTEXT_SIZE) << std::endl
<< "Context Manager with Memory Manager: " << CONTEXT_MANAGER_WITH_MEMORY_MANAGER << std::endl
<< "Memory Manager Victim Percentage: " << MEMORY_MANAGER_VICTIM_PERCENTAGE << std::endl
<< "Stack Guard Page: " << STACK_GUARD_PAGE << std::endl
<< "Assume Cache Line Size: " << ASSUME_CACHE_LINE_SIZE << std::endl
<< "Stats: " << STATS << std::endl
<< "Worker Sleep: " << WORKER_SLEEP << std::endl
<< "Overflow Queue: " << OVERFLOW_QUEUE << std::endl
;
// clang-format on
}
const bool IO_MUST_INVALIDATE_BROKEN_CHAIN = EMPER_LINUX_LT("5.15.0");
const bool IO_FORGOTTEN_FUTURES_SKIP_CQE = EMPER_LINUX_GE("5.17");
auto getFullVersion() -> std::string { return EMPER_FULL_VERSION; }
static void ensure_no_current_runtime() {
Runtime* runtime = Runtime::getRuntime();
if (runtime) {
throw std::runtime_error("Runtime already initialized");
}
}
void init_runtime() {
ensure_no_current_runtime();
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
new Runtime();
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
}
void init_runtime(workerid_t worker_count) {
ensure_no_current_runtime();
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
new Runtime(worker_count);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
}
void destroy_runtime() {
Runtime* runtime = Runtime::getRuntime();
if (!runtime) {
throw std::runtime_error("No runtime to destroy");
}
delete runtime;
}
void yield() {
Runtime* runtime = Runtime::getRuntime();
runtime->yield();
}
static auto _sleep(emper::io::AlarmFuture::Timespec& ts) -> bool {
if constexpr (!emper::IO) {
DIE_MSG("sleep requires emper::io");
}
emper::io::AlarmFuture alarm(ts);
int32_t res = alarm.submitAndWait();
return res == -ETIME;
}
auto sleep(unsigned int seconds) -> bool {
emper::io::AlarmFuture::Timespec ts = {.tv_sec = seconds, .tv_nsec = 0};
return _sleep(ts);
}
auto nanosleep(const struct timespec* rqtp) -> bool {
emper::io::AlarmFuture::Timespec ts = {.tv_sec = rqtp->tv_sec, .tv_nsec = rqtp->tv_nsec};
return _sleep(ts);
}
auto nanosleep(std::uint64_t ns) -> bool {
auto tv_sec = static_cast<__kernel_time64_t>(ns / 1'000'000'0000);
auto tv_nsec = static_cast<long long>(ns % 1'000'000'0000);
emper::io::AlarmFuture::Timespec ts = {.tv_sec = tv_sec, .tv_nsec = tv_nsec};
return _sleep(ts);
}
auto emper::getFullVersion() -> std::string { return EMPER_FULL_VERSION; }
} // namespace emper
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.