/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    timersup.c

Abstract:

    This module contains the support routines for the timer object. It
    contains functions to insert and remove from the timer queue.

Author:

    David N. Cutler (davec) 13-Mar-1989

Environment:

    Kernel mode only.

Revision History:

--*/

#include "ki.h"

//
// Define forward referenced function prototypes.
//

LOGICAL
FASTCALL
KiInsertTimerTable (
    LARGE_INTEGER Interval,
    LARGE_INTEGER CurrentTime,
    IN PKTIMER Timer
    );

LOGICAL
FASTCALL
KiInsertTreeTimer (
    IN PKTIMER Timer,
    IN LARGE_INTEGER Interval
    )

/*++

Routine Description:

    This function inserts a timer object in the timer queue.

    N.B. This routine assumes that the dispatcher data lock has been acquired.

Arguments:

    Timer - Supplies a pointer to a dispatcher object of type timer.

    Interval - Supplies the absolute or relative time at which the time
        is to expire.

Return Value:

    If the timer is inserted in the timer tree, than a value of TRUE is
    returned. Otherwise, a value of FALSE is returned.

--*/

{

    LARGE_INTEGER CurrentTime;
    LARGE_INTEGER SystemTime;
    LARGE_INTEGER TimeDifference;

    //
    // Clear the signal state of timer if the timer period is zero and set
    // the inserted state to TRUE.
    //

    Timer->Header.Inserted = TRUE;
    Timer->Header.Absolute = FALSE;
    if (Timer->Period == 0) {
        Timer->Header.SignalState = FALSE;
    }

    //
    // If the specified interval is not a relative time (i.e., is an absolute
    // time), then convert it to relative time.
    //

    if (Interval.HighPart >= 0) {
        KiQuerySystemTime(&SystemTime);
        TimeDifference.QuadPart = SystemTime.QuadPart - Interval.QuadPart;

        //
        // If the resultant relative time is greater than or equal to zero,
        // then the timer has already expired.
        //

        if (TimeDifference.HighPart >= 0) {
            Timer->Header.SignalState = TRUE;
            Timer->Header.Inserted = FALSE;
            return FALSE;
        }

        Interval = TimeDifference;
        Timer->Header.Absolute = TRUE;
    }

    //
    // Get the current interrupt time, insert the timer in the timer table,
    // and return the inserted state.
    //

    KiQueryInterruptTime(&CurrentTime);
    return KiInsertTimerTable(Interval, CurrentTime, Timer);
}

LOGICAL
FASTCALL
KiReinsertTreeTimer (
    IN PKTIMER Timer,
    IN ULARGE_INTEGER DueTime
    )

/*++

Routine Description:

    This function reinserts a timer object in the timer queue.

    N.B. This routine assumes that the dispatcher data lock has been acquired.

Arguments:

    Timer - Supplies a pointer to a dispatcher object of type timer.

    DueTime - Supplies the absolute time the timer is to expire.

Return Value:

    If the timer is inserted in the timer tree, than a value of TRUE is
    returned. Otherwise, a value of FALSE is returned.

--*/

{

    LARGE_INTEGER CurrentTime;
    LARGE_INTEGER Interval;

    //
    // Clear the signal state of timer if the timer period is zero and set
    // the inserted state to TRUE.
    //

    Timer->Header.Inserted = TRUE;
    if (Timer->Period == 0) {
        Timer->Header.SignalState = FALSE;
    }

    //
    // Compute the interval between the current time and the due time.
    // If the resultant relative time is greater than or equal to zero,
    // then the timer has already expired.
    //

    KiQueryInterruptTime(&CurrentTime);
    Interval.QuadPart = CurrentTime.QuadPart - DueTime.QuadPart;
    if (Interval.QuadPart >= 0) {
        Timer->Header.SignalState = TRUE;
        Timer->Header.Inserted = FALSE;
        return FALSE;
    }

    //
    // Insert the timer in the timer table and return the inserted state.
    //

    return KiInsertTimerTable(Interval, CurrentTime, Timer);
}

LOGICAL
FASTCALL
KiInsertTimerTable (
    LARGE_INTEGER Interval,
    LARGE_INTEGER CurrentTime,
    IN PKTIMER Timer
    )

/*++

Routine Description:

    This function inserts a timer object in the timer table.

    N.B. This routine assumes that the dispatcher data lock has been acquired.

Arguments:

    Interval - Supplies the relative timer before the timer is to expire.

    CurrentTime - supplies the current interrupt time.

    Timer - Supplies a pointer to a dispatcher object of type timer.

Return Value:

    If the timer is inserted in the timer tree, than a value of TRUE is
    returned. Otherwise, a value of FALSE is returned.

--*/

{

    ULONG Index;
    PLIST_ENTRY ListHead;
    PLIST_ENTRY NextEntry;
    PKTIMER NextTimer;
    ULONG SearchCount;

    //
    // Compute the timer table index and set the timer expiration time.
    //

    Index = KiComputeTimerTableIndex(Interval, CurrentTime, Timer);

    //
    // If the timer is due before the first entry in the computed list
    // or the computed list is empty, then insert the timer at the front
    // of the list and check if the timer has already expired. Otherwise,
    // insert then timer in the sorted order of the list searching from
    // the back of the list forward.
    //
    // N.B. The sequence of operations below is critical to avoid the race
    //      condition that exists between this code and the clock interrupt
    //      code that examines the timer table lists to detemine when timers
    //      expire.
    //

    ListHead = &KiTimerTableListHead[Index];
    NextEntry = ListHead->Blink;

#if DBG

    SearchCount = 0;

#endif

    while (NextEntry != ListHead) {

        //
        // Compute the maximum search count.
        //

#if DBG

        SearchCount += 1;
        if (SearchCount > KiMaximumSearchCount) {
            KiMaximumSearchCount = SearchCount;
        }

#endif

        NextTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
        if (Timer->DueTime.QuadPart >= NextTimer->DueTime.QuadPart) {
            break;
        }

        NextEntry = NextEntry->Blink;
    }

    InsertHeadList(NextEntry, &Timer->TimerListEntry);
    if (NextEntry == ListHead) {

        //
        // The computed list is empty or the timer is due to expire before
        // the first entry in the list.
        //

        KiQueryInterruptTime(&CurrentTime);
        if (Timer->DueTime.QuadPart <= (ULONG64)CurrentTime.QuadPart) {

            //
            // The timer is due to expire before the current time. Remove the
            // timer from the computed list, set its status to Signaled, and
            // set its inserted state to FALSE.
            //

            KiRemoveTreeTimer(Timer);
            Timer->Header.SignalState = TRUE;
            Timer->Header.Inserted = FALSE;
        }
    }

    return Timer->Header.Inserted;
}
