#15810·apollo

TimingWheel::Tick() use-after-free crash when Timer::Stop() called concurrently

Author: 1613302984-cellCreated Jul 24, 2026Updated Jul 24, 2026

Describe the bug When stopping a timer while TimingWheel::Tick() is executing, the application crashes with SIGSEGV due to use-after-free. The crash typically occurs in the coroutine scheduler thread (.CRT thread).

Stack trace: #0 0x... in std::function<void()>::operator()() #1 in TimingWheel::Tick() #2 in std::call_once

questions In timing_wheel.cc, the Tick() function captures a raw pointer to the callback function instead of capturing the shared_ptr:

// Bug in timing_wheel.cc:54-75 void TimingWheel::Tick() { auto task = ite->lock(); if (task) { auto* callback = reinterpret_cast<std::function<void()>*>(&(task->callback)); gaea::Async([this, callback] { // BUG: captures raw pointer! if (this->running_) { (*callback)(); // crashes if TimerTask already freed } }); } }

Race condition timeline:

  1. Thread A: Tick() gets callback* pointer
  2. Thread B: Timer::Stop() calls task_.reset(), freeing TimerTask
  3. Thread A: Async task executes (*callback)() on freed memory → CRASH

To Reproduce Long time start and stop application

  1. Create a timer with 20ms period
  2. In a separate thread, repeatedly call Stop() + reset() on the timer
  3. When Tick() and Stop() race, crash occurs

Expected behavior Not crash

Additional context This bug is similar to the classic use-after-free pattern in concurrent systems where a raw pointer is used after the underlying object has been freed. The fix ensures proper lifetime management through shared_ptr reference counting.