mirror of
https://github.com/RGBCube/serenity
synced 2025-07-10 04:07:35 +00:00
Kernel: Do not cancel stale timers when servicing sys$alarm
The sys$alarm() syscall has logic to cache a m_alarm_timer to avoid allocating a new timer for every call to alarm. Unfortunately that logic was broken, and there were conditions in which we could have a timer allocated, but it was no longer on the timer queue, and we would attempt to cancel that timer again resulting in an infinite loop waiting for the timers callback to fire. To fix this, we need to track if a timer is currently in use or not, allowing us to avoid attempting to cancel inactive timers. Luke and Tom did the initial investigation, I just happened to have time to write a repro and attempt a fix, so I'm adding them as the as co-authors of this commit. Co-authored-by: Luke <luke.wilde@live.co.uk> Co-authored-by: Tom <tomut@yahoo.com>
This commit is contained in:
parent
2dd6d2121a
commit
fc91eb365d
3 changed files with 28 additions and 6 deletions
|
@ -90,6 +90,7 @@ void TimerQueue::add_timer_locked(NonnullRefPtr<Timer> timer)
|
|||
|
||||
timer->clear_cancelled();
|
||||
timer->clear_callback_finished();
|
||||
timer->set_in_use();
|
||||
|
||||
auto& queue = queue_for_timer(*timer);
|
||||
if (queue.list.is_empty()) {
|
||||
|
@ -189,12 +190,23 @@ bool TimerQueue::cancel_timer(TimerId id)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool TimerQueue::cancel_timer(Timer& timer)
|
||||
bool TimerQueue::cancel_timer(Timer& timer, bool* was_in_use)
|
||||
{
|
||||
bool in_use = timer.is_in_use();
|
||||
if (was_in_use)
|
||||
*was_in_use = in_use;
|
||||
|
||||
// If the timer isn't in use, the cancellation is a no-op.
|
||||
if (!in_use) {
|
||||
VERIFY(!timer.m_list_node.is_in_list());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool did_already_run = timer.set_cancelled();
|
||||
auto& timer_queue = queue_for_timer(timer);
|
||||
|
||||
if (!did_already_run) {
|
||||
timer.clear_in_use();
|
||||
|
||||
ScopedSpinLock lock(g_timerqueue_lock);
|
||||
if (timer_queue.list.contains(timer)) {
|
||||
// The timer has not fired, remove it
|
||||
|
@ -218,7 +230,7 @@ bool TimerQueue::cancel_timer(Timer& timer)
|
|||
while (!timer.is_callback_finished())
|
||||
Processor::wait_check();
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void TimerQueue::remove_timer_locked(Queue& queue, Timer& timer)
|
||||
|
@ -265,6 +277,7 @@ void TimerQueue::fire()
|
|||
ScopedSpinLock lock(g_timerqueue_lock);
|
||||
m_timers_executing.remove(*timer);
|
||||
}
|
||||
timer->clear_in_use();
|
||||
timer->set_callback_finished();
|
||||
// Drop the reference we added when queueing the timer
|
||||
timer->unref();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue