99 */
1010
1111#include " srsran/support/timers.h"
12+ #include " cameron314/concurrentqueue.h"
1213#include " srsran/srslog/srslog.h"
1314
1415using namespace srsran ;
@@ -62,22 +63,56 @@ void timer_manager::timer_frontend::stop()
6263 parent.push_timer_command (cmd_t {id, cmd_id.load (std::memory_order_relaxed), cmd_t ::stop});
6364}
6465
65- timer_manager::timer_manager (size_t capacity) : logger(srslog::fetch_basic_logger(" ALL" )), time_wheel(WHEEL_SIZE)
66+ //
67+
68+ class timer_manager ::unique_timer_pool
69+ {
70+ public:
71+ unique_timer_pool (timer_manager& parent, unsigned capacity) : free_list(capacity) {}
72+
73+ void push (timer_manager::timer_frontend* obj) { free_list.enqueue (obj); }
74+
75+ timer_manager::timer_frontend* pop ()
76+ {
77+ timer_manager::timer_frontend* ret;
78+ if (free_list.try_dequeue (ret)) {
79+ return ret;
80+ }
81+ return nullptr ;
82+ }
83+
84+ size_t size_approx () const { return free_list.size_approx (); }
85+
86+ private:
87+ // List of timer_handle objects in timer_list that are currently not allocated.
88+ moodycamel::ConcurrentQueue<timer_manager::timer_frontend*> free_list;
89+ };
90+
91+ //
92+
93+ timer_manager::timer_manager (size_t capacity) :
94+ logger(srslog::fetch_basic_logger(" ALL" )),
95+ timer_pool(std::make_unique<unique_timer_pool>(*this , capacity)),
96+ time_wheel(WHEEL_SIZE)
6697{
6798 // Pre-reserve timers.
68- while (timer_list.size () < capacity) {
69- timer_list.emplace_back ().frontend = std::make_unique<timer_frontend>(*this , (timer_id_t )next_timer_id++);
99+ while (timers.size () < capacity) {
100+ timers.emplace_back ().frontend =
101+ std::make_unique<timer_frontend>(*this , (timer_id_t )next_timer_id.fetch_add (1 , std::memory_order_relaxed));
70102 }
71103
72- // Push to free list in reverse order to keep ascending ids .
73- for (auto i = timer_list. rbegin (), e = timer_list. rend (); i != e; ++i ) {
74- free_list. emplace_back (i-> frontend .get ());
104+ // Push to free list in ascending id order .
105+ for (const auto & t : timers ) {
106+ timer_pool-> push (t. frontend .get ());
75107 }
108+
76109 const uint16_t cmds_capacity = 16384 ;
77110 pending_cmds.reserve (cmds_capacity);
78111 cmds_to_process.reserve (cmds_capacity);
79112}
80113
114+ timer_manager::~timer_manager () {}
115+
81116void timer_manager::tick ()
82117{
83118 // Extract new commands from the timer front-ends to process in this tick.
@@ -97,7 +132,7 @@ void timer_manager::tick()
97132
98133 // Existing timer.
99134 const cmd_t & cmd = std::get<cmd_t >(event);
100- timer_handle& timer = timer_list [static_cast <unsigned >(cmd.id )];
135+ timer_handle& timer = timers [static_cast <size_t >(cmd.id )];
101136
102137 // Update the timer backend cmd_id to match frontend.
103138 timer.backend .cmd_id = cmd.cmd_id ;
@@ -125,7 +160,7 @@ void timer_manager::tick()
125160 // Iterate intrusive linked list of running timers with same wheel index.
126161 for (auto it = wheel_list.begin (); it != wheel_list.end ();) {
127162 srsran_assert (it->frontend != nullptr , " invalid state of timer in timer wheel" );
128- timer_handle& timer = timer_list [static_cast <unsigned >(it->frontend ->id )];
163+ timer_handle& timer = timers [static_cast <size_t >(it->frontend ->id )];
129164 // We move iterator already, in case, the current timer gets removed from the linked list.
130165 ++it;
131166
@@ -148,12 +183,11 @@ void timer_manager::push_timer_command(cmd_t cmd)
148183void timer_manager::create_timer_handle (std::unique_ptr<timer_frontend> timer)
149184{
150185 auto timer_idx = static_cast <unsigned >(timer->id );
151- srsran_assert (timer_idx >= timer_list.size () or timer_list[timer_idx].frontend == nullptr ,
152- " Duplicate timer id detection" );
153- if (timer_idx >= timer_list.size ()) {
154- timer_list.resize (timer_idx + 1 );
186+ srsran_assert (timer_idx >= timers.size () or timers[timer_idx].frontend == nullptr , " Duplicate timer id detection" );
187+ if (timer_idx >= timers.size ()) {
188+ timers.resize (timer_idx + 1 );
155189 }
156- timer_list [timer_idx].frontend = std::move (timer);
190+ timers [timer_idx].frontend = std::move (timer);
157191}
158192
159193void timer_manager::start_timer_backend (timer_handle& timer, unsigned duration)
@@ -222,7 +256,7 @@ bool timer_manager::try_stop_timer_backend(timer_handle& timer, bool expiry_reas
222256void timer_manager::handle_postponed_timeouts ()
223257{
224258 while (not failed_to_trigger_timers.empty ()) {
225- timer_handle& timer = timer_list[ static_cast < unsigned >( failed_to_trigger_timers.front ().first ) ];
259+ timer_handle& timer = timers[( size_t ) failed_to_trigger_timers.front ().first ];
226260 cmd_id_t prev_cmd_id = failed_to_trigger_timers.front ().second ;
227261
228262 if (timer.backend .cmd_id == prev_cmd_id and
@@ -256,41 +290,31 @@ void timer_manager::destroy_timer_backend(timer_handle& timer)
256290 timer.backend .state = state_t ::stopped;
257291 timer.backend .timeout = 0 ;
258292 // Add timer handle in free list.
259- std::lock_guard<std::mutex> lock (free_list_mutex);
260- free_list.emplace_back (timer.frontend .get ());
293+ timer_pool->push (timer.frontend .get ());
261294}
262295
263296timer_manager::timer_frontend& timer_manager::create_frontend_timer (task_executor& exec)
264297{
265- // Allocate timer frontend with unique timer id.
266- timer_id_t id = timer_id_t ::invalid;
267- timer_frontend* cached_timer = nullptr ;
268- {
269- std::lock_guard<std::mutex> lock (free_list_mutex);
270- if (!free_list.empty ()) {
271- cached_timer = free_list.back ();
272- free_list.pop_back ();
273- } else {
274- // Need to allocate new timer.
275- id = (timer_id_t )next_timer_id++;
276- }
277- }
278-
279- // In case it fails to reuse a cached timer frontend object. Need to create a new one.
280- if (cached_timer == nullptr ) {
281- auto new_handle = std::make_unique<timer_frontend>(*this , id);
282- new_handle->exec = &exec;
283- cached_timer = new_handle.get ();
284-
285- // Forward created timer handle to the backend.
286- {
287- std::lock_guard<std::mutex> lock (cmd_mutex);
288- pending_cmds.emplace_back (std::move (new_handle));
289- }
290- } else {
298+ // Pop cached timer from pool.
299+ timer_frontend* cached_timer = timer_pool->pop ();
300+ if (cached_timer != nullptr ) {
291301 srsran_assert (cached_timer->exec == nullptr , " Reassignment of timer detected" );
292302 // Assign new executor to created timer.
293303 cached_timer->exec = &exec;
304+ return *cached_timer;
305+ }
306+
307+ // In case it fails to reuse a cached timer frontend object. Need to create a new one.
308+ const auto id = (timer_id_t )next_timer_id.fetch_add (1 , std::memory_order_relaxed);
309+ auto new_handle = std::make_unique<timer_frontend>(*this , id);
310+ new_handle->exec = &exec;
311+ cached_timer = new_handle.get ();
312+
313+ // Forward created timer handle to the backend.
314+ // Note: This cannot fail, otherwise the created "id" cannot be reused.
315+ {
316+ std::lock_guard<std::mutex> lock (cmd_mutex);
317+ pending_cmds.emplace_back (std::move (new_handle));
294318 }
295319
296320 return *cached_timer;
@@ -303,6 +327,5 @@ unique_timer timer_manager::create_unique_timer(task_executor& exec)
303327
304328size_t timer_manager::nof_timers () const
305329{
306- std::lock_guard<std::mutex> lock (free_list_mutex);
307- return timer_list.size () - free_list.size ();
330+ return timers.size () - std::min (timers.size (), timer_pool->size_approx ());
308331}
0 commit comments