Skip to content

Commit

Permalink
Prototype for shared timer context
Browse files Browse the repository at this point in the history
Signed-off-by: AssemblyJohn <[email protected]>
  • Loading branch information
AssemblyJohn committed Oct 21, 2024
1 parent a48407d commit 9f60dbf
Showing 1 changed file with 65 additions and 18 deletions.
83 changes: 65 additions & 18 deletions include/everest/timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <stdexcept>
#include <thread>

namespace Everest {
Expand All @@ -30,6 +32,49 @@ template <template <typename> typename Guard, typename Mutex, bool Enabled> stru
OptionalTimerMember<Guard<Mutex>, Enabled> guard;
};

class TimerExecutionContext {
public:
boost::asio::io_context io_context;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work;
std::unique_ptr<std::thread> timer_thread = nullptr;
std::thread::id timer_thread_id;

public:
TimerExecutionContext() : work(boost::asio::make_work_guard(this->io_context)) {
this->timer_thread = std::make_unique<std::thread>([this]() {
timer_thread_id = std::this_thread::get_id();

Check warning on line 45 in include/everest/timer.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/everest/timer.hpp#L45

Variable 'timer_thread_id' is assigned in constructor body. Consider performing initialization in initialization list.
this->io_context.run();
});
}

~TimerExecutionContext() noexcept(false) {
if (std::this_thread::get_id() == timer_thread_id) {
throw std::runtime_error("Trying to destruct TimerExecContext from the same thread it was created!");

Check warning on line 52 in include/everest/timer.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/everest/timer.hpp#L52

Class TimerExecutionContext is not safe, destructor throws exception
}

this->io_context.stop();
this->timer_thread->join();
}

boost::asio::io_context& get_io_context() {
return this->io_context;
}

public:
static inline std::shared_ptr<TimerExecutionContext> get_unique_context() {
return std::make_shared<TimerExecutionContext>();
}

static std::shared_ptr<TimerExecutionContext> get_shared_context() {
static std::shared_ptr<TimerExecutionContext> context;
static std::once_flag context_flags;

std::call_once(context_flags, []() { context = std::make_shared<TimerExecutionContext>(); });

return context;
}
};

// template <typename TimerClock = date::steady_clock> class Timer {
template <typename TimerClock = date::utc_clock, bool ThreadSafe = false, bool SharedContext = false> class Timer {
private:
Expand All @@ -39,44 +84,46 @@ template <typename TimerClock = date::utc_clock, bool ThreadSafe = false, bool S
std::chrono::nanoseconds interval_nanoseconds;
bool running = false;

boost::asio::io_context io_context;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work;
std::unique_ptr<std::thread> timer_thread = nullptr;
std::shared_ptr<TimerExecutionContext> context;

OptionalTimerMember<std::mutex, ThreadSafe> mutex;

public:
/// This timer will initialize a boost::asio::io_context
explicit Timer() : work(boost::asio::make_work_guard(this->io_context)) {
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(this->io_context);
this->timer_thread = std::make_unique<std::thread>([this]() { this->io_context.run(); });
explicit Timer() {
if constexpr (SharedContext) {
context = TimerExecutionContext::get_shared_context();
} else {
context = TimerExecutionContext::get_unique_context();
}

this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(context->get_io_context());
}

explicit Timer(const std::function<void()>& callback) : work(boost::asio::make_work_guard(this->io_context)) {
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(this->io_context);
this->timer_thread = std::make_unique<std::thread>([this]() { this->io_context.run(); });
explicit Timer(const std::function<void()>& callback) {
if constexpr (SharedContext) {
context = TimerExecutionContext::get_shared_context();
} else {
context = TimerExecutionContext::get_unique_context();
}

this->callback = callback;
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(context->get_io_context());
}

explicit Timer(boost::asio::io_context* io_context) : work(boost::asio::make_work_guard(*io_context)) {
explicit Timer(boost::asio::io_context* io_context) {
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(*io_context);
}

explicit Timer(boost::asio::io_context* io_context, const std::function<void()>& callback) :
work(boost::asio::make_work_guard(*io_context)) {
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(*io_context);
explicit Timer(boost::asio::io_context* io_context, const std::function<void()>& callback) {
this->callback = callback;
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(*io_context);
}

~Timer() {
if (this->timer) {
// stop asio timer
this->timer->cancel();

if (this->timer_thread) {
this->io_context.stop();
this->timer_thread->join();
}
}
}

Expand Down

0 comments on commit 9f60dbf

Please sign in to comment.