diff --git a/lib/include/srslte/common/timers.h b/lib/include/srslte/common/timers.h index 756203c86..d5ab59910 100644 --- a/lib/include/srslte/common/timers.h +++ b/lib/include/srslte/common/timers.h @@ -41,63 +41,69 @@ #include "srslte/srslte.h" namespace srslte { - -class timer_callback + +class timer_callback { - public: - virtual void timer_expired(uint32_t timer_id) = 0; -}; - +public: + virtual void timer_expired(uint32_t timer_id) = 0; +}; + class timers { public: class timer { public: - timer(uint32_t id_=0) {id = id_; counter = 0; timeout = 0; running = false; callback = NULL; } - void set(timer_callback *callback_, uint32_t timeout_) { - callback = callback_; - timeout = timeout_; + timer(uint32_t id_ = 0) + { + id = id_; + counter = 0; + timeout = 0; + running = false; + callback = nullptr; + } + + void set(timer_callback* callback_, uint32_t timeout_) + { + callback = callback_; + timeout = timeout_; reset(); } - bool is_running() { - return (counter < timeout) && running; - } - bool is_expired() { - return (timeout > 0) && (counter >= timeout); - } - uint32_t get_timeout() { - return timeout; - } - void reset() { - counter = 0; - } - uint32_t value() { - return counter; - } - void step() { + + bool is_running() { return (counter < timeout) && running; } + + bool is_expired() { return (timeout > 0) && (counter >= timeout); } + + uint32_t get_timeout() { return timeout; } + + void reset() { counter = 0; } + + uint32_t value() { return counter; } + + void step() + { if (running) { counter++; if (is_expired()) { - running = false; + running = false; if (callback) { - callback->timer_expired(id); + callback->timer_expired(id); } - } + } } } - void stop() { - running = false; - } - void run() { - running = true; - } - uint32_t id; - private: - timer_callback *callback; - uint32_t timeout; - uint32_t counter; - bool running; + + void stop() { running = false; } + + void run() { running = true; } + + uint32_t id; + + private: + timer_callback* callback; + uint32_t timeout; + uint32_t counter; + bool running; }; timers(uint32_t nof_timers_) : timer_list(nof_timers_), used_timers(nof_timers_) @@ -107,39 +113,50 @@ public: nof_used_timers = 0; for (uint32_t i = 0; i < nof_timers; i++) { timer_list[i].id = i; - used_timers[i] = false; + used_timers[i] = false; } } - - void step_all() { - for (uint32_t i=0;istep(); } } - void stop_all() { - for (uint32_t i=0;istop(); } } - void run_all() { - for (uint32_t i=0;irun(); } } - void reset_all() { - for (uint32_t i=0;ireset(); } } - timer *get(uint32_t i) { + + timer* get(uint32_t i) + { if (i < nof_timers) { - return &timer_list[i]; + return &timer_list[i]; } else { printf("Error accessing invalid timer %d (Only %d timers available)\n", i, nof_timers); - return NULL; + return NULL; } } - void release_id(uint32_t i) { + + void release_id(uint32_t i) + { if (nof_used_timers > 0 && i < nof_timers) { used_timers[i] = false; nof_used_timers--; @@ -147,12 +164,14 @@ public: ERROR("Error releasing timer id=%d: nof_used_timers=%d, nof_timers=%d\n", i, nof_used_timers, nof_timers); } } - uint32_t get_unique_id() { + + uint32_t get_unique_id() + { if (nof_used_timers >= nof_timers) { ERROR("Error getting unique timer id: no more timers available\n"); return 0; } else { - for (uint32_t i=0;i timer_list; - std::vector used_timers; + uint32_t next_timer; + uint32_t nof_used_timers; + uint32_t nof_timers; + std::vector timer_list; + std::vector used_timers; }; -class timers2 +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 { - timers2* parent; + timer_handler* parent; uint32_t duration = 0, timeout = 0; bool running = false; bool active = false; std::function callback; - explicit timer_impl(timers2* parent_) : parent(parent_) {} - uint32_t id() const { return std::distance((const timers2::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; } + 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; } bool set(uint32_t duration_) { - if (duration_ > std::numeric_limits::max() / 4) { - ERROR("Error: timer durations above %u are not supported\n", std::numeric_limits::max() / 4); + if (duration_ > MAX_TIMER_DURATION) { + ERROR("Error: timer durations above %u are not supported\n", MAX_TIMER_DURATION); return false; } if (not active) { @@ -199,6 +225,7 @@ class timers2 } duration = duration_; running = false; // invalidates any on-going run + timeout = 0; return true; } @@ -218,10 +245,16 @@ class timers2 return; } timeout = parent->cur_time + duration; - parent->running_timers.emplace(parent, id(), timeout); + parent->running_timers.emplace(id(), timeout); running = true; } + void stop() + { + running = false; // invalidates trigger + timeout = 0; // makes is_expired() == false && is_running() == false + } + void clear() { timeout = 0; @@ -229,13 +262,15 @@ class timers2 running = false; active = false; callback = std::function(); - // leave run_id unchanged; + // leave run_id unchanged. Since the timeout was changed, we shall not get spurious triggering } void trigger() { if (is_running()) { - callback(id()); + if (callback) { + callback(id()); + } running = false; } } @@ -245,12 +280,15 @@ public: class unique_timer { public: - explicit unique_timer(timers2* parent_, uint32_t timer_id_) : parent(parent_), timer_id(timer_id_) {} + 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) { @@ -258,8 +296,10 @@ public: impl()->clear(); } } + unique_timer& operator=(const unique_timer&) = delete; - unique_timer& operator =(unique_timer&& other) noexcept + + unique_timer& operator=(unique_timer&& other) noexcept { if (this != &other) { timer_id = other.timer_id; @@ -269,23 +309,30 @@ public: return *this; } - 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(); } - void run() { impl()->run(); } - void stop() { impl()->running = false; } + 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(); } + + void run() { impl()->run(); } + + void stop() { impl()->stop(); } + uint32_t id() const { return timer_id; } private: - timer_impl* impl() { return &parent->timer_list[timer_id]; } + timer_impl* impl() { return &parent->timer_list[timer_id]; } + const timer_impl* impl() const { return &parent->timer_list[timer_id]; } - timers2* parent; - uint32_t timer_id; + timer_handler* parent; + uint32_t timer_id; }; - explicit timers2(uint32_t capacity = 64) + explicit timer_handler(uint32_t capacity = 64) { timer_list.reserve(capacity); // reserve a priority queue using a vector @@ -315,8 +362,8 @@ public: while (not running_timers.empty()) { running_timers.pop(); } - for (uint32_t i = 0; i < timer_list.size(); ++i) { - timer_list[i].running = false; + for (auto& i : timer_list) { + i.running = false; } } @@ -336,6 +383,7 @@ public: } 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; }); @@ -343,23 +391,18 @@ public: private: struct timer_run { - timers2* parent; uint32_t timer_id; uint32_t timeout; - timer_run(timers2* parent_, uint32_t timer_id_, uint32_t timeout_) : - parent(parent_), - timer_id(timer_id_), - timeout(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 greater - uint32_t lim = std::numeric_limits::max(); + // returns true, if other.timeout is lower than timeout, accounting for wrap around if (timeout > other.timeout) { - return (timeout - other.timeout) < lim / 2; + return (timeout - other.timeout) < MAX_TIMER_VALUE / 2; } - return (other.timeout - timeout) > lim / 2; + return (other.timeout - timeout) > MAX_TIMER_VALUE / 2; } }; diff --git a/lib/test/common/timer_test.cc b/lib/test/common/timer_test.cc index 4a50d1d45..d56ecb778 100644 --- a/lib/test/common/timer_test.cc +++ b/lib/test/common/timer_test.cc @@ -23,78 +23,78 @@ #include #define TESTASSERT(cond) \ - { \ + do { \ if (!(cond)) { \ std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ return -1; \ } \ - } + } while (0) using namespace srslte; int timers2_test() { - timers2 timers; - uint32_t dur = 5; + timer_handler timers; + uint32_t dur = 5; { - timers2::unique_timer t = timers.get_unique_timer(); - TESTASSERT(not t.is_running() and not t.is_expired()) - TESTASSERT(t.id() == 0) - timers2::unique_timer t2 = timers.get_unique_timer(); - TESTASSERT(not t2.is_running() and not t2.is_expired()) - TESTASSERT(t2.id() == 1) - TESTASSERT(timers.nof_timers() == 2) + timer_handler::unique_timer t = timers.get_unique_timer(); + TESTASSERT(not t.is_running() and not t.is_expired()); + TESTASSERT(t.id() == 0); + timer_handler::unique_timer t2 = timers.get_unique_timer(); + TESTASSERT(not t2.is_running() and not t2.is_expired()); + TESTASSERT(t2.id() == 1); + TESTASSERT(timers.nof_timers() == 2); // TEST: Run multiple times with the same duration bool callback_called = false; t.set(dur, [&callback_called](int) { callback_called = true; }); - TESTASSERT(timers.get_cur_time() == 0) + TESTASSERT(timers.get_cur_time() == 0); for (uint32_t runs = 0; runs < 3; ++runs) { callback_called = false; - TESTASSERT(not t.is_running()) + TESTASSERT(not t.is_running()); t.run(); - TESTASSERT(t.is_running() and not t.is_expired()) + TESTASSERT(t.is_running() and not t.is_expired()); for (uint32_t i = 0; i < dur; ++i) { timers.step_all(); - TESTASSERT(t.is_running() and not t.is_expired()) + TESTASSERT(t.is_running() and not t.is_expired()); } timers.step_all(); - TESTASSERT(not t.is_running() and t.is_expired()) - TESTASSERT(callback_called) + TESTASSERT(not t.is_running() and t.is_expired()); + TESTASSERT(callback_called); } - TESTASSERT(timers.get_cur_time() == 3 * (1 + dur)) + TESTASSERT(timers.get_cur_time() == 3 * (1 + dur)); // TEST: interrupt a timer. check if callback was called callback_called = false; t.run(); timers.step_all(); - TESTASSERT(t.is_running()) + TESTASSERT(t.is_running()); t.stop(); - TESTASSERT(not t.is_running()) + TESTASSERT(not t.is_running()); for (uint32_t i = 0; i < dur; ++i) { timers.step_all(); - TESTASSERT(not t.is_running()) + TESTASSERT(not t.is_running()); } - TESTASSERT(not callback_called) + TESTASSERT(not callback_called); // TEST: call timer::run() when it is already running. Check if duration gets extended. callback_called = false; t.run(); timers.step_all(); - TESTASSERT(t.is_running()) + TESTASSERT(t.is_running()); t.run(); // re-run for (uint32_t i = 0; i < dur; ++i) { timers.step_all(); - TESTASSERT(t.is_running()) + TESTASSERT(t.is_running()); } timers.step_all(); - TESTASSERT(not t.is_running()) - TESTASSERT(callback_called) + TESTASSERT(not t.is_running()); + TESTASSERT(callback_called); // TEST: ordering of timers is respected - timers2::unique_timer t3 = timers.get_unique_timer(); - TESTASSERT(t3.id() == 2) + timer_handler::unique_timer t3 = timers.get_unique_timer(); + TESTASSERT(t3.id() == 2); int first_id = -1, last_id = -1; auto callback = [&first_id, &last_id](int id) { if (first_id < 0) { @@ -111,27 +111,55 @@ int timers2_test() t3.run(); for (uint32_t i = 0; i < 6; ++i) { timers.step_all(); - TESTASSERT(i >= 4 or t.is_running()) - TESTASSERT(i >= 2 or t2.is_running()) - TESTASSERT(t3.is_running()) + TESTASSERT(i >= 4 or t.is_running()); + TESTASSERT(i >= 2 or t2.is_running()); + TESTASSERT(t3.is_running()); } timers.step_all(); - TESTASSERT(t.is_expired() and t2.is_expired() and t3.is_expired()) - TESTASSERT(first_id == 1) + TESTASSERT(t.is_expired() and t2.is_expired() and t3.is_expired()); + TESTASSERT(first_id == 1); printf("Last timer id=%d\n", last_id); - TESTASSERT(last_id == 2) + TESTASSERT(last_id == 2); } // TEST: timer dtor is called and removes "timer" from "timers" - TESTASSERT(timers.nof_timers() == 0) + TESTASSERT(timers.nof_timers() == 0); - printf("Success\n"); + return SRSLTE_SUCCESS; +} + +int timers2_test2() +{ + /** + * Description: + * - check if we call stop(), the timer does not get into expired state + */ + timer_handler timers; + uint32_t duration = 2; + + auto utimer = timers.get_unique_timer(); + auto utimer2 = timers.get_unique_timer(); + utimer.set(duration); + utimer.run(); + utimer2.set(duration); + utimer2.run(); + TESTASSERT(utimer.is_running() and not utimer.is_expired()); + utimer.stop(); + TESTASSERT(not utimer.is_running() and not utimer.is_expired()); + + for (uint32_t i = 0; i < 5; ++i) { + timers.step_all(); + } + TESTASSERT(not utimer.is_expired()); + TESTASSERT(utimer2.is_expired()); return SRSLTE_SUCCESS; } int main() { - TESTASSERT(timers2_test() == SRSLTE_SUCCESS) + TESTASSERT(timers2_test() == SRSLTE_SUCCESS); + TESTASSERT(timers2_test2() == SRSLTE_SUCCESS); + printf("Success\n"); return 0; }