You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lokinet/llarp/timer.cpp

256 lines
4.8 KiB
C++

6 years ago
#include <llarp/timer.h>
#include <atomic>
6 years ago
#include <condition_variable>
6 years ago
#include <list>
6 years ago
#include <map>
6 years ago
#include "logger.hpp"
namespace llarp
{
struct timer
{
static uint64_t
now()
{
return std::chrono::duration_cast< std::chrono::milliseconds >(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
6 years ago
void* user;
6 years ago
uint64_t called_at;
uint64_t started;
uint64_t timeout;
llarp_timer_handler_func func;
bool done;
6 years ago
timer(uint64_t ms = 0, void* _user = nullptr,
llarp_timer_handler_func _func = nullptr)
: user(_user)
, called_at(0)
, started(now())
, timeout(ms)
, func(_func)
, done(false)
{
}
6 years ago
6 years ago
timer&
operator=(const timer& other)
{
6 years ago
user = other.user;
called_at = other.called_at;
started = other.started;
timeout = other.timeout;
func = other.func;
6 years ago
return *this;
}
void
exec();
6 years ago
static void
call(void* user)
{
static_cast< timer* >(user)->exec();
}
6 years ago
operator llarp_thread_job()
{
return {this, timer::call};
}
};
6 years ago
}; // namespace llarp
6 years ago
struct llarp_timer_context
{
llarp_threadpool* threadpool;
6 years ago
std::mutex timersMutex;
std::map< uint32_t, llarp::timer > timers;
6 years ago
std::mutex tickerMutex;
6 years ago
std::condition_variable ticker;
6 years ago
std::chrono::milliseconds nextTickLen = std::chrono::milliseconds(10);
6 years ago
uint32_t ids = 0;
bool _run = true;
6 years ago
bool
run()
{
return _run;
}
6 years ago
void
stop()
{
_run = false;
}
6 years ago
void
cancel(uint32_t id)
{
llarp::timer t;
{
std::unique_lock< std::mutex > lock(timersMutex);
auto itr = timers.find(id);
if(itr == timers.end())
return;
t = itr->second;
6 years ago
}
6 years ago
t.called_at = llarp::timer::now();
t.exec();
6 years ago
}
void
remove(uint32_t id)
6 years ago
{
std::unique_lock< std::mutex > lock(timersMutex);
auto itr = timers.find(id);
if(itr != timers.end())
timers.erase(itr);
6 years ago
}
uint32_t
call_later(void* user, llarp_timer_handler_func func, uint64_t timeout_ms)
{
std::unique_lock< std::mutex > lock(timersMutex);
6 years ago
uint32_t id = ++ids;
6 years ago
timers.emplace(id, std::move(llarp::timer(timeout_ms, user, func)));
6 years ago
return id;
}
6 years ago
void
cancel_all()
{
std::list< uint32_t > ids;
6 years ago
{
std::unique_lock< std::mutex > lock(timersMutex);
for(auto& item : timers)
{
ids.push_back(item.first);
}
6 years ago
}
6 years ago
for(auto id : ids)
{
cancel(id);
6 years ago
}
}
};
extern "C" {
struct llarp_timer_context*
llarp_init_timer()
{
6 years ago
return new llarp_timer_context;
}
6 years ago
uint32_t
llarp_timer_call_later(struct llarp_timer_context* t,
struct llarp_timeout_job job)
{
6 years ago
return t->call_later(job.user, job.handler, job.timeout);
}
6 years ago
void
llarp_free_timer(struct llarp_timer_context** t)
{
if(*t)
delete *t;
6 years ago
*t = nullptr;
}
6 years ago
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)
{
// destroy all timers
// don't call callbacks on timers
llarp::Debug("clear timers");
t->timers.clear();
6 years ago
t->stop();
llarp::Debug("stop timers");
t->ticker.notify_all();
6 years ago
}
6 years ago
void
llarp_timer_cancel_job(struct llarp_timer_context* t, uint32_t id)
{
6 years ago
t->cancel(id);
}
6 years ago
void
llarp_timer_run(struct llarp_timer_context* t, struct llarp_threadpool* pool)
{
6 years ago
t->threadpool = pool;
while(t->run())
{
6 years ago
// wait for timer mutex
6 years ago
{
6 years ago
std::unique_lock< std::mutex > lock(t->tickerMutex);
t->ticker.wait_for(lock, t->nextTickLen);
}
6 years ago
if(t->run())
{
std::unique_lock< std::mutex > lock(t->timersMutex);
// we woke up
auto now = llarp::timer::now();
auto itr = t->timers.begin();
while(itr != t->timers.end())
6 years ago
{
if(now - itr->second.started >= itr->second.timeout)
{
6 years ago
if(itr->second.func)
{
// timer hit
itr->second.called_at = now;
llarp_threadpool_queue_job(pool, itr->second);
6 years ago
++itr;
6 years ago
}
else if(itr->second.done)
6 years ago
{
// timer was already called, remove timer
itr = t->timers.erase(itr);
}
else
++itr;
}
6 years ago
else // timer not hit yet
++itr;
6 years ago
}
6 years ago
}
}
}
6 years ago
}
6 years ago
namespace llarp
{
void
timer::exec()
6 years ago
{
if(func)
{
6 years ago
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)
6 years ago
call(user, timeout, 0);
6 years ago
else
6 years ago
call(user, timeout, diff);
done = true;
6 years ago
}
}
}