alarm: reposition reprogrammed alarms in queue

The alarm library failed to handle the case properly where an already
scheduled alarm gets rescheduled before it triggered. Even though the
attempt to reschedule the alarm (twice insertion into alarm queue) was
detected, this condition resulted in the mere modification of the
alarm's parameters while keeping the alarm's queue position unchanged.
This, in turn, may violate the invariant that all enqueued alarm objects
are strictly ordered by their deadlines. The patch handles the case by
dequeuing the alarm object before reinserting it into the queue at the
right position.

Fixes #1646
This commit is contained in:
Norman Feske 2015-08-13 22:53:03 +02:00 committed by Christian Helmuth
parent e410ecc995
commit 26524edbf4
2 changed files with 37 additions and 6 deletions

View File

@ -92,6 +92,11 @@ class Genode::Alarm_scheduler
*/
Alarm *_get_pending_alarm();
/**
* Assign timeout values to alarm object and add it to the schedule
*/
void _setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time deadline);
public:
Alarm_scheduler() : _head(0), _now(0) { }

View File

@ -19,9 +19,10 @@ using namespace Genode;
void Alarm_scheduler::_unsynchronized_enqueue(Alarm *alarm)
{
/* do not enqueue twice */
if (alarm->_active)
if (alarm->_active) {
PERR("trying to insert the same alarm twice!");
return;
}
alarm->_active++;
@ -146,12 +147,27 @@ void Alarm_scheduler::handle(Alarm::Time curr_time)
}
void Alarm_scheduler::_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time deadline)
{
/*
* If the alarm is already present in the queue, re-consider its queue
* position because its deadline might have changed. I.e., if an alarm is
* rescheduled with a new timeout before the original timeout triggered.
*/
if (alarm._active)
_unsynchronized_dequeue(&alarm);
alarm._assign(period, deadline, this);
_unsynchronized_enqueue(&alarm);
}
void Alarm_scheduler::schedule_absolute(Alarm *alarm, Alarm::Time timeout)
{
Lock::Guard alarm_list_lock_guard(_lock);
alarm->_assign(0, timeout, this);
_unsynchronized_enqueue(alarm);
_setup_alarm(*alarm, 0, timeout);
}
@ -159,9 +175,19 @@ void Alarm_scheduler::schedule(Alarm *alarm, Alarm::Time period)
{
Lock::Guard alarm_list_lock_guard(_lock);
/*
* Refuse to schedule a periodic timeout of 0 because it would trigger
* infinitely in the 'handle' function. To account for the case where the
* alarm object was already scheduled, we make sure to remove it from the
* queue.
*/
if (period == 0) {
_unsynchronized_dequeue(alarm);
return;
}
/* first deadline is overdue */
alarm->_assign(period, _now, this);
_unsynchronized_enqueue(alarm);
_setup_alarm(*alarm, period, _now);
}