2005-01-06 13:58:04 +00:00
|
|
|
/* $Id$
|
2004-07-08 00:40:31 +00:00
|
|
|
*
|
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS system libraries
|
|
|
|
* PURPOSE: Timer Queue functions
|
2006-05-25 19:50:19 +00:00
|
|
|
* FILE: dll/win32/kernel32/misc/timerqueue.c
|
2004-07-08 00:40:31 +00:00
|
|
|
* PROGRAMER: Thomas Weidenmueller <w3seek@reactos.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
|
|
|
|
#include <k32.h>
|
|
|
|
|
|
|
|
#define NDEBUG
|
2007-09-02 19:42:22 +00:00
|
|
|
#include <debug.h>
|
2004-07-08 00:40:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
|
|
|
|
HANDLE DefaultTimerQueue = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the default timer queue for the current process. This function is only
|
2005-05-09 01:46:57 +00:00
|
|
|
* called if CreateTimerQueueTimer() or SetTimerQueueTimer() is called.
|
|
|
|
* However, ChangeTimerQueueTimer() fails with ERROR_INVALID_PARAMETER if the
|
2004-07-08 00:40:31 +00:00
|
|
|
* default timer queue has not been created, because it assumes there has to be
|
|
|
|
* a timer queue with a timer if it want's to be changed.
|
|
|
|
*/
|
|
|
|
static BOOL
|
|
|
|
IntCreateDefaultTimerQueue(VOID)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* FIXME - make this thread safe */
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* create the timer queue */
|
|
|
|
Status = RtlCreateTimerQueue(&DefaultTimerQueue);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
SetLastErrorByStatus(Status);
|
|
|
|
DPRINT1("Unable to create the default timer queue!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
CancelTimerQueueTimer(HANDLE TimerQueue,
|
|
|
|
HANDLE Timer)
|
|
|
|
{
|
|
|
|
/* Since this function is not documented in PSDK and apparently does nothing
|
|
|
|
but delete the timer, we just do the same as DeleteTimerQueueTimer(), without
|
|
|
|
passing a completion event. */
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(TimerQueue == NULL)
|
|
|
|
{
|
|
|
|
/* let's use the process' default timer queue. We assume the default timer
|
|
|
|
queue has been created with a previous call to CreateTimerQueueTimer() or
|
|
|
|
SetTimerQueueTimer(), otherwise this call wouldn't make much sense. */
|
|
|
|
if(!(TimerQueue = DefaultTimerQueue))
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(Timer == NULL)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* delete the timer */
|
|
|
|
Status = RtlDeleteTimer(TimerQueue, Timer, NULL);
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
SetLastErrorByStatus(Status);
|
2005-05-09 01:46:57 +00:00
|
|
|
return FALSE;
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
ChangeTimerQueueTimer(HANDLE TimerQueue,
|
|
|
|
HANDLE Timer,
|
|
|
|
ULONG DueTime,
|
|
|
|
ULONG Period)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(TimerQueue == NULL)
|
|
|
|
{
|
|
|
|
/* let's use the process' default timer queue. We assume the default timer
|
|
|
|
queue has been created with a previous call to CreateTimerQueueTimer() or
|
|
|
|
SetTimerQueueTimer(), otherwise this call wouldn't make much sense. */
|
|
|
|
if(!(TimerQueue = DefaultTimerQueue))
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(Timer == NULL)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* update the timer */
|
|
|
|
Status = RtlUpdateTimer(TimerQueue, Timer, DueTime, Period);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
SetLastErrorByStatus(Status);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
CreateTimerQueue(VOID)
|
|
|
|
{
|
|
|
|
HANDLE Handle;
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* create the timer queue */
|
|
|
|
Status = RtlCreateTimerQueue(&Handle);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
SetLastErrorByStatus(Status);
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return Handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
CreateTimerQueueTimer(PHANDLE phNewTimer,
|
|
|
|
HANDLE TimerQueue,
|
|
|
|
WAITORTIMERCALLBACK Callback,
|
|
|
|
PVOID Parameter,
|
|
|
|
DWORD DueTime,
|
|
|
|
DWORD Period,
|
|
|
|
ULONG Flags)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* windows seems not to test this parameter at all, so we'll try to clear it here
|
|
|
|
so we don't crash somewhere inside ntdll */
|
|
|
|
*phNewTimer = NULL;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(TimerQueue == NULL)
|
|
|
|
{
|
|
|
|
/* the default timer queue is requested, try to create it if it hasn't been already */
|
|
|
|
if(!(TimerQueue = DefaultTimerQueue))
|
|
|
|
{
|
|
|
|
if(!IntCreateDefaultTimerQueue())
|
|
|
|
{
|
|
|
|
/* IntCreateDefaultTimerQueue() set the last error code already, just fail */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
TimerQueue = DefaultTimerQueue;
|
|
|
|
}
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* !!! Win doesn't even check if Callback == NULL, so we don't, too! That'll
|
|
|
|
raise a nice exception later... */
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* create the timer */
|
|
|
|
Status = RtlCreateTimer(TimerQueue, phNewTimer, Callback, Parameter, DueTime,
|
|
|
|
Period, Flags);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
SetLastErrorByStatus(Status);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
DeleteTimerQueue(HANDLE TimerQueue)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* We don't allow the user to delete the default timer queue */
|
|
|
|
if(TimerQueue == NULL)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* delete the timer queue */
|
|
|
|
Status = RtlDeleteTimerQueue(TimerQueue);
|
|
|
|
return NT_SUCCESS(Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
DeleteTimerQueueEx(HANDLE TimerQueue,
|
|
|
|
HANDLE CompletionEvent)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* We don't allow the user to delete the default timer queue */
|
|
|
|
if(TimerQueue == NULL)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* delete the queue */
|
|
|
|
Status = RtlDeleteTimerQueueEx(TimerQueue, CompletionEvent);
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if((CompletionEvent != INVALID_HANDLE_VALUE && Status == STATUS_PENDING) ||
|
|
|
|
!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* In case CompletionEvent == NULL, RtlDeleteTimerQueueEx() returns before
|
|
|
|
all callback routines returned. We set the last error code to STATUS_PENDING
|
2005-05-09 01:46:57 +00:00
|
|
|
and return FALSE. In case CompletionEvent == INVALID_HANDLE_VALUE we only
|
2004-07-08 00:40:31 +00:00
|
|
|
can get here if another error occured. In case CompletionEvent is something
|
|
|
|
else, we get here and fail, even though it isn't really an error (if Status == STATUS_PENDING).
|
|
|
|
We also handle all other failures the same way. */
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
SetLastErrorByStatus(Status);
|
2005-05-09 01:46:57 +00:00
|
|
|
return FALSE;
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
DeleteTimerQueueTimer(HANDLE TimerQueue,
|
|
|
|
HANDLE Timer,
|
|
|
|
HANDLE CompletionEvent)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(TimerQueue == NULL)
|
|
|
|
{
|
|
|
|
/* let's use the process' default timer queue. We assume the default timer
|
|
|
|
queue has been created with a previous call to CreateTimerQueueTimer() or
|
|
|
|
SetTimerQueueTimer(), otherwise this call wouldn't make much sense. */
|
|
|
|
if(!(TimerQueue = DefaultTimerQueue))
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(Timer == NULL)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* delete the timer */
|
|
|
|
Status = RtlDeleteTimer(TimerQueue, Timer, CompletionEvent);
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if((CompletionEvent != INVALID_HANDLE_VALUE && Status == STATUS_PENDING) ||
|
|
|
|
!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* In case CompletionEvent == NULL, RtlDeleteTimer() returns before
|
|
|
|
the callback routine returned. We set the last error code to STATUS_PENDING
|
2005-05-09 01:46:57 +00:00
|
|
|
and return FALSE. In case CompletionEvent == INVALID_HANDLE_VALUE we only
|
2004-07-08 00:40:31 +00:00
|
|
|
can get here if another error occured. In case CompletionEvent is something
|
|
|
|
else, we get here and fail, even though it isn't really an error (if Status == STATUS_PENDING).
|
|
|
|
We also handle all other failures the same way. */
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
SetLastErrorByStatus(Status);
|
2005-05-09 01:46:57 +00:00
|
|
|
return FALSE;
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
2008-11-30 11:42:05 +00:00
|
|
|
WINAPI
|
2004-07-08 00:40:31 +00:00
|
|
|
SetTimerQueueTimer(HANDLE TimerQueue,
|
|
|
|
WAITORTIMERCALLBACK Callback,
|
|
|
|
PVOID Parameter,
|
|
|
|
DWORD DueTime,
|
|
|
|
DWORD Period,
|
|
|
|
BOOL PreferIo)
|
|
|
|
{
|
|
|
|
/* Since this function is not documented in PSDK and apparently does nothing
|
|
|
|
but create a timer, we just do the same as CreateTimerQueueTimer(). Unfortunately
|
|
|
|
I don't really know what PreferIo is supposed to be, it propably just affects the
|
|
|
|
Flags parameter of CreateTimerQueueTimer(). Looking at the PSDK documentation of
|
|
|
|
CreateTimerQueueTimer() there's only one flag (WT_EXECUTEINIOTHREAD) that causes
|
|
|
|
the callback function queued to an I/O worker thread. I guess it uses this flag
|
|
|
|
if PreferIo == TRUE, otherwise let's just use WT_EXECUTEDEFAULT. We should
|
2005-05-09 01:46:57 +00:00
|
|
|
test this though, this is only guess work and I'm too lazy to do further
|
2004-07-08 00:40:31 +00:00
|
|
|
investigation. */
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
HANDLE Timer;
|
|
|
|
NTSTATUS Status;
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
if(TimerQueue == NULL)
|
|
|
|
{
|
|
|
|
/* the default timer queue is requested, try to create it if it hasn't been already */
|
|
|
|
if(!(TimerQueue = DefaultTimerQueue))
|
|
|
|
{
|
|
|
|
if(!IntCreateDefaultTimerQueue())
|
|
|
|
{
|
|
|
|
/* IntCreateDefaultTimerQueue() set the last error code already, just fail */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
TimerQueue = DefaultTimerQueue;
|
|
|
|
}
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/* create the timer */
|
|
|
|
Status = RtlCreateTimer(TimerQueue, &Timer, Callback, Parameter, DueTime,
|
|
|
|
Period, (PreferIo ? WT_EXECUTEINIOTHREAD : WT_EXECUTEDEFAULT));
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
SetLastErrorByStatus(Status);
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-05-09 01:46:57 +00:00
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
return Timer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|