src/ssx/ssx/ssx_timer_core.c
// The tickless timer mechanism has timed out.  Note that due to timer
// deletions and other factors, there may not actually be a timer in the queue
// that has timed out - but it doesn't matter (other than for efficiency).
//
// Noncritical interrupts are (must be) disabled at entry, and this invariant
// is checked. This routine must not be entered reentrantly.
//
// First, time out any timers that have expired.  Timers in the queue are
// unordered, so we have to check every one.  Since passing through the
// loop takes time, we may have to make multiple passes until we know
// that there are no timers in the queue that have already timed
// out. Note that it would also work to only go through the loop once and
// let the hardware scheduler take care of looping, but that would imply
// more overhead than the current implementation.
//
// On each pass through the loop tq->next_timeout computes the minimum timeout
// of events remaining in the queue.  This is the only part of the kernel that
// searches a list of indefinite length. Kernel interrupt latency is mitigated
// by running callbacks with interrupts disabled either during or after the
// call for timed out events, and also after every check for events that have
// not timed out.
//
// Because interrupt preemption is enabled during processing, and preempting
// handlers may invoke time queue operations, we need to establish a pointer
// to the next entry to be examined (tq->cursor) before enabling interupts.
// It's possible that this pointer will be changed by other interrupt handlers
// that cancel the timer pointed to by tq->cursor.
//
// The main loop iterates on the SsxDeque form of the time queue, casting each
// element back up to the SsxTimer as it is processed.
The tickless timer mechanism has timed out.  Note that due to timer
deletions and other factors, there may not actually be a timer in the queue
that has timed out - but it doesn't matter (other than for efficiency).

Noncritical interrupts are (must be) disabled at entry, and this invariant
is checked. This routine must not be entered reentrantly.

First, time out any timers that have expired.  Timers in the queue are
unordered, so we have to check every one.  Since passing through the
loop takes time, we may have to make multiple passes until we know
that there are no timers in the queue that have already timed
out. Note that it would also work to only go through the loop once and
let the hardware scheduler take care of looping, but that would imply
more overhead than the current implementation.

On each pass through the loop tq->next_timeout computes the minimum timeout
of events remaining in the queue.  This is the only part of the kernel that
searches a list of indefinite length. Kernel interrupt latency is mitigated
by running callbacks with interrupts disabled either during or after the
call for timed out events, and also after every check for events that have
not timed out.

Because interrupt preemption is enabled during processing, and preempting
handlers may invoke time queue operations, we need to establish a pointer
to the next entry to be examined (tq->cursor) before enabling interupts.
It's possible that this pointer will be changed by other interrupt handlers
that cancel the timer pointed to by tq->cursor.

The main loop iterates on the SsxDeque form of the time queue, casting each
element back up to the SsxTimer as it is processed.
The tickless timer mechanism has timed out . Note that due to timer deletions and other factors there may not actually be a timer in the queue that has timed out - but it doesn ' t matter ( other than for efficiency ) . Noncritical interrupts are ( must be ) disabled at entry and this invariant is checked . This routine must not be entered reentrantly . First time out any timers that have expired . Timers in the queue are unordered so we have to check every one . Since passing through the loop takes time we may have to make multiple passes until we know that there are no timers in the queue that have already timed out . Note that it would also work to only go through the loop once and let the hardware scheduler take care of looping but that would imply more overhead than the current implementation . On each pass through the loop tq - > next_timeout computes the minimum timeout of events remaining in the queue . This is the only part of the kernel that searches a list of indefinite length . Kernel interrupt latency is mitigated by running callbacks with interrupts disabled either during or after the call for timed out events and also after every check for events that have not timed out . Because interrupt preemption is enabled during processing and preempting handlers may invoke time queue operations we need to establish a pointer to the next entry to be examined ( tq - > cursor ) before enabling interupts . It ' s possible that this pointer will be changed by other interrupt handlers that cancel the timer pointed to by tq - > cursor . The main loop iterates on the SsxDeque form of the time queue casting each element back up to the SsxTimer as it is processed .
void
__ssx_timer_handler()
{
    SsxTimeQueue* tq;
    SsxTimebase now;
    SsxTimer* timer;
    SsxDeque* timer_deque;
    SsxTimerCallback callback;
    tq = &__ssx_time_queue;
    if (SSX_ERROR_CHECK_KERNEL)
    {
        if (tq->cursor != 0)
        {
            SSX_PANIC(SSX_TIMER_HANDLER_INVARIANT);
        }
    }
    while ((now = ssx_timebase_get()) >= tq->next_timeout)
    {
        tq->next_timeout = SSX_TIMEBASE_MAX;
        timer_deque = ((SsxDeque*)tq)->next;
        while (timer_deque != (SsxDeque*)tq)
        {
            timer = (SsxTimer*)timer_deque;
            tq->cursor = timer_deque->next;
            if (timer->timeout <= now)
            {
                 The callback may be made with interrupt preemption enabled
                 or disabled.  However to mitigate kernel interrupt latency
                 we go ahead and open up to interrupts after the callback if
                 the callback itself was not preemptible.
                if (timer->period == 0)
                {
                    ssx_deque_delete(timer_deque);
                }
                else
                {
                    timer->timeout += timer->period;
                    tq->next_timeout = MIN(timer->timeout, tq->next_timeout);
                }
                callback = timer->callback;
                if (callback)
                {
                    if (timer->options & SSX_TIMER_CALLBACK_PREEMPTIBLE)
                    {
                        ssx_interrupt_preemption_enable();
                        callback(timer->arg);
                    }
                    else
                    {
                        callback(timer->arg);
                        ssx_interrupt_preemption_enable();
                    }
                }
                ssx_interrupt_preemption_disable();
            }
            else
            {
                 This timer has not timed out.  Its timeout will simply
                 participate in the computation of the next timeout.  For
                 interrupt latency reasons we always allow a period of
                 interrupt preemption.
                tq->next_timeout = MIN(timer->timeout, tq->next_timeout);
                ssx_interrupt_preemption_enable();
                ssx_interrupt_preemption_disable();
            }
            timer_deque = tq->cursor;
        }
    }
    tq->cursor = 0;
     Finally, reschedule the next timeout
    __ssx_schedule_hardware_timeout(tq->next_timeout);
}
void __ssx_timer_handler ( ) { SsxTimeQueue * tq ; SsxTimebase now ; SsxTimer * timer ; SsxDeque * timer_deque ; SsxTimerCallback callback ; tq = & __ssx_time_queue ; if ( SSX_ERROR_CHECK_KERNEL ) { if ( tq -> cursor != 0 ) { SSX_PANIC ( SSX_TIMER_HANDLER_INVARIANT ) ; } } while ( ( now = ssx_timebase_get ( ) ) >= tq -> next_timeout ) { tq -> next_timeout = SSX_TIMEBASE_MAX ; timer_deque = ( ( SsxDeque * ) tq ) -> next ; while ( timer_deque != ( SsxDeque * ) tq ) { timer = ( SsxTimer * ) timer_deque ; tq -> cursor = timer_deque -> next ; if ( timer -> timeout <= now ) { if ( timer -> period == 0 ) { ssx_deque_delete ( timer_deque ) ; } else { timer -> timeout += timer -> period ; tq -> next_timeout = MIN ( timer -> timeout , tq -> next_timeout ) ; } callback = timer -> callback ; if ( callback ) { if ( timer -> options & SSX_TIMER_CALLBACK_PREEMPTIBLE ) { ssx_interrupt_preemption_enable ( ) ; callback ( timer -> arg ) ; } else { callback ( timer -> arg ) ; ssx_interrupt_preemption_enable ( ) ; } } ssx_interrupt_preemption_disable ( ) ; } else { tq -> next_timeout = MIN ( timer -> timeout , tq -> next_timeout ) ; ssx_interrupt_preemption_enable ( ) ; ssx_interrupt_preemption_disable ( ) ; } timer_deque = tq -> cursor ; } } tq -> cursor = 0 ; __ssx_schedule_hardware_timeout ( tq -> next_timeout ) ; }
The tickless timer mechanism has timed out.
The tickless timer mechanism has timed out .
// The timer timed out.  It is removed from the queue unless // it is a peridic timer that needs to be rescheduled.  We do // rescheduling here in the critical section to correctly // handle timers whose callbacks may cancel the timer.  The // timer is rescheduled in absolute time. // // The callback may be made with interrupt preemption enabled // or disabled.  However to mitigate kernel interrupt latency // we go ahead and open up to interrupts after the callback if // the callback itself was not preemptible. // This timer has not timed out.  Its timeout will simply // participate in the computation of the next timeout.  For // interrupt latency reasons we always allow a period of // interrupt preemption. // Finally, reschedule the next timeout