@@ -95,8 +95,7 @@ std::chrono::nanoseconds TimersManager::get_head_timeout()
9595 }
9696
9797 std::unique_lock<std::mutex> lock (timers_mutex_);
98- TimersHeap timers_heap = weak_timers_heap_.validate_and_lock ();
99- return this ->get_head_timeout_unsafe (timers_heap);
98+ return this ->get_head_timeout_unsafe ();
10099}
101100
102101void TimersManager::execute_ready_timers ()
@@ -108,10 +107,7 @@ void TimersManager::execute_ready_timers()
108107 }
109108
110109 std::unique_lock<std::mutex> lock (timers_mutex_);
111-
112- TimersHeap timers_heap = weak_timers_heap_.validate_and_lock ();
113- this ->execute_ready_timers_unsafe (timers_heap);
114- weak_timers_heap_.store (timers_heap);
110+ this ->execute_ready_timers_unsafe ();
115111}
116112
117113bool TimersManager::execute_head_timer (
@@ -153,55 +149,77 @@ bool TimersManager::execute_head_timer(
153149 return false ;
154150}
155151
156- void TimersManager::execute_ready_timers_unsafe (TimersHeap & heap )
152+ std::chrono::nanoseconds TimersManager::get_head_timeout_unsafe ( )
157153{
154+ // If we don't have any weak pointer, then we just return maximum timeout
155+ if (weak_timers_heap_.empty ()) {
156+ return MAX_TIME;
157+ }
158+
159+ // Weak heap is not empty, so try to lock the first element
160+ TimerPtr head_timer = weak_timers_heap_.front ().lock ();
161+ // If it is still a valid pointer, it is guaranteed to be the correct head
162+ if (head_timer != nullptr ) {
163+ return head_timer->time_until_trigger ();
164+ }
165+
166+ // If the first elements has expired, we can't make other assumptions on the heap
167+ // and we need to entirely validate it.
168+ TimersHeap locked_heap = weak_timers_heap_.validate_and_lock ();
169+
170+ // NOTE: the following operations will not modify any element in the heap, so we
171+ // don't have to call `weak_timers_heap_.store(locked_heap)` at the end.
172+
173+ if (locked_heap.empty ()) {
174+ return MAX_TIME;
175+ }
176+ return locked_heap.front ()->time_until_trigger ();
177+ }
178+
179+ void TimersManager::execute_ready_timers_unsafe ()
180+ {
181+ // We start by locking the timers
182+ TimersHeap locked_heap = weak_timers_heap_.validate_and_lock ();
183+
158184 // Nothing to do if we don't have any timer
159- if (heap .empty ()) {
185+ if (locked_heap .empty ()) {
160186 return ;
161187 }
162188
163189 // Keep executing timers until they are ready and they were already ready when we started.
164190 // The second check prevents this function from blocking indefinitely if the
165191 // time required for executing the timers is longer than their period.
166192
167- TimerPtr head = heap .front ();
193+ TimerPtr head = locked_heap .front ();
168194 auto start_time = std::chrono::steady_clock::now ();
169195 while (head->is_ready () && this ->timer_was_ready_at_tp (head, start_time)) {
170196 // Execute head timer
171197 head->execute_callback ();
172198 // Executing a timer will result in updating its time_until_trigger, so re-heapify
173- heap .heapify_root ();
199+ locked_heap .heapify_root ();
174200 // Get new head timer
175- head = heap .front ();
201+ head = locked_heap .front ();
176202 }
203+
204+ // After having performed work on the locked heap we reflect the changes to weak one.
205+ // Timers will be already sorted the next time we need them if none went out of scope.
206+ weak_timers_heap_.store (locked_heap);
177207}
178208
179209void TimersManager::run_timers ()
180210{
181- std::chrono::nanoseconds time_to_sleep;
182- {
183- std::unique_lock<std::mutex> lock (timers_mutex_);
184- TimersHeap timers_heap = weak_timers_heap_.validate_and_lock ();
185- time_to_sleep = this ->get_head_timeout_unsafe (timers_heap);
186- }
187-
188211 while (rclcpp::ok (context_) && running_) {
189212 // Lock mutex
190213 std::unique_lock<std::mutex> timers_lock (timers_mutex_);
191214
215+ std::chrono::nanoseconds time_to_sleep = get_head_timeout_unsafe ();
192216 // Wait until timeout or notification that timers have been updated
193217 timers_cv_.wait_for (timers_lock, time_to_sleep, [this ]() {return timers_updated_;});
194218 // Reset timers updated flag
195219 timers_updated_ = false ;
196220
197- // Get ownership of timers
198- TimersHeap timers_heap = weak_timers_heap_.validate_and_lock ();
199221 // Execute timers
200- this ->execute_ready_timers_unsafe (timers_heap);
201- // Store updated order of elements to efficiently re-use it next iteration
202- weak_timers_heap_.store (timers_heap);
203- // Get next timeout
204- time_to_sleep = this ->get_head_timeout_unsafe (timers_heap);
222+ this ->execute_ready_timers_unsafe ();
205223 }
206224
207225 // Make sure the running flag is set to false when we exit from this function
0 commit comments