Skip to content

Commit e6086f2

Browse files
fix TimerQueue shutdown race that left cancelled items in the queue (#1200)
~TimerQueue raised m_finish before cancelAll(), and run() exited its loop without a last checkWork(). A worker woken by cancel() right before the shutdown left the loop with items still queued: the assert fired in Debug (Decorator.DestroyTreeWhileTimerIsPending, ~90% of local runs) and in Release the pending handlers were never invoked with aborted=true. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent b4137a1 commit e6086f2

1 file changed

Lines changed: 8 additions & 1 deletion

File tree

‎include/behaviortree_cpp/utils/timer_queue.h‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,11 @@ class TimerQueue
8585

8686
~TimerQueue()
8787
{
88-
m_finish.store(true);
88+
// Cancel before raising m_finish: once the worker sees the flag, every
89+
// item is already due, so its final checkWork() drains the whole queue.
8990
cancelAll();
91+
m_finish.store(true);
92+
m_checkWork.notify();
9093
if(m_thread.joinable())
9194
{
9295
m_thread.join();
@@ -204,6 +207,10 @@ class TimerQueue
204207
checkWork();
205208
}
206209

210+
// The loop may exit without a last pass (e.g. woken up by cancel() right
211+
// before the shutdown), leaving cancelled items in the queue.
212+
checkWork();
213+
207214
// If we are shutting down, we should not have any items left,
208215
// since the shutdown cancels all items
209216
assert(m_items.size() == 0);

0 commit comments

Comments
 (0)