lokinet/llarp/util/thread/timer.cpp

328 lines
7.1 KiB
C++
Raw Normal View History

2019-09-01 13:26:16 +00:00
#include <util/thread/timer.hpp>
#include <util/logging/logger.hpp>
#include <util/time.hpp>
2018-04-30 18:18:18 +00:00
#include <atomic>
2018-04-30 18:18:34 +00:00
#include <condition_variable>
2018-04-30 18:18:18 +00:00
#include <list>
#include <memory>
#include <queue>
2018-06-04 17:22:14 +00:00
#include <unordered_map>
2019-07-30 23:42:13 +00:00
#include <utility>
2018-04-30 18:18:18 +00:00
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;
std::function< void(void) > deferredFunc;
2018-06-04 11:49:37 +00:00
bool done;
2018-06-05 11:48:06 +00:00
bool canceled;
2018-10-29 16:48:36 +00:00
timer(llarp_time_t now, uint64_t ms = 0, void* _user = nullptr,
2018-06-04 11:46:02 +00:00
llarp_timer_handler_func _func = nullptr)
2018-06-04 11:49:37 +00:00
: user(_user)
, called_at(0)
2018-10-29 16:48:36 +00:00
, started(now)
2018-06-04 11:49:37 +00:00
, timeout(ms)
2019-07-30 23:42:13 +00:00
, func(std::move(_func))
2018-06-04 11:49:37 +00:00
, done(false)
2018-06-05 11:48:06 +00:00
, canceled(false)
{
}
2019-07-30 23:42:13 +00:00
~timer() = default;
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
bool
operator<(const timer& other) const
{
return (started + timeout) < (other.started + other.timeout);
}
};
2019-04-25 23:21:19 +00:00
} // namespace llarp
2018-04-30 18:18:18 +00:00
struct llarp_timer_context
{
llarp::util::Mutex timersMutex; // protects timers
std::unordered_map< uint32_t, std::unique_ptr< llarp::timer > > timers
GUARDED_BY(timersMutex);
2018-08-30 18:48:43 +00:00
std::priority_queue< std::unique_ptr< llarp::timer > > calling;
2018-08-12 17:22:29 +00:00
llarp::util::Mutex tickerMutex;
std::unique_ptr< llarp::util::Condition > ticker;
absl::Duration nextTickLen = absl::Milliseconds(100);
2018-04-30 18:18:18 +00:00
2018-10-29 16:48:36 +00:00
llarp_time_t m_Now;
llarp_time_t m_NextRequiredTickAt =
std::numeric_limits< llarp_time_t >::max();
size_t m_NumPendingTimers;
2018-10-29 16:48:36 +00:00
llarp_timer_context()
{
2018-11-19 22:45:37 +00:00
m_Now = llarp::time_now_ms();
2018-10-29 16:48:36 +00:00
}
2019-04-25 23:21:19 +00:00
uint32_t currentId = 0;
bool _run = true;
2018-04-30 18:18:18 +00:00
2019-07-30 23:42:13 +00:00
~llarp_timer_context() = default;
2018-06-06 12:46:26 +00:00
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) LOCKS_EXCLUDED(timersMutex)
{
llarp::util::Lock lock(&timersMutex);
2018-08-30 18:48:43 +00:00
const auto& itr = timers.find(id);
2018-06-05 11:48:06 +00:00
if(itr == timers.end())
return;
itr->second->canceled = true;
2018-04-30 18:18:18 +00:00
}
void
remove(uint32_t id) LOCKS_EXCLUDED(timersMutex)
2018-05-18 17:50:21 +00:00
{
llarp::util::Lock lock(&timersMutex);
2018-08-30 18:48:43 +00:00
const 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)
LOCKS_EXCLUDED(timersMutex)
{
llarp::util::Lock lock(&timersMutex);
2018-10-29 16:48:36 +00:00
const uint32_t id = ++currentId;
timers.emplace(
id, std::make_unique< llarp::timer >(m_Now, timeout_ms, user, func));
m_NextRequiredTickAt = std::min(m_NextRequiredTickAt, m_Now + timeout_ms);
m_NumPendingTimers = timers.size();
2018-04-30 18:18:18 +00:00
return id;
}
2018-04-30 18:18:34 +00:00
uint32_t
call_func_later(std::function< void(void) > func, llarp_time_t timeout_ms)
{
llarp::util::Lock lock(&timersMutex);
const uint32_t id = ++currentId;
timers.emplace(
id,
std::make_unique< llarp::timer >(m_Now, timeout_ms, nullptr, nullptr));
timers[id]->deferredFunc = func;
m_NextRequiredTickAt = std::min(m_NextRequiredTickAt, m_Now + timeout_ms);
m_NumPendingTimers = timers.size();
return id;
}
void
cancel_all() LOCKS_EXCLUDED(timersMutex)
{
{
llarp::util::Lock lock(&timersMutex);
for(auto& item : timers)
{
item.second->func = nullptr;
item.second->canceled = true;
}
2018-04-30 18:18:18 +00:00
}
}
2018-04-30 18:18:34 +00:00
bool
ShouldTriggerTimers(llarp_time_t peekAhead) const
{
return m_NumPendingTimers > 0
and (m_Now + peekAhead) >= m_NextRequiredTickAt;
2018-04-30 18:18:18 +00:00
}
};
struct llarp_timer_context*
llarp_init_timer()
{
return new llarp_timer_context();
2018-04-30 18:18:34 +00:00
}
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
uint32_t
llarp_timer_call_func_later(struct llarp_timer_context* t, llarp_time_t timeout,
std::function< void(void) > func)
{
return t->call_func_later(func, timeout);
}
void
llarp_free_timer(struct llarp_timer_context* t)
{
delete t;
2018-04-30 18:18:34 +00:00
}
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)
{
llarp::LogDebug("timers stopping");
2018-05-28 20:51:15 +00:00
// destroy all timers
// don't call callbacks on timers
2019-05-15 15:54:26 +00:00
{
llarp::util::Lock lock(&t->timersMutex);
t->timers.clear();
t->stop();
}
2018-06-06 12:46:26 +00:00
if(t->ticker)
t->ticker->SignalAll();
llarp::LogDebug("timers stopped");
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-10-29 16:48:36 +00:00
void
llarp_timer_set_time(struct llarp_timer_context* t, llarp_time_t now)
{
if(now == 0)
2018-11-19 22:45:37 +00:00
now = llarp::time_now_ms();
2018-10-29 16:48:36 +00:00
t->m_Now = now;
}
2018-06-06 12:46:26 +00:00
void
2018-08-09 19:02:17 +00:00
llarp_timer_tick_all(struct llarp_timer_context* t)
2018-06-06 12:46:26 +00:00
{
if(!t->run())
return;
2018-10-29 16:48:36 +00:00
2018-08-30 18:48:43 +00:00
std::list< std::unique_ptr< llarp::timer > > hit;
2018-06-06 12:46:26 +00:00
{
llarp::util::Lock lock(&t->timersMutex);
2018-08-30 18:48:43 +00:00
auto itr = t->timers.begin();
while(itr != t->timers.end())
2018-06-06 12:46:26 +00:00
{
2018-10-29 16:48:36 +00:00
if(t->m_Now - itr->second->started >= itr->second->timeout
2018-08-30 18:48:43 +00:00
|| itr->second->canceled)
2018-06-06 12:46:26 +00:00
{
// timer hit
2018-08-30 18:48:43 +00:00
hit.emplace_back(std::move(itr->second));
itr = t->timers.erase(itr);
2018-06-06 12:46:26 +00:00
}
2018-08-30 18:48:43 +00:00
else
{
2018-08-30 18:48:43 +00:00
++itr;
}
2018-08-30 18:48:43 +00:00
}
}
while(not hit.empty())
{
const auto& h = hit.front();
h->called_at = t->m_Now;
h->exec();
hit.pop_front();
}
// reindex next tick info
2018-08-30 18:48:43 +00:00
{
llarp::util::Lock lock(&t->timersMutex);
t->m_Now = llarp::time_now_ms();
t->m_NextRequiredTickAt = std::numeric_limits< llarp_time_t >::max();
for(const auto& item : t->timers)
2018-08-30 18:48:43 +00:00
{
t->m_NextRequiredTickAt =
std::min(t->m_NextRequiredTickAt, item.second->timeout + t->m_Now);
2018-06-06 12:46:26 +00:00
}
t->m_NumPendingTimers = t->timers.size();
2018-06-06 12:46:26 +00:00
}
}
2018-08-09 19:02:17 +00:00
void
llarp_timer_tick_all_async(struct llarp_timer_context* t,
2018-10-29 16:48:36 +00:00
struct llarp_threadpool* pool, llarp_time_t now)
2018-08-09 19:02:17 +00:00
{
llarp_timer_set_time(t, now);
if(t->ShouldTriggerTimers(pool->GuessJobLatency()))
llarp_threadpool_queue_job(pool, std::bind(&llarp_timer_tick_all, t));
2018-08-09 19:02:17 +00:00
}
void
llarp_timer_run(struct llarp_timer_context* t, struct llarp_threadpool* pool)
{
t->ticker = std::make_unique< llarp::util::Condition >();
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
{
llarp::util::Lock lock(&t->tickerMutex);
t->ticker->WaitWithTimeout(&t->tickerMutex, 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())
{
llarp::util::Lock lock(&t->timersMutex);
2018-05-28 20:51:15 +00:00
// we woke up
2018-11-19 22:45:37 +00:00
llarp_timer_tick_all_async(t, pool, llarp::time_now_ms());
2018-04-30 18:18:18 +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
}
if(deferredFunc && not canceled)
deferredFunc();
deferredFunc = nullptr;
done = true;
2018-05-18 17:50:21 +00:00
}
2018-07-09 03:34:29 +00:00
} // namespace llarp