Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

668 lines
16 KiB

/*************************************************************************
*
* 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 );
}