2020-11-26 01:25:22 -08:00
|
|
|
/**
|
2017-05-18 03:11:31 -07:00
|
|
|
*
|
2020-11-26 01:25:22 -08:00
|
|
|
* \section COPYRIGHT
|
2017-05-18 03:11:31 -07:00
|
|
|
*
|
2021-03-19 03:45:56 -07:00
|
|
|
* Copyright 2013-2021 Software Radio Systems Limited
|
2017-05-18 03:11:31 -07:00
|
|
|
*
|
2020-11-26 01:25:22 -08:00
|
|
|
* By using this file, you agree to the terms and conditions set
|
|
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
|
|
* the distribution.
|
2017-05-18 03:11:31 -07:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* File: timers.h
|
|
|
|
* Description: Manually incremented timers. Call a callback function upon
|
|
|
|
* expiry.
|
|
|
|
* Reference:
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2021-03-19 03:45:56 -07:00
|
|
|
#ifndef SRSRAN_TIMERS_H
|
|
|
|
#define SRSRAN_TIMERS_H
|
2017-05-18 03:11:31 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
#include "srsran/adt/circular_array.h"
|
|
|
|
#include "srsran/adt/intrusive_list.h"
|
2021-03-19 03:45:56 -07:00
|
|
|
#include "srsran/adt/move_callback.h"
|
|
|
|
#include "srsran/phy/utils/debug.h"
|
2019-09-17 03:29:03 -07:00
|
|
|
#include <algorithm>
|
2021-04-14 13:10:06 -07:00
|
|
|
#include <cstdint>
|
|
|
|
#include <deque>
|
2019-09-16 11:28:48 -07:00
|
|
|
#include <limits>
|
2020-01-26 08:24:19 -08:00
|
|
|
#include <mutex>
|
2017-05-18 03:11:31 -07:00
|
|
|
|
2021-03-19 03:45:56 -07:00
|
|
|
namespace srsran {
|
2019-10-21 04:03:30 -07:00
|
|
|
|
|
|
|
class timer_callback
|
2017-05-18 03:11:31 -07:00
|
|
|
{
|
2019-10-21 04:03:30 -07:00
|
|
|
public:
|
|
|
|
virtual void timer_expired(uint32_t timer_id) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
class timer_handler
|
2019-09-16 11:02:47 -07:00
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
using tic_diff_t = uint32_t;
|
|
|
|
using tic_t = uint32_t;
|
|
|
|
constexpr static size_t WHEEL_SIZE = 1024;
|
|
|
|
constexpr static tic_t invalid_tic = std::numeric_limits<tic_t>::max();
|
|
|
|
constexpr static uint32_t MAX_TIMER_DURATION = std::numeric_limits<tic_diff_t>::max() / 4;
|
|
|
|
|
|
|
|
struct timer_impl : public intrusive_double_linked_list_element<> {
|
|
|
|
timer_handler& parent;
|
|
|
|
const size_t id;
|
|
|
|
tic_diff_t duration = 0;
|
|
|
|
tic_t timeout = 0;
|
|
|
|
enum state_t : int8_t { empty, stopped, running, expired } state = empty;
|
2021-03-19 03:45:56 -07:00
|
|
|
srsran::move_callback<void(uint32_t)> callback;
|
2019-09-16 11:02:47 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
explicit timer_impl(timer_handler& parent_, size_t id_) : parent(parent_), id(id_) {}
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
bool is_empty() const { return state == empty; }
|
|
|
|
bool is_running() const { return state == running; }
|
|
|
|
bool is_expired() const { return state == expired; }
|
|
|
|
tic_diff_t time_left() const { return is_running() ? timeout - parent.cur_time : (is_expired() ? 0 : duration); }
|
|
|
|
uint32_t time_elapsed() const { return duration - time_left(); }
|
2019-09-16 11:02:47 -07:00
|
|
|
|
2019-09-16 11:28:48 -07:00
|
|
|
bool set(uint32_t duration_)
|
2019-09-16 11:02:47 -07:00
|
|
|
{
|
2019-10-21 04:03:30 -07:00
|
|
|
if (duration_ > MAX_TIMER_DURATION) {
|
2021-02-01 09:13:07 -08:00
|
|
|
ERROR("Error: timer durations above %u are not supported", MAX_TIMER_DURATION);
|
2019-09-16 11:28:48 -07:00
|
|
|
return false;
|
|
|
|
}
|
2021-04-14 13:10:06 -07:00
|
|
|
duration = std::max(duration_, 1u);
|
2019-10-22 08:56:28 -07:00
|
|
|
if (is_running()) {
|
|
|
|
// if already running, just extends timer lifetime
|
|
|
|
run();
|
2021-04-14 13:10:06 -07:00
|
|
|
} else {
|
|
|
|
state = stopped;
|
|
|
|
timeout = 0;
|
2019-10-22 08:56:28 -07:00
|
|
|
}
|
2019-09-16 11:28:48 -07:00
|
|
|
return true;
|
2019-09-16 11:02:47 -07:00
|
|
|
}
|
|
|
|
|
2021-03-19 03:45:56 -07:00
|
|
|
bool set(uint32_t duration_, srsran::move_callback<void(uint32_t)> callback_)
|
2019-09-16 11:02:47 -07:00
|
|
|
{
|
2019-09-16 11:28:48 -07:00
|
|
|
if (set(duration_)) {
|
|
|
|
callback = std::move(callback_);
|
|
|
|
return true;
|
2019-09-16 11:02:47 -07:00
|
|
|
}
|
2019-09-16 11:28:48 -07:00
|
|
|
return false;
|
2019-09-16 11:02:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void run()
|
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
std::lock_guard<std::mutex> lock(parent.mutex);
|
|
|
|
parent.start_run_(*this);
|
2019-09-16 11:02:47 -07:00
|
|
|
}
|
|
|
|
|
2019-10-21 04:03:30 -07:00
|
|
|
void stop()
|
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
std::lock_guard<std::mutex> lock(parent.mutex);
|
|
|
|
// does not call callback
|
|
|
|
parent.stop_timer_(*this);
|
2019-10-21 04:03:30 -07:00
|
|
|
}
|
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
void clear() { parent.dealloc_timer(*this); }
|
2019-09-16 11:02:47 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
class unique_timer
|
|
|
|
{
|
|
|
|
public:
|
2021-04-14 13:10:06 -07:00
|
|
|
unique_timer() = default;
|
|
|
|
explicit unique_timer(timer_impl* handle_) : handle(handle_) {}
|
2019-09-16 11:02:47 -07:00
|
|
|
unique_timer(const unique_timer&) = delete;
|
2021-04-14 13:10:06 -07:00
|
|
|
unique_timer(unique_timer&& other) noexcept : handle(other.handle) { other.handle = nullptr; }
|
|
|
|
~unique_timer() { release(); }
|
2019-09-16 11:02:47 -07:00
|
|
|
unique_timer& operator=(const unique_timer&) = delete;
|
2021-04-14 13:10:06 -07:00
|
|
|
unique_timer& operator =(unique_timer&& other) noexcept
|
2019-09-16 11:02:47 -07:00
|
|
|
{
|
|
|
|
if (this != &other) {
|
2021-04-14 13:10:06 -07:00
|
|
|
handle = other.handle;
|
|
|
|
other.handle = nullptr;
|
2019-09-16 11:02:47 -07:00
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
bool is_valid() const { return handle != nullptr; }
|
2019-10-21 10:00:49 -07:00
|
|
|
|
2021-03-08 17:25:26 -08:00
|
|
|
void set(uint32_t duration_, move_callback<void(uint32_t)> callback_)
|
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
srsran_assert(is_valid(), "Trying to setup empty timer handle");
|
|
|
|
handle->set(duration_, std::move(callback_));
|
|
|
|
}
|
|
|
|
void set(uint32_t duration_)
|
|
|
|
{
|
|
|
|
srsran_assert(is_valid(), "Trying to setup empty timer handle");
|
|
|
|
handle->set(duration_);
|
2021-03-08 17:25:26 -08:00
|
|
|
}
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
bool is_set() const { return is_valid() and handle->duration > 0; }
|
2019-12-12 02:47:48 -08:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
bool is_running() const { return is_valid() and handle->is_running(); }
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
bool is_expired() const { return is_valid() and handle->is_expired(); }
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed() : -1; }
|
2019-10-21 10:00:49 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
uint32_t id() const { return is_valid() ? handle->id : -1; }
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
tic_diff_t duration() const { return is_valid() ? handle->duration : -1; }
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
void run()
|
2019-10-21 10:00:49 -07:00
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
srsran_assert(is_valid(), "Starting invalid timer");
|
|
|
|
handle->run();
|
2019-10-21 10:00:49 -07:00
|
|
|
}
|
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
void stop()
|
|
|
|
{
|
|
|
|
if (is_valid()) {
|
|
|
|
handle->stop();
|
|
|
|
}
|
|
|
|
}
|
2019-09-16 11:02:47 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
void release()
|
|
|
|
{
|
|
|
|
if (is_valid()) {
|
|
|
|
handle->clear();
|
|
|
|
handle = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2019-10-21 10:00:49 -07:00
|
|
|
|
2019-09-16 11:02:47 -07:00
|
|
|
private:
|
2021-04-14 13:10:06 -07:00
|
|
|
timer_impl* handle = nullptr;
|
2019-09-16 11:02:47 -07:00
|
|
|
};
|
|
|
|
|
2019-10-21 04:03:30 -07:00
|
|
|
explicit timer_handler(uint32_t capacity = 64)
|
2019-09-17 03:29:03 -07:00
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
// Pre-reserve timers
|
|
|
|
while (timer_list.size() < capacity) {
|
|
|
|
timer_list.emplace_back(*this, timer_list.size());
|
|
|
|
}
|
2019-09-17 03:29:03 -07:00
|
|
|
}
|
|
|
|
|
2019-09-16 11:02:47 -07:00
|
|
|
void step_all()
|
|
|
|
{
|
2020-01-26 08:24:19 -08:00
|
|
|
std::unique_lock<std::mutex> lock(mutex);
|
2019-09-16 11:02:47 -07:00
|
|
|
cur_time++;
|
2021-04-14 13:10:06 -07:00
|
|
|
if (cur_time == WHEEL_SIZE) {
|
|
|
|
// Promote timers from 2nd wheel to first if needed
|
|
|
|
for (size_t i = 0; i < WHEEL_SIZE; ++i) {
|
|
|
|
for (auto it = second_wheel[i].begin(); it != second_wheel[i].end();) {
|
|
|
|
auto& timer = timer_list[it->id];
|
|
|
|
timer.timeout -= WHEEL_SIZE;
|
|
|
|
++it;
|
|
|
|
if (timer.timeout < WHEEL_SIZE) {
|
|
|
|
second_wheel[i].pop(&timer);
|
|
|
|
first_wheel[i].push_front(&timer);
|
|
|
|
}
|
|
|
|
}
|
2020-02-28 10:28:51 -08:00
|
|
|
}
|
2021-04-14 13:10:06 -07:00
|
|
|
cur_time = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& wheel_list = first_wheel[cur_time % WHEEL_SIZE];
|
|
|
|
while (not wheel_list.empty()) {
|
|
|
|
// Remove timer from wheel
|
|
|
|
timer_impl& timer = wheel_list.front();
|
|
|
|
wheel_list.pop_front();
|
2020-01-26 08:24:19 -08:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
// update timer state
|
|
|
|
timer.state = timer_impl::expired;
|
|
|
|
nof_timers_running_--;
|
|
|
|
|
|
|
|
// Call callback
|
|
|
|
if (not timer.callback.is_empty()) {
|
2020-01-26 08:24:19 -08:00
|
|
|
// unlock mutex, it could be that the callback tries to run a timer too
|
|
|
|
lock.unlock();
|
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
timer.callback(timer.id);
|
2020-01-26 08:24:19 -08:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
// Lock again to keep protecting the wheel
|
2020-01-26 08:24:19 -08:00
|
|
|
lock.lock();
|
2019-09-16 11:02:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop_all()
|
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
2019-09-16 11:02:47 -07:00
|
|
|
// does not call callback
|
2021-04-14 13:10:06 -07:00
|
|
|
for (timer_impl& timer : timer_list) {
|
|
|
|
stop_timer_(timer);
|
2019-09-16 11:02:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
unique_timer get_unique_timer() { return unique_timer(&alloc_timer()); }
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2019-09-17 03:29:03 -07:00
|
|
|
uint32_t nof_timers() const
|
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
return nof_timers_;
|
2019-09-17 03:29:03 -07:00
|
|
|
}
|
2019-09-16 11:02:47 -07:00
|
|
|
|
2019-10-21 10:00:49 -07:00
|
|
|
uint32_t nof_running_timers() const
|
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
return nof_timers_running_;
|
2019-10-21 10:00:49 -07:00
|
|
|
}
|
|
|
|
|
2020-02-28 09:56:20 -08:00
|
|
|
template <typename F>
|
|
|
|
void defer_callback(uint32_t duration, const F& func)
|
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
timer_impl& timer = alloc_timer();
|
|
|
|
srsran::move_callback<void(uint32_t)> c = [func, &timer](uint32_t tid) {
|
2020-02-28 09:56:20 -08:00
|
|
|
func();
|
|
|
|
// auto-deletes timer
|
2021-04-14 13:10:06 -07:00
|
|
|
timer.clear();
|
2020-02-28 09:56:20 -08:00
|
|
|
};
|
2021-04-14 13:10:06 -07:00
|
|
|
timer.set(duration, std::move(c));
|
|
|
|
timer.run();
|
2020-02-28 09:56:20 -08:00
|
|
|
}
|
|
|
|
|
2019-09-16 11:02:47 -07:00
|
|
|
private:
|
2021-04-14 13:10:06 -07:00
|
|
|
timer_impl& alloc_timer()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
nof_timers_++;
|
|
|
|
if (nof_timers_ > timer_list.size()) {
|
|
|
|
// Need to increase deque
|
|
|
|
timer_list.emplace_back(*this, timer_list.size());
|
|
|
|
timer_impl& ret = timer_list.back();
|
|
|
|
ret.state = timer_impl::stopped;
|
|
|
|
return ret;
|
|
|
|
}
|
2019-10-21 04:03:30 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
for (auto& timer : timer_list) {
|
|
|
|
if (timer.is_empty()) {
|
|
|
|
timer.state = timer_impl::stopped;
|
|
|
|
return timer;
|
2019-09-16 11:28:48 -07:00
|
|
|
}
|
|
|
|
}
|
2021-04-14 13:10:06 -07:00
|
|
|
srsran_terminate("Failed to allocate timer");
|
|
|
|
}
|
2019-09-16 11:02:47 -07:00
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
void dealloc_timer(timer_impl& timer)
|
2020-02-28 09:56:20 -08:00
|
|
|
{
|
2021-04-14 13:10:06 -07:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
if (timer.is_empty()) {
|
|
|
|
// already deallocated
|
|
|
|
return;
|
2020-02-28 09:56:20 -08:00
|
|
|
}
|
2021-04-14 13:10:06 -07:00
|
|
|
stop_timer_(timer);
|
|
|
|
timer.state = timer_impl::empty;
|
|
|
|
timer.duration = 0;
|
|
|
|
timer.timeout = 0;
|
|
|
|
timer.callback = srsran::move_callback<void(uint32_t)>();
|
|
|
|
nof_timers_--;
|
|
|
|
// leave id unchanged.
|
|
|
|
}
|
|
|
|
|
|
|
|
void start_run_(timer_impl& timer)
|
|
|
|
{
|
|
|
|
uint32_t timeout = cur_time + timer.duration;
|
|
|
|
if (timer.is_running() and timer.timeout == timeout) {
|
|
|
|
// If no change in timeout, no need to change wheel position
|
|
|
|
return;
|
2020-02-28 09:56:20 -08:00
|
|
|
}
|
2021-04-14 13:10:06 -07:00
|
|
|
|
|
|
|
// Stop timer if it was running, removing it from wheel in the process
|
|
|
|
stop_timer_(timer);
|
|
|
|
|
|
|
|
// Insert timer in wheel
|
|
|
|
if (timeout < WHEEL_SIZE) {
|
|
|
|
first_wheel[timeout].push_front(&timer);
|
|
|
|
} else {
|
|
|
|
second_wheel[timeout % WHEEL_SIZE].push_front(&timer);
|
|
|
|
}
|
|
|
|
timer.timeout = timeout;
|
|
|
|
timer.state = timer_impl::running;
|
|
|
|
nof_timers_running_++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// called when user manually stops timer (as an alternative to expiry)
|
|
|
|
void stop_timer_(timer_impl& timer)
|
|
|
|
{
|
|
|
|
if (not timer.is_running()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If already running, need to disconnect it from previous wheel
|
|
|
|
if (timer.timeout < WHEEL_SIZE) {
|
|
|
|
first_wheel[timer.timeout].pop(&timer);
|
|
|
|
} else {
|
|
|
|
second_wheel[timer.timeout % WHEEL_SIZE].pop(&timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
timer.state = timer_impl::stopped;
|
|
|
|
nof_timers_running_--;
|
2020-02-28 09:56:20 -08:00
|
|
|
}
|
|
|
|
|
2021-04-14 13:10:06 -07:00
|
|
|
uint32_t cur_time = 0;
|
|
|
|
size_t nof_timers_ = 0;
|
|
|
|
size_t nof_timers_running_ = 0;
|
|
|
|
std::deque<timer_impl> timer_list;
|
|
|
|
srsran::circular_array<srsran::intrusive_double_linked_list<timer_impl>, WHEEL_SIZE> first_wheel;
|
|
|
|
srsran::circular_array<srsran::intrusive_double_linked_list<timer_impl>, WHEEL_SIZE> second_wheel;
|
|
|
|
mutable std::mutex mutex; // Protect priority queue
|
2019-09-16 11:02:47 -07:00
|
|
|
};
|
|
|
|
|
2020-07-08 16:13:55 -07:00
|
|
|
using unique_timer = timer_handler::unique_timer;
|
|
|
|
|
2021-03-19 03:45:56 -07:00
|
|
|
} // namespace srsran
|
2019-09-04 06:28:09 -07:00
|
|
|
|
2021-03-19 03:45:56 -07:00
|
|
|
#endif // SRSRAN_TIMERS_H
|