created a new class for timers. It uses a priority queue to avoid incrementing all timers every tti (step_all() went from O(N) to O(1)), and that is not bounded in number of timers. Not being bounded will be useful for layers that require one or more timers per UE

This commit is contained in:
Francisco Paisana 2019-09-16 19:02:47 +01:00
parent 15bde8660a
commit 349646a9da
3 changed files with 278 additions and 2 deletions

View File

@ -29,10 +29,12 @@
#ifndef SRSLTE_TIMERS_H
#define SRSLTE_TIMERS_H
#include <stdio.h>
#include <functional>
#include <queue>
#include <stdint.h>
#include <vector>
#include <stdio.h>
#include <time.h>
#include <vector>
#include "srslte/srslte.h"
@ -169,6 +171,176 @@ private:
std::vector<bool> used_timers;
};
class timers2
{
struct timer_impl {
timers2* parent;
uint32_t duration = 0, timeout = 0;
bool running = false;
bool active = false;
std::function<void(uint32_t)> 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; }
void set(uint32_t duration_, std::function<void(int)> callback_)
{
if (not active) {
ERROR("Error: setting inactive timer id=%d\n", id());
return;
}
callback = std::move(callback_);
duration = duration_;
running = false; // invalidates any on-going run
}
void set(uint32_t duration_)
{
if (not active) {
ERROR("Error: setting inactive timer id=%d\n", id());
return;
}
duration = duration_;
running = false; // invalidates any on-going run
}
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(parent, id(), timeout);
running = true;
}
void clear()
{
timeout = 0;
duration = 0;
running = false;
active = false;
callback = std::function<void(uint32_t)>();
// leave run_id unchanged;
}
void trigger()
{
if (is_running()) {
callback(id());
running = false;
}
}
};
public:
class unique_timer
{
public:
explicit unique_timer(timers2* 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;
}
void set(uint32_t duration_, const std::function<void(int)>& 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; }
uint32_t id() const { return timer_id; }
private:
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;
};
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 (uint32_t i = 0; i < timer_list.size(); ++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; }
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_)
{
}
bool operator<(const timer_run& other) const { return timeout > other.timeout; }
};
std::vector<timer_impl> timer_list;
std::priority_queue<timer_run> running_timers;
uint32_t cur_time = 0;
};
} // namespace srslte
#endif // SRSLTE_TIMERS_H

View File

@ -75,3 +75,6 @@ add_test(stack_procedure_test stack_procedure_test)
add_executable(queue_test queue_test.cc)
target_link_libraries(queue_test srslte_common ${CMAKE_THREAD_LIBS_INIT})
add_test(queue_test queue_test)
add_executable(timer_test timer_test.cc)
target_link_libraries(timer_test srslte_common)

View File

@ -0,0 +1,101 @@
/*
* 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/.
*
*/
#include "srslte/common/timers.h"
#include <iostream>
#define TESTASSERT(cond) \
{ \
if (!(cond)) { \
std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \
return -1; \
} \
}
using namespace srslte;
int timers2_test()
{
timers2 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)
// TEST: Run multiple times with the same duration
bool callback_called = false;
t.set(dur, [&callback_called](int) { callback_called = true; });
for (uint32_t runs = 0; runs < 3; ++runs) {
callback_called = false;
TESTASSERT(not t.is_running())
t.run();
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())
}
timers.step_all();
TESTASSERT(not t.is_running() and t.is_expired())
TESTASSERT(callback_called)
}
// TEST: interrupt a timer. check if callback was called
callback_called = false;
t.run();
timers.step_all();
TESTASSERT(t.is_running())
t.stop();
TESTASSERT(not t.is_running())
for (uint32_t i = 0; i < dur; ++i) {
timers.step_all();
TESTASSERT(not t.is_running())
}
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())
t.run(); // re-run
for (uint32_t i = 0; i < dur; ++i) {
timers.step_all();
TESTASSERT(t.is_running())
}
timers.step_all();
TESTASSERT(not t.is_running())
TESTASSERT(callback_called)
printf("Success\n");
return SRSLTE_SUCCESS;
}
int main()
{
TESTASSERT(timers2_test() == SRSLTE_SUCCESS)
return 0;
}