Files
cariflex/tools/EVerest-main/lib/everest/timer/include/everest/timer.hpp
Eric F d398a6ced2 Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
2026-06-08 00:38:27 -04:00

245 lines
8.1 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
#ifndef EVEREST_TIMER_HPP
#define EVEREST_TIMER_HPP
#include <atomic>
#include <chrono>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <boost/asio.hpp>
#include <date/date.h>
#include <date/tz.h>
namespace Everest {
template <typename TimerClock = date::utc_clock> class Timer {
private:
std::unique_ptr<boost::asio::basic_waitable_timer<TimerClock>> timer = nullptr;
std::function<void()> timer_callback;
std::function<void(const boost::system::error_code& error)> callback_wrapper;
std::chrono::nanoseconds interval_nanoseconds = std::chrono::nanoseconds(0);
std::atomic<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::mutex mutex;
public:
/// This timer will initialize a boost::asio::io_context
Timer() :
work(boost::asio::make_work_guard(this->io_context)),
timer_thread(std::make_unique<std::thread>([this]() { this->io_context.run(); })) {
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(this->io_context);
}
explicit Timer(const std::function<void()>& callback) :
timer_callback(callback),
work(boost::asio::make_work_guard(this->io_context)),
timer_thread(std::make_unique<std::thread>([this]() { this->io_context.run(); })) {
this->timer = std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(this->io_context);
}
explicit Timer(boost::asio::io_context* io_context) :
timer(std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(*io_context)),
work(boost::asio::make_work_guard(*io_context)) {
}
Timer(boost::asio::io_context* io_context, const std::function<void()>& callback) :
timer(std::make_unique<boost::asio::basic_waitable_timer<TimerClock>>(*io_context)),
timer_callback(callback),
work(boost::asio::make_work_guard(*io_context)) {
}
/// \brief Cancel the asio timer and join the io_context thread.
///
/// The mutex is released BEFORE joining the io_context thread. A timer callback running on
/// that thread may re-enter \ref at, \ref timeout, \ref interval, or \ref stop — each of which
/// takes the mutex; if the destructor still held it, \c join would deadlock waiting for the
/// callback to exit while the callback waited for the mutex.
~Timer() {
{
std::lock_guard<std::mutex> lock(this->mutex);
if (this->timer) {
this->timer->cancel();
}
}
if (this->timer_thread) {
this->io_context.stop();
this->timer_thread->join();
}
}
/// Executes the given callback at the given timepoint
template <class Clock, class Duration = typename Clock::duration>
void at(const std::function<void()>& callback, const std::chrono::time_point<Clock, Duration>& time_point) {
std::lock_guard<std::mutex> lock(this->mutex);
this->stop_internal();
this->timer_callback = callback;
this->at_internal(time_point);
}
/// Executes a previously configured callback at the given timepoint
template <class Clock, class Duration = typename Clock::duration>
void at(const std::chrono::time_point<Clock, Duration>& time_point) {
std::lock_guard<std::mutex> lock(this->mutex);
this->stop_internal();
this->at_internal<Clock, Duration>(time_point);
}
/// Executes the given callback periodically from now in the given interval
template <class Rep, class Period>
void interval(const std::function<void()>& callback, const std::chrono::duration<Rep, Period>& interval) {
std::lock_guard<std::mutex> lock(this->mutex);
this->stop_internal();
this->timer_callback = callback;
this->interval_internal(interval);
}
/// Executes a previously configured callback periodically from now in the given interval
template <class Rep, class Period> void interval(const std::chrono::duration<Rep, Period>& interval) {
std::lock_guard<std::mutex> lock(this->mutex);
this->stop_internal();
this->interval_internal(interval);
}
/// Executes the given callback once after the given interval
template <class Rep, class Period>
void timeout(const std::function<void()>& callback, const std::chrono::duration<Rep, Period>& interval) {
std::lock_guard<std::mutex> lock(this->mutex);
this->stop_internal();
this->timer_callback = callback;
this->timeout_internal(interval);
}
/// Executes a previously configured callback once after the given interval
template <class Rep, class Period> void timeout(const std::chrono::duration<Rep, Period>& interval) {
std::lock_guard<std::mutex> lock(this->mutex);
this->stop_internal();
this->timeout_internal(interval);
}
/// Stops timer from executing its callback
void stop() {
std::lock_guard<std::mutex> lock(this->mutex);
stop_internal();
}
/// Indicates if the timer is running
bool is_running() {
std::lock_guard<std::mutex> lock(this->mutex);
return running;
}
private:
template <class Clock, class Duration = typename Clock::duration>
void at_internal(const std::chrono::time_point<Clock, Duration>& time_point) {
if (this->timer_callback == nullptr) {
return;
}
if (this->timer) {
running = true;
// use asio timer
this->timer->expires_at(time_point);
this->timer->async_wait([this](const boost::system::error_code& e) {
if (e) {
return;
}
this->timer_callback();
running = false;
});
}
}
template <class Rep, class Period> void interval_internal(const std::chrono::duration<Rep, Period>& interval) {
this->interval_nanoseconds = interval;
if (interval_nanoseconds == std::chrono::nanoseconds(0)) {
return;
}
if (this->timer_callback == nullptr) {
return;
}
if (this->timer) {
running = true;
// use asio timer
this->callback_wrapper = [this](const boost::system::error_code& error) {
if (error) {
running = false;
return;
}
{
std::lock_guard<std::mutex> lock(this->mutex);
this->timer->expires_after(
std::chrono::duration_cast<typename TimerClock::duration>(this->interval_nanoseconds));
this->timer->async_wait(this->callback_wrapper);
}
this->timer_callback();
};
this->timer->expires_after(
std::chrono::duration_cast<typename TimerClock::duration>(this->interval_nanoseconds));
this->timer->async_wait(this->callback_wrapper);
}
}
template <class Rep, class Period> void timeout_internal(const std::chrono::duration<Rep, Period>& interval) {
if (this->timer_callback == nullptr) {
return;
}
if (this->timer) {
running = true;
// use asio timer
this->timer->expires_after(interval);
this->timer->async_wait([this](const boost::system::error_code& error) {
if (error) {
running = false;
return;
}
this->timer_callback();
running = false;
});
}
}
void stop_internal() {
if (this->timer) {
// asio based timer
this->timer->cancel();
}
running = false;
}
};
using SteadyTimer = Timer<date::utc_clock>;
using SystemTimer = Timer<date::utc_clock>;
} // namespace Everest
#endif // EVEREST_TIMER_HPP