feat(radio): OS compatibility layer (#5840)
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
#include "timer.h"
|
||||
#include "timer_pthread_impl.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
bool _timer_cmp(timer_handle_t *lh, timer_handle_t *rh) {
|
||||
return lh->next_trigger < rh->next_trigger;
|
||||
}
|
||||
|
||||
timer_queue::timer_queue() {}
|
||||
|
||||
void timer_queue::update_current_time() {
|
||||
_current_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void timer_queue::sort_timers() {
|
||||
std::sort(_timers.begin(), _timers.end(), _timer_cmp);
|
||||
}
|
||||
|
||||
timer_queue& timer_queue::instance()
|
||||
{
|
||||
static timer_queue _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void timer_queue::start()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_cmds_mutex);
|
||||
if (!_running) {
|
||||
_running = true;
|
||||
_thread = std::make_unique<std::thread>([&]() { main_loop(); });
|
||||
}
|
||||
}
|
||||
|
||||
void timer_queue::stop() {
|
||||
bool stopping = false;
|
||||
|
||||
std::unique_lock<std::mutex> lock(_cmds_mutex);
|
||||
std::unique_lock<std::mutex> slock(_stop_mutex);
|
||||
|
||||
if (_running) {
|
||||
stopping = true;
|
||||
_running = false;
|
||||
lock.unlock();
|
||||
_cmds_condition.notify_one();
|
||||
_stop_condition.wait(slock);
|
||||
slock.unlock();
|
||||
}
|
||||
_thread->join();
|
||||
}
|
||||
|
||||
void timer_queue::create_timer(timer_handle_t *timer, timer_func_t func, const char *name,
|
||||
unsigned period, bool repeat) {
|
||||
timer->func = func;
|
||||
timer->name = name;
|
||||
timer->period = period;
|
||||
timer->repeat = repeat;
|
||||
timer->next_trigger = time_point_t{};
|
||||
}
|
||||
|
||||
void timer_queue::send_cmd(timer_req_t&& req)
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(_cmds_mutex);
|
||||
_cmds.emplace_back(req);
|
||||
}
|
||||
_cmds_condition.notify_one();
|
||||
}
|
||||
|
||||
void timer_queue::start_timer(timer_handle_t *timer) {
|
||||
send_cmd(timer_req_t{.cmd = timer_req_t::cmd_start, .timer = timer});
|
||||
}
|
||||
|
||||
void timer_queue::stop_timer(timer_handle_t *timer) {
|
||||
send_cmd(timer_req_t{.cmd = timer_req_t::cmd_stop, .timer = timer});
|
||||
}
|
||||
|
||||
void timer_queue::pend_function(timer_async_func_t func, void *param1,
|
||||
uint32_t param2) {
|
||||
send_cmd(timer_req_t{
|
||||
.cmd = timer_req_t::cmd_pend_func,
|
||||
.func_call = {.func = func, .param1 = param1, .param2 = param2}});
|
||||
}
|
||||
|
||||
void timer_queue::main_loop() {
|
||||
|
||||
while (true) {
|
||||
{
|
||||
std::unique_lock lock(_cmds_mutex);
|
||||
time_point_t until;
|
||||
|
||||
update_current_time();
|
||||
process_cmds();
|
||||
|
||||
if (_timers.size() > 0) {
|
||||
timer_handle_t* t = _timers[0];
|
||||
until = t->next_trigger;
|
||||
} else {
|
||||
until = _current_time + std::chrono::milliseconds(1000);
|
||||
}
|
||||
|
||||
std::cv_status result = _cmds_condition.wait_until(lock, until);
|
||||
if (!_running) break;
|
||||
}
|
||||
|
||||
async_calls();
|
||||
trigger_timers();
|
||||
}
|
||||
|
||||
_stop_condition.notify_one();
|
||||
}
|
||||
|
||||
void timer_queue::process_cmds()
|
||||
{
|
||||
while (!_cmds.empty()) {
|
||||
timer_req_t req = _cmds.back();
|
||||
_cmds.pop_back();
|
||||
|
||||
if (req.cmd == timer_req_t::cmd_pend_func) {
|
||||
if (req.func_call.func) {
|
||||
_funcs.emplace_back(std::move(req.func_call));
|
||||
}
|
||||
} else {
|
||||
timer_handle_t *t = req.timer;
|
||||
auto pos = std::find(_timers.begin(), _timers.end(), t);
|
||||
switch (req.cmd) {
|
||||
case timer_req_t::cmd_start:
|
||||
t->next_trigger = _current_time + std::chrono::milliseconds(t->period);
|
||||
t->active = true;
|
||||
if (pos == _timers.end()) {
|
||||
_timers.emplace_back(t);
|
||||
}
|
||||
break;
|
||||
|
||||
case timer_req_t::cmd_stop:
|
||||
if (pos != _timers.end()) {
|
||||
_timers.erase(pos);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void timer_queue::trigger_timers() {
|
||||
int triggered = 0;
|
||||
for (auto t : _timers) {
|
||||
if (t->next_trigger <= _current_time) {
|
||||
if (t->repeat) {
|
||||
t->next_trigger += std::chrono::milliseconds(t->period);
|
||||
} else {
|
||||
t->active = false;
|
||||
stop_timer(t);
|
||||
}
|
||||
t->func(t);
|
||||
triggered++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (triggered) {
|
||||
sort_timers();
|
||||
}
|
||||
}
|
||||
|
||||
void timer_queue::async_calls() {
|
||||
for (auto f : _funcs) {
|
||||
f.func(f.param1, f.param2);
|
||||
}
|
||||
_funcs.clear();
|
||||
}
|
||||
|
||||
int timer_create(timer_handle_t* h, timer_func_t func, const char* name,
|
||||
unsigned period, bool repeat)
|
||||
{
|
||||
if (!h || !func) return -1;
|
||||
timer_queue::create_timer(h, func, name, period, repeat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool timer_is_created(timer_handle_t* h)
|
||||
{
|
||||
return h && h->func;
|
||||
}
|
||||
|
||||
bool timer_is_active(timer_handle_t* h)
|
||||
{
|
||||
return h && h->active;
|
||||
}
|
||||
|
||||
int timer_start(timer_handle_t* h)
|
||||
{
|
||||
if (!timer_is_created(h)) return -1;
|
||||
timer_queue::instance().start_timer(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_stop(timer_handle_t* h)
|
||||
{
|
||||
if (!timer_is_created(h)) return -1;
|
||||
timer_queue::instance().stop_timer(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_set_period(timer_handle_t *h, unsigned period)
|
||||
{
|
||||
if (!timer_is_created(h)) return -1;
|
||||
h->period = period;
|
||||
timer_queue::instance().start_timer(h);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user