lokinet/llarp/timer.cpp

259 lines
4.7 KiB
C++
Raw Normal View History

2018-06-05 11:48:06 +00:00
#include <llarp/time.h>
2018-04-30 18:18:18 +00:00
#include <llarp/timer.h>
#include <atomic>
2018-04-30 18:18:34 +00:00
#include <condition_variable>
2018-04-30 18:18:18 +00:00
#include <list>
2018-06-04 17:22:14 +00:00
#include <unordered_map>
2018-04-30 18:18:18 +00:00
2018-05-28 20:51:15 +00:00
#include "logger.hpp"
namespace llarp
{
struct timer
{
void* user;
2018-06-04 11:46:02 +00:00
uint64_t called_at;
uint64_t started;
uint64_t timeout;
llarp_timer_handler_func func;
2018-06-04 11:49:37 +00:00
bool done;
2018-06-05 11:48:06 +00:00
bool canceled;
2018-06-04 11:46:02 +00:00
timer(uint64_t ms = 0, void* _user = nullptr,
llarp_timer_handler_func _func = nullptr)
2018-06-04 11:49:37 +00:00
: user(_user)
, called_at(0)
2018-06-05 11:48:06 +00:00
, started(llarp_time_now_ms())
2018-06-04 11:49:37 +00:00
, timeout(ms)
, func(_func)
, done(false)
2018-06-05 11:48:06 +00:00
, canceled(false)
{
}
~timer()
{
}
2018-04-30 18:18:34 +00:00
void
exec();
2018-05-18 17:50:21 +00:00
static void
call(void* user)
{
static_cast< timer* >(user)->exec();
}
2018-05-18 17:50:21 +00:00
2018-06-05 11:48:06 +00:00
void
send_job(llarp_threadpool* pool)
{
2018-06-05 11:48:06 +00:00
llarp_threadpool_queue_job(pool, {this, timer::call});
}
};
2018-04-30 18:18:34 +00:00
}; // namespace llarp
2018-04-30 18:18:18 +00:00
struct llarp_timer_context
{
2018-04-30 18:18:18 +00:00
std::mutex timersMutex;
2018-06-04 17:22:14 +00:00
std::unordered_map< uint32_t, llarp::timer* > timers;
2018-05-29 13:40:26 +00:00
std::mutex tickerMutex;
2018-06-06 12:46:26 +00:00
std::condition_variable* ticker = nullptr;
std::chrono::milliseconds nextTickLen = std::chrono::milliseconds(100);
2018-04-30 18:18:18 +00:00
2018-05-28 20:51:15 +00:00
uint32_t ids = 0;
bool _run = true;
2018-04-30 18:18:18 +00:00
2018-06-06 12:46:26 +00:00
~llarp_timer_context()
{
if(ticker)
delete ticker;
}
bool
run()
{
2018-05-28 20:51:15 +00:00
return _run;
}
2018-04-30 18:18:34 +00:00
void
stop()
{
2018-05-28 20:51:15 +00:00
_run = false;
}
2018-04-30 18:18:18 +00:00
void
cancel(uint32_t id)
{
2018-06-05 11:48:06 +00:00
std::unique_lock< std::mutex > lock(timersMutex);
auto itr = timers.find(id);
if(itr == timers.end())
return;
itr->second->canceled = true;
2018-04-30 18:18:18 +00:00
}
void
remove(uint32_t id)
2018-05-18 17:50:21 +00:00
{
std::unique_lock< std::mutex > lock(timersMutex);
auto itr = timers.find(id);
2018-06-05 11:48:06 +00:00
if(itr == timers.end())
return;
itr->second->func = nullptr;
itr->second->canceled = true;
2018-05-18 17:50:21 +00:00
}
uint32_t
call_later(void* user, llarp_timer_handler_func func, uint64_t timeout_ms)
{
std::unique_lock< std::mutex > lock(timersMutex);
2018-05-10 23:32:46 +00:00
uint32_t id = ++ids;
2018-06-06 12:46:26 +00:00
timers[id] = new llarp::timer(timeout_ms, user, func);
2018-04-30 18:18:18 +00:00
return id;
}
2018-04-30 18:18:34 +00:00
void
cancel_all()
{
std::list< uint32_t > ids;
2018-04-30 18:18:34 +00:00
{
std::unique_lock< std::mutex > lock(timersMutex);
for(auto& item : timers)
{
ids.push_back(item.first);
}
2018-04-30 18:18:18 +00:00
}
2018-04-30 18:18:34 +00:00
for(auto id : ids)
{
cancel(id);
2018-04-30 18:18:18 +00:00
}
}
};
extern "C" {
struct llarp_timer_context*
llarp_init_timer()
{
2018-04-30 18:18:34 +00:00
return new llarp_timer_context;
}
2018-04-30 18:18:18 +00:00
uint32_t
llarp_timer_call_later(struct llarp_timer_context* t,
struct llarp_timeout_job job)
{
2018-04-30 18:18:34 +00:00
return t->call_later(job.user, job.handler, job.timeout);
}
2018-04-30 18:18:18 +00:00
void
llarp_free_timer(struct llarp_timer_context** t)
{
if(*t)
delete *t;
2018-04-30 18:18:34 +00:00
*t = nullptr;
}
2018-04-30 18:18:18 +00:00
void
llarp_timer_remove_job(struct llarp_timer_context* t, uint32_t id)
{
t->remove(id);
}
void
llarp_timer_stop(struct llarp_timer_context* t)
{
2018-05-28 20:51:15 +00:00
// destroy all timers
// don't call callbacks on timers
t->timers.clear();
2018-04-30 18:18:34 +00:00
t->stop();
2018-06-06 12:46:26 +00:00
if(t->ticker)
t->ticker->notify_all();
2018-04-30 18:18:34 +00:00
}
2018-04-30 18:18:18 +00:00
void
llarp_timer_cancel_job(struct llarp_timer_context* t, uint32_t id)
{
2018-04-30 18:18:34 +00:00
t->cancel(id);
}
2018-04-30 18:18:18 +00:00
2018-06-06 12:46:26 +00:00
void
llarp_timer_tick_all(struct llarp_timer_context* t,
struct llarp_threadpool* pool)
{
if(!t->run())
return;
auto now = llarp_time_now_ms();
auto itr = t->timers.begin();
while(itr != t->timers.end())
{
if(now - itr->second->started >= itr->second->timeout
|| itr->second->canceled)
{
if(itr->second->func && itr->second->called_at == 0)
{
// timer hit
itr->second->called_at = now;
itr->second->send_job(pool);
++itr;
}
else if(itr->second->done)
{
// remove timer
llarp::timer* timer = itr->second;
itr = t->timers.erase(itr);
delete timer;
}
else
++itr;
}
else // timer not hit yet
++itr;
}
}
void
llarp_timer_run(struct llarp_timer_context* t, struct llarp_threadpool* pool)
{
2018-06-06 12:46:26 +00:00
t->ticker = new std::condition_variable;
while(t->run())
{
2018-05-29 13:40:26 +00:00
// wait for timer mutex
2018-06-06 12:46:26 +00:00
if(t->ticker)
2018-05-18 17:50:21 +00:00
{
2018-05-29 13:40:26 +00:00
std::unique_lock< std::mutex > lock(t->tickerMutex);
2018-06-06 12:46:26 +00:00
t->ticker->wait_for(lock, t->nextTickLen);
2018-05-29 13:40:26 +00:00
}
2018-05-28 20:51:15 +00:00
2018-05-29 13:40:26 +00:00
if(t->run())
{
std::unique_lock< std::mutex > lock(t->timersMutex);
2018-05-28 20:51:15 +00:00
// we woke up
2018-06-06 12:46:26 +00:00
llarp_timer_tick_all(t, pool);
2018-04-30 18:18:18 +00:00
}
}
}
2018-04-30 18:18:34 +00:00
}
2018-05-18 17:50:21 +00:00
namespace llarp
{
void
timer::exec()
2018-05-18 17:50:21 +00:00
{
if(func)
{
2018-06-04 11:46:02 +00:00
auto diff = called_at - started;
// zero out function pointer before call to prevent multiple calls being
// queued if call takes longer than 1 timer tick
auto call = func;
func = nullptr;
if(diff >= timeout)
2018-06-04 11:46:02 +00:00
call(user, timeout, 0);
2018-05-18 17:50:21 +00:00
else
2018-06-04 11:46:02 +00:00
call(user, timeout, diff);
2018-05-18 17:50:21 +00:00
}
2018-06-05 11:48:06 +00:00
done = true;
2018-05-18 17:50:21 +00:00
}
}