/* * Copyright 2013-2019 Software Radio Systems Limited * * This file is part of srsLTE. * * srsLTE is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * srsLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * A copy of the GNU Affero General Public License can be found in * the LICENSE file in the top-level directory of this distribution * and at http://www.gnu.org/licenses/. * */ /****************************************************************************** * File: timers.h * Description: Manually incremented timers. Call a callback function upon * expiry. * Reference: *****************************************************************************/ #ifndef SRSLTE_TIMERS_H #define SRSLTE_TIMERS_H #include #include #include #include #include #include #include #include #include "srslte/srslte.h" namespace srslte { class timer_callback { public: virtual void timer_expired(uint32_t timer_id) = 0; }; class timer_handler { constexpr static uint32_t MAX_TIMER_DURATION = std::numeric_limits::max() / 4; constexpr static uint32_t MAX_TIMER_VALUE = std::numeric_limits::max() / 2; struct timer_impl { timer_handler* parent; uint32_t duration = 0, timeout = 0; bool running = false; bool active = false; std::function callback; explicit timer_impl(timer_handler* parent_) : parent(parent_) {} uint32_t id() const { return std::distance((const timer_handler::timer_impl*)&parent->timer_list[0], this); } bool is_running() const { return active and running and timeout > 0; } bool is_expired() const { return active and not running and timeout > 0 and timeout <= parent->cur_time; } uint32_t value() const { return parent->cur_time - (timeout - duration); } bool set(uint32_t duration_) { if (duration_ > MAX_TIMER_DURATION) { ERROR("Error: timer durations above %u are not supported\n", MAX_TIMER_DURATION); return false; } if (not active) { ERROR("Error: setting inactive timer id=%d\n", id()); return false; } duration = duration_; if (is_running()) { // if already running, just extends timer lifetime run(); } return true; } bool set(uint32_t duration_, std::function callback_) { if (set(duration_)) { callback = std::move(callback_); return true; } return false; } void run() { if (not active) { ERROR("Error: calling run() for inactive timer id=%d\n", id()); return; } timeout = parent->cur_time + duration; parent->running_timers.emplace(id(), timeout); running = true; } void stop() { running = false; // invalidates trigger if (not is_expired()) { timeout = 0; // if it has already expired, then do not alter is_expired() state } } void clear() { stop(); duration = 0; active = false; callback = std::function(); // leave run_id unchanged. Since the timeout was changed, we shall not get spurious triggering } void trigger() { if (is_running()) { running = false; if (callback) { callback(id()); } } } }; public: class unique_timer { public: unique_timer() : parent(nullptr), timer_id(std::numeric_limits::max()) {} explicit unique_timer(timer_handler* parent_, uint32_t timer_id_) : parent(parent_), timer_id(timer_id_) {} unique_timer(const unique_timer&) = delete; unique_timer(unique_timer&& other) noexcept : parent(other.parent), timer_id(other.timer_id) { other.parent = nullptr; } ~unique_timer() { if (parent) { // does not call callback impl()->clear(); } } unique_timer& operator=(const unique_timer&) = delete; unique_timer& operator=(unique_timer&& other) noexcept { if (this != &other) { timer_id = other.timer_id; parent = other.parent; other.parent = nullptr; } return *this; } bool is_valid() const { return parent != nullptr; } void set(uint32_t duration_, const std::function& callback_) { impl()->set(duration_, callback_); } void set(uint32_t duration_) { impl()->set(duration_); } bool is_running() const { return impl()->is_running(); } bool is_expired() const { return impl()->is_expired(); } uint32_t value() const { return impl()->value(); } void run() { impl()->run(); } void stop() { impl()->stop(); } void release() { impl()->clear(); parent = nullptr; } uint32_t id() const { return timer_id; } uint32_t duration() const { return impl()->duration; } private: timer_impl* impl() { return &parent->timer_list[timer_id]; } const timer_impl* impl() const { return &parent->timer_list[timer_id]; } timer_handler* parent; uint32_t timer_id; }; explicit timer_handler(uint32_t capacity = 64) { timer_list.reserve(capacity); // reserve a priority queue using a vector std::vector v; v.reserve(capacity); std::priority_queue q(std::less(), std::move(v)); running_timers = std::move(q); } void step_all() { cur_time++; while (not running_timers.empty() and cur_time >= running_timers.top().timeout) { timer_impl* ptr = &timer_list[running_timers.top().timer_id]; // if the timer_run and timer_impl timeouts do not match, it means that timer_impl::timeout was overwritten. // in such case, do not trigger if (ptr->timeout == running_timers.top().timeout) { ptr->trigger(); } running_timers.pop(); } } void stop_all() { // does not call callback while (not running_timers.empty()) { running_timers.pop(); } for (auto& i : timer_list) { i.running = false; } } unique_timer get_unique_timer() { uint32_t i = 0; for (; i < timer_list.size(); ++i) { if (not timer_list[i].active) { break; } } if (i == timer_list.size()) { timer_list.emplace_back(this); } timer_list[i].active = true; return unique_timer(this, i); } uint32_t get_cur_time() const { return cur_time; } uint32_t nof_timers() const { return std::count_if(timer_list.begin(), timer_list.end(), [](const timer_impl& t) { return t.active; }); } uint32_t nof_running_timers() const { return std::count_if(timer_list.begin(), timer_list.end(), [](const timer_impl& t) { return t.is_running(); }); } private: struct timer_run { uint32_t timer_id; uint32_t timeout; timer_run(uint32_t timer_id_, uint32_t timeout_) : timer_id(timer_id_), timeout(timeout_) {} bool operator<(const timer_run& other) const { // returns true, if other.timeout is lower than timeout, accounting for wrap around if (timeout > other.timeout) { return (timeout - other.timeout) < MAX_TIMER_VALUE / 2; } return (other.timeout - timeout) > MAX_TIMER_VALUE / 2; } }; std::vector timer_list; std::priority_queue running_timers; uint32_t cur_time = 0; }; } // namespace srslte #endif // SRSLTE_TIMERS_H