669 lines
16 KiB
C
669 lines
16 KiB
C
|
|
/*************************************************************************
|
|
*
|
|
* 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 );
|
|
}
|
|
|