|
|
/*************************************************************************
* * timer.c * * Common timer routines * * Copyright Microsoft Corporation, 1998 * * *************************************************************************/
/*
* Includes */ #include "precomp.h"
#pragma hdrstop
/*=============================================================================
== Local structures =============================================================================*/
typedef VOID (*PCLIBTIMERFUNC)(PVOID); typedef NTSTATUS (*PCREATETHREAD)( PUSER_THREAD_START_ROUTINE, PVOID, BOOLEAN, PHANDLE );
/*
* Timer thread structure */ typedef struct _CLIBTIMERTHREAD { HANDLE hTimerThread; HANDLE hTimer; LIST_ENTRY TimerHead; } CLIBTIMERTHREAD, * PCLIBTIMERTHREAD;
/*
* Timer structures */ typedef struct _CLIBTIMER { PCLIBTIMERTHREAD pThread; LIST_ENTRY Links; LARGE_INTEGER ExpireTime; PCLIBTIMERFUNC pFunc; PVOID pParam; ULONG Flags; } CLIBTIMER, * PCLIBTIMER;
#define TIMER_ENABLED 0x00000001
/*=============================================================================
== External Functions Defined =============================================================================*/
NTSTATUS IcaTimerCreate( ULONG, HANDLE * ); NTSTATUS IcaTimerStart( HANDLE, PVOID, PVOID, ULONG ); BOOLEAN IcaTimerCancel( HANDLE ); BOOLEAN IcaTimerClose( HANDLE );
/*=============================================================================
== Internal Functions Defined =============================================================================*/
NTSTATUS _TimersInit( PCLIBTIMERTHREAD ); NTSTATUS _TimerSet( PCLIBTIMERTHREAD ); BOOLEAN _TimerRemove( PCLIBTIMERTHREAD, PCLIBTIMER, BOOLEAN ); DWORD _TimerThread( PCLIBTIMERTHREAD );
/*=============================================================================
== Global data =============================================================================*/
CLIBTIMERTHREAD ThreadData[ 3 ];
/*******************************************************************************
* * _TimersInit * * Initialize timers for process * * NOTE: timer semaphore must be locked * * * ENTRY: * pThread (input) * pointer to timer thread structure * * EXIT: * NO_ERROR : successful * ******************************************************************************/
NTSTATUS _TimersInit( PCLIBTIMERTHREAD pThread ) { ULONG Tid; NTSTATUS Status;
/*
* Check if someone beat us here */ if ( pThread->hTimerThread ) return( STATUS_SUCCESS );
/*
* Initialize timer variables */ InitializeListHead( &pThread->TimerHead ); pThread->hTimerThread = NULL; pThread->hTimer = NULL;
/*
* Create timer object */ Status = NtCreateTimer( &pThread->hTimer, TIMER_ALL_ACCESS, NULL, NotificationTimer ); if ( !NT_SUCCESS(Status) ) goto badtimer;
pThread->hTimerThread = CreateThread( NULL, 0, _TimerThread, pThread, THREAD_SET_INFORMATION, &Tid );
if ( !pThread->hTimerThread ) { Status = NtCurrentTeb()->LastStatusValue; goto badthread; }
SetThreadPriority( pThread->hTimerThread, THREAD_PRIORITY_TIME_CRITICAL-2 );
return( STATUS_SUCCESS );
/*=============================================================================
== Error returns =============================================================================*/
/*
* bad thread create */ badthread: NtClose( pThread->hTimer );
/*
* bad timer object */ badtimer: pThread->hTimerThread = NULL; ASSERT( Status == STATUS_SUCCESS ); return( Status ); }
/*******************************************************************************
* * IcaTimerCreate * * Create a timer * * * ENTRY: * TimerThread (input) * index of time thread (TIMERTHREAD_?) clib.h * phTimer (output) * address to return timer handle * * EXIT: * STATUS_SUCCESS - no error * * ******************************************************************************/
NTSTATUS IcaTimerCreate( ULONG TimerThread, HANDLE * phTimer ) { PCLIBTIMER pTimer; NTSTATUS Status; PCLIBTIMERTHREAD pThread;
if ( TimerThread >= 3 ) return( STATUS_INVALID_PARAMETER );
/*
* Lock timer semaphore */ RtlEnterCriticalSection( &TimerCritSec );
/*
* Get pointer to thread structure */ pThread = &ThreadData[ TimerThread ];
/*
* Make sure timers are initialized */ if ( pThread->hTimerThread == NULL ) { Status = _TimersInit( pThread ); if ( !NT_SUCCESS(Status) ) goto badinit; }
/*
* Unlock timer semaphore */ RtlLeaveCriticalSection( &TimerCritSec );
/*
* Allocate timer event */ pTimer = MemAlloc( sizeof(CLIBTIMER) ); if ( !pTimer ) { Status = STATUS_NO_MEMORY; goto badmalloc; }
/*
* Initialize timer event */ RtlZeroMemory( pTimer, sizeof(CLIBTIMER) ); pTimer->pThread = pThread;
*phTimer = (HANDLE) pTimer; return( STATUS_SUCCESS );
/*=============================================================================
== Error returns =============================================================================*/
/*
* timer create failed * timer initialization failed */
// badmalloc:
badinit: RtlLeaveCriticalSection( &TimerCritSec ); badmalloc: /* makarp; dont LeaveCritical Section in case of bad malloc as we have done it already. #182846*/ *phTimer = NULL; return( Status ); }
/*******************************************************************************
* * IcaTimerStart * * Start a timer * * * ENTRY: * TimerHandle (input) * timer handle * pFunc (input) * address of procedure to call when timer expires * pParam (input) * parameter to pass to procedure * TimeLeft (input) * relative time until timer expires (1/1000 seconds) * * EXIT: * NO_ERROR : successful * * ******************************************************************************/
NTSTATUS IcaTimerStart( HANDLE TimerHandle, PVOID pFunc, PVOID pParam, ULONG TimeLeft ) { PCLIBTIMER pTimer; PCLIBTIMER pNextTimer; LARGE_INTEGER CurrentTime; LARGE_INTEGER Time; PLIST_ENTRY Head, Next; BOOLEAN fSetTimer = FALSE; NTSTATUS Status; PCLIBTIMERTHREAD pThread;
/*
* Lock timer semaphore */ RtlEnterCriticalSection( &TimerCritSec );
/*
* Get timer pointer */ pTimer = (PCLIBTIMER) TimerHandle; pThread = pTimer->pThread;
/*
* Remove timer if it is enabled * (If the timer was the head entry, then fSetTimer * will be TRUE and _TimerSet will be called below.) */ if ( (pTimer->Flags & TIMER_ENABLED) ) fSetTimer = _TimerRemove( pThread, pTimer, FALSE );
/*
* Initialize timer event */ Time = RtlEnlargedUnsignedMultiply( TimeLeft, 10000 ); (VOID) NtQuerySystemTime( &CurrentTime ); pTimer->ExpireTime = RtlLargeIntegerAdd( CurrentTime, Time ); pTimer->pFunc = pFunc; pTimer->pParam = pParam;
/*
* Locate correct spot in linked list to insert this entry */ Head = &pThread->TimerHead; for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) { pNextTimer = CONTAINING_RECORD( Next, CLIBTIMER, Links ); if ( RtlLargeIntegerLessThan( pTimer->ExpireTime, pNextTimer->ExpireTime ) ) break; }
/*
* Insert timer event into timer list. * (InsertTailList inserts 'pTimer' entry in front of 'Next' entry. * If 'Next' points to TimerHead, either because the list is empty, * or because we searched thru the entire list and got back to the * head, this will insert the new entry at the tail.) */ InsertTailList( Next, &pTimer->Links ); pTimer->Flags |= TIMER_ENABLED;
/*
* Update timer if needed. * (If we just added this entry to the head of the list, the timer * needs to be set. Also, if fSetTimer is TRUE, then this entry was * removed by _TimerRemove and was the head entry, so set the timer.) */ if ( pThread->TimerHead.Flink == &pTimer->Links || fSetTimer ) { Status = _TimerSet( pThread ); if ( !NT_SUCCESS(Status) ) goto badset; }
/*
* Unlock timer semaphore */ RtlLeaveCriticalSection( &TimerCritSec );
return( STATUS_SUCCESS );
/*=============================================================================
== Error returns =============================================================================*/
/*
* timer set failed * timer create failed * timer initialization failed */ badset: RtlLeaveCriticalSection( &TimerCritSec ); ASSERT( Status == STATUS_SUCCESS ); return( Status ); }
/*******************************************************************************
* * IcaTimerCancel * * cancel the specified timer * * * ENTRY: * TimerHandle (input) * timer handle * * EXIT: * TRUE : timer was actually canceled * FALSE : timer was not armed * * ******************************************************************************/
BOOLEAN IcaTimerCancel( HANDLE TimerHandle ) { PCLIBTIMERTHREAD pThread; PCLIBTIMER pTimer; BOOLEAN fCanceled = FALSE;
/*
* Lock timer semaphore */ RtlEnterCriticalSection( &TimerCritSec );
/*
* Get timer pointer */ pTimer = (PCLIBTIMER) TimerHandle; pThread = pTimer->pThread;
/*
* Remove timer if it is enabled */ if ( (pTimer->Flags & TIMER_ENABLED) ) { _TimerRemove( pThread, pTimer, TRUE ); fCanceled = TRUE; }
/*
* Unlock timer semaphore */ RtlLeaveCriticalSection( &TimerCritSec );
return( fCanceled ); }
/*******************************************************************************
* * IcaTimerClose * * cancel the specified timer * * * ENTRY: * TimerHandle (input) * timer handle * * EXIT: * TRUE : timer was actually canceled * FALSE : timer was not armed * * ******************************************************************************/
BOOLEAN IcaTimerClose( HANDLE TimerHandle ) { BOOLEAN fCanceled;
/*
* Cancel timer if it is enabled */ fCanceled = IcaTimerCancel( TimerHandle );
/*
* Free timer memory */ MemFree( TimerHandle );
return( fCanceled ); }
/*******************************************************************************
* * _TimerSet * * set the timer * * NOTE: timer semaphore must be locked * * * ENTRY: * pThread (input) * pointer to timer thread structure * * EXIT: * NO_ERROR : successful * * ******************************************************************************/
NTSTATUS _TimerSet( PCLIBTIMERTHREAD pThread ) { PCLIBTIMER pTimer; LARGE_INTEGER Time; // the following is roughly 1 year in 100 nanosecond increments
static LARGE_INTEGER LongWaitTime = { 0, 0x00010000 };
/*
* Get ExpireTime for next timer entry or 'large' value if none */ if ( pThread->TimerHead.Flink != &pThread->TimerHead ) { pTimer = CONTAINING_RECORD( pThread->TimerHead.Flink, CLIBTIMER, Links ); Time = pTimer->ExpireTime; } else { LARGE_INTEGER CurrentTime;
NtQuerySystemTime( &CurrentTime ); Time = RtlLargeIntegerAdd( CurrentTime, LongWaitTime ); }
/*
* Set the timer */ return( NtSetTimer( pThread->hTimer, &Time, NULL, NULL, FALSE, 0, NULL ) ); }
/*******************************************************************************
* * _TimerRemove * * remove the specified timer from the timer list * and optionally set the time for the next timer to trigger * * NOTE: timer semaphore must be locked * * * ENTRY: * pThread (input) * pointer to timer thread structure * pTimer (input) * timer entry pointer * SetTimer (input) * BOOLEAN which indicates if _TimerSet should be called * * EXIT: * TRUE : timer needs to be set (removed entry was head of list) * FALSE : timer does not need to be set * * ******************************************************************************/
BOOLEAN _TimerRemove( PCLIBTIMERTHREAD pThread, PCLIBTIMER pTimer, BOOLEAN fSetTimer ) { BOOLEAN fSetNeeded = FALSE; NTSTATUS Status;
/*
* See if timer is currently enabled */ if ( (pTimer->Flags & TIMER_ENABLED) ) {
/*
* Unlink the entry from the timer list and clear enabled flag */ RemoveEntryList( &pTimer->Links ); pTimer->Flags &= ~TIMER_ENABLED;
/*
* If we removed the head entry, then set the timer * or indicate to caller that it needs to be set. */ if ( pTimer->Links.Blink == &pThread->TimerHead ) { if ( fSetTimer ) { Status = _TimerSet( pThread ); ASSERT( Status == STATUS_SUCCESS ); } else { fSetNeeded = TRUE; } } }
return( fSetNeeded ); }
/*******************************************************************************
* * _TimerThread * * * ENTRY: * pThread (input) * pointer to timer thread structure * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/
DWORD _TimerThread( PCLIBTIMERTHREAD pThread ) { PCLIBTIMER pTimer; PCLIBTIMERFUNC pFunc; PVOID pParam; LARGE_INTEGER CurrentTime; NTSTATUS Status;
for (;;) {
/*
* Wait on timer */ Status = NtWaitForSingleObject( pThread->hTimer, TRUE, NULL );
/*
* Check for an error */ if ( Status != STATUS_WAIT_0 ) break;
/*
* Lock semaphore */ RtlEnterCriticalSection( &TimerCritSec );
/*
* Make sure a timer entry exists */ if ( IsListEmpty( &pThread->TimerHead ) ) { Status = _TimerSet( pThread ); ASSERT( Status == STATUS_SUCCESS ); RtlLeaveCriticalSection( &TimerCritSec ); continue; }
/*
* Make sure the head entry should be removed now. * (The timer may have been triggered while the * head entry was being removed.) */ pTimer = CONTAINING_RECORD( pThread->TimerHead.Flink, CLIBTIMER, Links ); NtQuerySystemTime( &CurrentTime ); if ( RtlLargeIntegerGreaterThan( pTimer->ExpireTime, CurrentTime ) ) { Status = _TimerSet( pThread ); ASSERT( Status == STATUS_SUCCESS ); RtlLeaveCriticalSection( &TimerCritSec ); continue; }
/*
* Remove the entry and indicate it is no longer enabled */ RemoveEntryList( &pTimer->Links ); pTimer->Flags &= ~TIMER_ENABLED;
/*
* Set the timer for next time */ Status = _TimerSet( pThread ); ASSERT( Status == STATUS_SUCCESS );
/*
* Get all the data we need out of the timer structure */ pFunc = pTimer->pFunc; pParam = pTimer->pParam;
/*
* Unload semaphore */ RtlLeaveCriticalSection( &TimerCritSec );
/*
* Call timer function */ if ( pFunc ) { (*pFunc)( pParam ); } }
pThread->hTimerThread = NULL; return( STATUS_SUCCESS ); }
|