From 78fe80e7f1783f92bd724b4af0c82427a12c3fc0 Mon Sep 17 00:00:00 2001 From: iceseer Date: Tue, 18 Mar 2025 13:18:47 +0300 Subject: [PATCH 1/8] SE inited --- .gitignore | 1 + src/CMakeLists.txt | 5 + src/loaders/README | 1 + src/loaders/loader.hpp | 34 ++++ src/modules/CMakeLists.txt | 13 ++ src/modules/README | 1 + src/modules/module.hpp | 66 ++++++++ src/modules/module_loader.cpp | 80 ++++++++++ src/modules/module_loader.hpp | 56 +++++++ src/se/CMakeLists.txt | 21 +++ src/se/async_dispatcher.cpp | 20 +++ src/se/impl/async_dispatcher_impl.hpp | 160 +++++++++++++++++++ src/se/impl/common.hpp | 108 +++++++++++++ src/se/impl/compile-time_murmur2.hpp | 102 ++++++++++++ src/se/impl/dispatcher.hpp | 39 +++++ src/se/impl/scheduler.hpp | 42 +++++ src/se/impl/scheduler_impl.hpp | 185 ++++++++++++++++++++++ src/se/impl/subscriber.hpp | 43 +++++ src/se/impl/subscriber_impl.hpp | 196 +++++++++++++++++++++++ src/se/impl/subscription_engine.hpp | 219 ++++++++++++++++++++++++++ src/se/impl/subscription_manager.hpp | 172 ++++++++++++++++++++ src/se/impl/sync_dispatcher_impl.hpp | 59 +++++++ src/se/impl/thread_handler.hpp | 36 +++++ src/se/subscription.cpp | 30 ++++ src/se/subscription.hpp | 48 ++++++ src/se/subscription_fwd.hpp | 46 ++++++ src/se/sync_dispatcher.cpp | 20 +++ vcpkg.json | 3 +- 28 files changed, 1805 insertions(+), 1 deletion(-) create mode 100644 src/loaders/README create mode 100644 src/loaders/loader.hpp create mode 100644 src/modules/CMakeLists.txt create mode 100644 src/modules/README create mode 100644 src/modules/module.hpp create mode 100644 src/modules/module_loader.cpp create mode 100644 src/modules/module_loader.hpp create mode 100644 src/se/CMakeLists.txt create mode 100644 src/se/async_dispatcher.cpp create mode 100644 src/se/impl/async_dispatcher_impl.hpp create mode 100644 src/se/impl/common.hpp create mode 100644 src/se/impl/compile-time_murmur2.hpp create mode 100644 src/se/impl/dispatcher.hpp create mode 100644 src/se/impl/scheduler.hpp create mode 100644 src/se/impl/scheduler_impl.hpp create mode 100644 src/se/impl/subscriber.hpp create mode 100644 src/se/impl/subscriber_impl.hpp create mode 100644 src/se/impl/subscription_engine.hpp create mode 100644 src/se/impl/subscription_manager.hpp create mode 100644 src/se/impl/sync_dispatcher_impl.hpp create mode 100644 src/se/impl/thread_handler.hpp create mode 100644 src/se/subscription.cpp create mode 100644 src/se/subscription.hpp create mode 100644 src/se/subscription_fwd.hpp create mode 100644 src/se/sync_dispatcher.cpp diff --git a/.gitignore b/.gitignore index 4933a2d..70315af 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ .idea /build +/build-ex /cmake-build-* /.build diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 08f0356..87a038b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,3 +24,8 @@ add_subdirectory(metrics) # Clocks and time subsystem add_subdirectory(clock) +# Subscription Engine subsystem +add_subdirectory(se) + +# Modules subsystem +add_subdirectory(modules) diff --git a/src/loaders/README b/src/loaders/README new file mode 100644 index 0000000..31be259 --- /dev/null +++ b/src/loaders/README @@ -0,0 +1 @@ +# loaders are locating here \ No newline at end of file diff --git a/src/loaders/loader.hpp b/src/loaders/loader.hpp new file mode 100644 index 0000000..341d4ae --- /dev/null +++ b/src/loaders/loader.hpp @@ -0,0 +1,34 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "injector/node_injector.hpp" +#include "modules/module.hpp" + +namespace jam::loaders { + + class Loader { + public: + Loader(const Loader &) = delete; + Loader &operator=(const Loader &) = delete; + + virtual ~Loader() = default; + virtual void start() = 0; + + std::optional get_module_name_and_version() { + auto result = module_.getFunctionFromLibrary("get_module_name_and_version"); + if (result) { + return std::string((*result)()); + } + return std::nullopt; + } + + protected: + injector::NodeInjector injector_; + modules::Module module_; + }; +} // namespace jam::loaders diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt new file mode 100644 index 0000000..09cd83f --- /dev/null +++ b/src/modules/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright Quadrivium LLC +# All Rights Reserved +# SPDX-License-Identifier: Apache-2.0 +# + +add_library(modules + module_loader.cpp + ) + +target_link_libraries(modules + qtils::qtils + ) diff --git a/src/modules/README b/src/modules/README new file mode 100644 index 0000000..a7c5cfe --- /dev/null +++ b/src/modules/README @@ -0,0 +1 @@ +# modules are locating here \ No newline at end of file diff --git a/src/modules/module.hpp b/src/modules/module.hpp new file mode 100644 index 0000000..8cb095b --- /dev/null +++ b/src/modules/module.hpp @@ -0,0 +1,66 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace jam::modules { + + class Module final : public std::enable_shared_from_this { + public: + // Static method for Module object creation + static std::shared_ptr create( + const std::string &path, + std::unique_ptr handle, + const std::string &loader_id) { + return std::shared_ptr( + new Module(path, std::move(handle), loader_id)); + } + + // Getter for library path + const std::string &get_path() const { + return path_; + } + + // Getter for loader Id + const std::string &get_loader_id() const { + std::function p; + return loader_id_; + } + + // Get function address from library + template + std::optional getFunctionFromLibrary(const char *funcName) { + void *funcAddr = dlsym(handle_.get(), funcName); + if (!funcAddr) { + return std::nullopt; + } + return reinterpret_cast(funcAddr); + } + + private: + Module(const std::string &path, + std::unique_ptr handle, + const std::string &loader_id) + : path_(path), handle_(std::move(handle)), loader_id_(loader_id) {} + + std::string path_; // Library path + std::unique_ptr handle_; // Library handle + std::string loader_id_; // Loader ID + + Module(const Module &) = delete; + Module &operator=(const Module &) = delete; + Module(Module &&) = delete; + Module &operator=(Module &&) = delete; + }; + +} // namespace jam::modules diff --git a/src/modules/module_loader.cpp b/src/modules/module_loader.cpp new file mode 100644 index 0000000..582fbc3 --- /dev/null +++ b/src/modules/module_loader.cpp @@ -0,0 +1,80 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "modules/module_loader.hpp" + +#define COMPONENT_NAME "ModuleLoader" + +OUTCOME_CPP_DEFINE_CATEGORY(jam::modules, ModuleLoader::Error, e) { + using E = jam::modules::ModuleLoader::Error; + switch (e) { + case E::PathIsNotADir: + return COMPONENT_NAME ": path is not a directory"; + case E::OpenLibraryFailed: + return COMPONENT_NAME ": open library failed"; + case E::NoLoaderIdExport: + return COMPONENT_NAME ": library doesn't provide loader_id function"; + case E::UnexpectedLoaderId: + return COMPONENT_NAME ": unexpected loader id"; + } + return COMPONENT_NAME ": unknown error"; +} + +namespace jam::modules { + + Result ModuleLoader::recursive_search( + const fs::path &dir_path, std::deque> &modules) { + if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) { + return Error::PathIsNotADir; + } + + for (const auto &entry : fs::directory_iterator(dir_path)) { + const auto &entry_path = entry.path(); + const auto &entry_name = entry.path().filename().string(); + + if (entry_name[0] == '.' || entry_name[0] == '_') { + continue; + } + + if (fs::is_directory(entry)) { + OUTCOME_TRY(recursive_search(entry_path, modules)); + } else if (fs::is_regular_file(entry) + && entry_path.extension() == ".so") { + OUTCOME_TRY(load_module(entry_path.string(), modules)); + } + } + return outcome::success(); + } + + Result ModuleLoader::load_module( + const std::string &module_path, + std::deque> &modules) { + std::unique_ptr handle( + dlopen(module_path.c_str(), RTLD_LAZY), dlclose); + if (!handle) { + return Error::OpenLibraryFailed; + } + + typedef const char *(*LoaderIdFunc)(); + LoaderIdFunc loader_id_func = + (LoaderIdFunc)dlsym(handle.get(), "loader_id"); + + if (!loader_id_func) { + return Error::NoLoaderIdExport; + } + + const char *loader_id = loader_id_func(); + if (!loader_id) { + return Error::UnexpectedLoaderId; + } + + auto module = Module::create(module_path, std::move(handle), loader_id); + modules.push_back(module); + return outcome::success(); + } + + +} // namespace jam::modules diff --git a/src/modules/module_loader.hpp b/src/modules/module_loader.hpp new file mode 100644 index 0000000..daae0d8 --- /dev/null +++ b/src/modules/module_loader.hpp @@ -0,0 +1,56 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "modules/module.hpp" + +namespace fs = std::filesystem; +template +using Result = outcome::result; + +namespace jam::modules { + + class ModuleLoader { + public: + enum class Error : uint8_t { + PathIsNotADir, + OpenLibraryFailed, + NoLoaderIdExport, + UnexpectedLoaderId, + }; + + explicit ModuleLoader(const std::string &dir_path) : dir_path_(dir_path) {} + + Result>> get_modules() { + std::deque> modules; + OUTCOME_TRY(recursive_search(fs::path(dir_path_), modules)); + return modules; + } + + private: + std::string dir_path_; + + Result recursive_search(const fs::path &dir_path, + std::deque> &modules); + Result load_module(const std::string &module_path, + std::deque> &modules); + }; + +} // namespace jam::modules + +OUTCOME_HPP_DECLARE_ERROR(jam::modules, ModuleLoader::Error); diff --git a/src/se/CMakeLists.txt b/src/se/CMakeLists.txt new file mode 100644 index 0000000..fe524f6 --- /dev/null +++ b/src/se/CMakeLists.txt @@ -0,0 +1,21 @@ +# +# Copyright Quadrivium LLC +# All Rights Reserved +# SPDX-License-Identifier: Apache-2.0 +# + +add_library(se_async + async_dispatcher.cpp + subscription.cpp + ) + +target_link_libraries(se_async + ) + +add_library(se_sync + sync_dispatcher.cpp + subscription.cpp + ) + +target_link_libraries(se_sync + ) diff --git a/src/se/async_dispatcher.cpp b/src/se/async_dispatcher.cpp new file mode 100644 index 0000000..a742632 --- /dev/null +++ b/src/se/async_dispatcher.cpp @@ -0,0 +1,20 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "impl/async_dispatcher_impl.hpp" +#include "subscription.hpp" + +namespace jam::se { + + std::shared_ptr getDispatcher() { + return std::make_shared< + AsyncDispatcher>(); + } + +} // namespace jam::se diff --git a/src/se/impl/async_dispatcher_impl.hpp b/src/se/impl/async_dispatcher_impl.hpp new file mode 100644 index 0000000..8169cca --- /dev/null +++ b/src/se/impl/async_dispatcher_impl.hpp @@ -0,0 +1,160 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "common.hpp" +#include "dispatcher.hpp" +#include "thread_handler.hpp" + +namespace jam::se { + + template + class AsyncDispatcher final : public IDispatcher, + utils::NoCopy, + utils::NoMove { + public: + static constexpr uint32_t kHandlersCount = kCount; + static constexpr uint32_t kPoolThreadsCount = kPoolSize; + + private: + using Parent = IDispatcher; + + struct SchedulerContext { + /// Scheduler to execute tasks + std::shared_ptr handler; + }; + + SchedulerContext handlers_[kHandlersCount]; + SchedulerContext pool_[kPoolThreadsCount]; + + std::atomic_int64_t temporary_handlers_tasks_counter_; + std::atomic is_disposed_; + + struct BoundContexts { + typename Parent::Tid next_tid_offset = 0u; + std::unordered_map contexts; + }; + utils::ReadWriteObject bound_; + + void uploadToHandler(const typename Parent::Tid tid, + std::chrono::microseconds timeout, + typename Parent::Task &&task, + typename Parent::Predicate &&pred) { + assert(tid != kExecuteInPool || !pred); + if (is_disposed_.load()) { + return; + } + + if (tid < kHandlersCount) { + pred ? handlers_[tid].handler->repeat( + timeout, std::move(task), std::move(pred)) + : handlers_[tid].handler->addDelayed(timeout, std::move(task)); + return; + } + + if (auto context = + bound_.sharedAccess([tid](const BoundContexts &bound) + -> std::optional { + if (auto it = bound.contexts.find(tid); + it != bound.contexts.end()) { + return it->second; + } + return std::nullopt; + })) { + pred ? context->handler->repeat( + timeout, std::move(task), std::move(pred)) + : context->handler->addDelayed(timeout, std::move(task)); + return; + } + + std::optional opt_task = std::move(task); + for (auto &handler : pool_) { + if (opt_task = + handler.handler->uploadIfFree(timeout, std::move(*opt_task)); + !opt_task) { + return; + } + } + + auto h = std::make_shared(); + ++temporary_handlers_tasks_counter_; + h->addDelayed(timeout, [this, h, task{std::move(*opt_task)}]() mutable { + if (!is_disposed_.load()) { + task(); + } + --temporary_handlers_tasks_counter_; + h->dispose(false); + }); + } + + public: + AsyncDispatcher() { + temporary_handlers_tasks_counter_.store(0); + is_disposed_ = false; + for (auto &h : handlers_) { + h.handler = std::make_shared(); + } + for (auto &h : pool_) { + h.handler = std::make_shared(); + } + } + + void dispose() override { + is_disposed_ = true; + for (auto &h : handlers_) { + h.handler->dispose(); + } + for (auto &h : pool_) { + h.handler->dispose(); + } + + while (temporary_handlers_tasks_counter_.load() != 0) { + std::this_thread::sleep_for(std::chrono::microseconds(0ull)); + } + } + + void add(typename Parent::Tid tid, typename Parent::Task &&task) override { + uploadToHandler( + tid, std::chrono::microseconds(0ull), std::move(task), nullptr); + } + + void addDelayed(typename Parent::Tid tid, + std::chrono::microseconds timeout, + typename Parent::Task &&task) override { + uploadToHandler(tid, timeout, std::move(task), nullptr); + } + + void repeat(typename Parent::Tid tid, + std::chrono::microseconds timeout, + typename Parent::Task &&task, + typename Parent::Predicate &&pred) override { + uploadToHandler(tid, timeout, std::move(task), std::move(pred)); + } + + std::optional bind(std::shared_ptr scheduler) override { + if (!scheduler) { + return std::nullopt; + } + + return bound_.exclusiveAccess( + [scheduler(std::move(scheduler))](BoundContexts &bound) { + const auto execution_tid = kHandlersCount + bound.next_tid_offset; + assert(bound.contexts.find(execution_tid) == bound.contexts.end()); + bound.contexts[execution_tid] = SchedulerContext{scheduler}; + ++bound.next_tid_offset; + return execution_tid; + }); + } + + bool unbind(Tid tid) override { + return bound_.exclusiveAccess([tid](BoundContexts &bound) { + return bound.contexts.erase(tid) == 1; + }); + } + }; + +} // namespace jam::se diff --git a/src/se/impl/common.hpp b/src/se/impl/common.hpp new file mode 100644 index 0000000..6e53912 --- /dev/null +++ b/src/se/impl/common.hpp @@ -0,0 +1,108 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include + +namespace jam::se::utils { + + template + inline std::shared_ptr reinterpret_pointer_cast( + const std::shared_ptr &ptr) noexcept { + return std::shared_ptr(ptr, reinterpret_cast(ptr.get())); + } + + template + inline std::weak_ptr make_weak(const std::shared_ptr &ptr) noexcept { + return ptr; + } + + struct NoCopy { + NoCopy(const NoCopy &) = delete; + NoCopy &operator=(const NoCopy &) = delete; + NoCopy() = default; + }; + + struct NoMove { + NoMove(NoMove &&) = delete; + NoMove &operator=(NoMove &&) = delete; + NoMove() = default; + }; + + template + struct SafeObject { + using Type = T; + + template + SafeObject(Args &&...args) : t_(std::forward(args)...) {} + + template + inline auto exclusiveAccess(F &&f) { + std::unique_lock lock(cs_); + return std::forward(f)(t_); + } + + template + inline auto sharedAccess(F &&f) const { + std::shared_lock lock(cs_); + return std::forward(f)(t_); + } + + T &unsafeGet() { + return t_; + } + + const T &unsafeGet() const { + return t_; + } + + private: + T t_; + mutable M cs_; + }; + + template + using ReadWriteObject = SafeObject; + + class WaitForSingleObject final : NoMove, NoCopy { + std::condition_variable wait_cv_; + std::mutex wait_m_; + bool flag_; + + public: + WaitForSingleObject() : flag_{true} {} + + bool wait(std::chrono::microseconds wait_timeout) { + std::unique_lock _lock(wait_m_); + return wait_cv_.wait_for(_lock, wait_timeout, [&]() { + auto prev = !flag_; + flag_ = true; + return prev; + }); + } + + void wait() { + std::unique_lock _lock(wait_m_); + wait_cv_.wait(_lock, [&]() { + auto prev = !flag_; + flag_ = true; + return prev; + }); + } + + void set() { + { + std::unique_lock _lock(wait_m_); + flag_ = false; + } + wait_cv_.notify_one(); + } + }; +} // namespace jam::se::utils diff --git a/src/se/impl/compile-time_murmur2.hpp b/src/se/impl/compile-time_murmur2.hpp new file mode 100644 index 0000000..4aa1beb --- /dev/null +++ b/src/se/impl/compile-time_murmur2.hpp @@ -0,0 +1,102 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +namespace jam::se::utils { + + class Hasher { + static constexpr /* h */ uint32_t __init__(uint32_t len) { + return 0 ^ len; + } + + template + static constexpr uint32_t __load__(__T &data, uint32_t offset) { + return data[offset + 0] | (data[offset + 1] << 8) + | (data[offset + 2] << 16) | (data[offset + 3] << 24); + } + + static constexpr uint32_t __mul__(uint32_t val1, uint32_t val2) { + return val1 * val2; + } + + static constexpr uint32_t __sl__(uint32_t value, uint32_t count) { + return (value << count); + } + + static constexpr uint32_t __sr__(uint32_t value, uint32_t count) { + return (value >> count); + } + + static constexpr uint32_t __xor__(uint32_t h, uint32_t k) { + return h ^ k; + } + + static constexpr uint32_t __xor_with_sr__(uint32_t k, uint32_t r) { + return __xor__(k, __sr__(k, r)); + } + + template + static constexpr /* h */ uint32_t __proc__(__Type &data, + uint32_t len, + uint32_t offset, + uint32_t h, + uint32_t m, + uint32_t r) { + return len >= 4 + ? __proc__( + data, + len - 4, + offset + 4, + __xor__(__mul__(h, m), + __mul__(__xor_with_sr__( + __mul__(__load__(data, offset), m), r), + m)), + m, + r) + : len == 3 ? __proc__(data, + len - 1, + offset, + __xor__(h, __sl__(data[offset + 2], 16)), + m, + r) + : len == 2 ? __proc__(data, + len - 1, + offset, + __xor__(h, __sl__(data[offset + 1], 8)), + m, + r) + : len == 1 + ? __proc__( + data, len - 1, offset, __xor__(h, data[offset]) * m, m, r) + : __xor__(__mul__(__xor_with_sr__(h, 13), m), + __sr__(__mul__(__xor_with_sr__(h, 13), m), 15)); + } + + public: + template + static constexpr uint32_t murmur2(__Type &data, uint32_t len) { + return __proc__(data, len, 0, __init__(len), 0x5bd1e995, 24); + } + }; + +} // namespace jam::se::utils + +#ifndef CT_MURMUR2 +#define CT_MURMUR2(x) \ + ::jam::se::utils::Hasher::murmur2(x, (sizeof(x) / sizeof(x[0])) - 1) +#endif // CT_MURMUR2 + +static_assert(CT_MURMUR2("Called the One Ring, or the Ruling Ring.") + == 1333588607); +static_assert( + CT_MURMUR2("Fashioned by Sauron a decade after the making of the Elven " + "rings in the fires of Mount Doom in Mordor and which") + == 1319897327); +static_assert(CT_MURMUR2("could only be destroyed in that same fire.") + == 702138758); diff --git a/src/se/impl/dispatcher.hpp b/src/se/impl/dispatcher.hpp new file mode 100644 index 0000000..5dda15c --- /dev/null +++ b/src/se/impl/dispatcher.hpp @@ -0,0 +1,39 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "scheduler.hpp" + +namespace jam::se { + + struct IDispatcher { + using Tid = uint32_t; + using Task = IScheduler::Task; + using Predicate = IScheduler::Predicate; + + static constexpr Tid kExecuteInPool = std::numeric_limits::max(); + + virtual ~IDispatcher() = default; + + virtual std::optional bind(std::shared_ptr scheduler) = 0; + virtual bool unbind(Tid tid) = 0; + + virtual void dispose() = 0; + virtual void add(Tid tid, Task &&task) = 0; + virtual void addDelayed(Tid tid, + std::chrono::microseconds timeout, + Task &&task) = 0; + virtual void repeat(Tid tid, + std::chrono::microseconds timeout, + Task &&task, + Predicate &&pred) = 0; + }; + +} // namespace jam::se diff --git a/src/se/impl/scheduler.hpp b/src/se/impl/scheduler.hpp new file mode 100644 index 0000000..cdc141a --- /dev/null +++ b/src/se/impl/scheduler.hpp @@ -0,0 +1,42 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +namespace jam::se { + + class IScheduler { + public: + using Task = std::function; + using Predicate = std::function; + virtual ~IScheduler() {} + + /// Stops sheduler work and tasks execution + virtual void dispose(bool wait_for_release = true) = 0; + + /// Checks if current scheduler executes task + virtual bool isBusy() const = 0; + + /// If scheduller is not busy it takes task for execution. Otherwise it + /// returns it back. + virtual std::optional uploadIfFree(std::chrono::microseconds timeout, + Task &&task) = 0; + + /// Adds delayed task to execution queue + virtual void addDelayed(std::chrono::microseconds timeout, Task &&t) = 0; + + /// Adds task that will be periodicaly called with timeout period after + /// timeout, until predicate return true + virtual void repeat(std::chrono::microseconds timeout, + Task &&t, + Predicate &&pred) = 0; + }; + +} // namespace jam::se diff --git a/src/se/impl/scheduler_impl.hpp b/src/se/impl/scheduler_impl.hpp new file mode 100644 index 0000000..d2c9841 --- /dev/null +++ b/src/se/impl/scheduler_impl.hpp @@ -0,0 +1,185 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.hpp" +#include "scheduler.hpp" + +namespace jam::se { + + class SchedulerBase : public IScheduler, utils::NoCopy, utils::NoMove { + private: + using Time = std::chrono::high_resolution_clock; + using Timepoint = std::chrono::time_point