2004-07-08 00:40:31 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS system libraries
|
|
|
|
* PURPOSE: Timer Queue implementation
|
2005-09-08 00:09:32 +00:00
|
|
|
* FILE: lib/rtl/timerqueue.c
|
2007-10-19 23:21:45 +00:00
|
|
|
* PROGRAMMER:
|
2004-07-08 00:40:31 +00:00
|
|
|
*/
|
|
|
|
|
2005-09-08 00:09:32 +00:00
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
|
|
|
|
#include <rtl.h>
|
2004-07-08 00:40:31 +00:00
|
|
|
|
2005-06-19 22:50:59 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
2004-07-08 00:40:31 +00:00
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
#undef LIST_FOR_EACH
|
|
|
|
#undef LIST_FOR_EACH_SAFE
|
|
|
|
#include <wine/list.h>
|
|
|
|
|
2005-09-08 00:09:32 +00:00
|
|
|
/* FUNCTIONS ***************************************************************/
|
|
|
|
|
[RTL/NTDLL/KERNEL32]: Rtl provides worker queue and timer queue functionality, which queues a worker thread associated with a caller-supplied callback. In Windows, Rtl by default calls RtlCreateUserThread, but as soon as kernel32 loads, it's DllMain calls an exported function RtlSetThreadPoolStartFunc which changes that default to a special Base function that calls CreateRemoteThread instead. The net result is that Win32 processes using the Rtl functionality get their threads properly registered with CSRSS. In ReactOS, this did not happen, so when those threads called into CSRSS, CSRSS had no CSR_THREAD structure/state for them, which is why CsrCreateThread (and the API loop) are so badly hacked. This commit implements RtlSetThreadPoolStartFunc, implements the kernel32 base functions which wrap CreateRemoteThread, and implements the rtl functions which wrap RtlCreateUserThread. Services, Setup, and any ReactOS application using RPC now have the worker threads correctly registered.
svn path=/trunk/; revision=55706
2012-02-19 10:06:31 +00:00
|
|
|
extern PRTL_START_POOL_THREAD RtlpStartThreadFunc;
|
|
|
|
extern PRTL_EXIT_POOL_THREAD RtlpExitThreadFunc;
|
2006-07-03 20:24:46 +00:00
|
|
|
HANDLE TimerThreadHandle = NULL;
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
RtlpInitializeTimerThread(VOID)
|
|
|
|
{
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout )
|
2004-07-08 00:40:31 +00:00
|
|
|
{
|
2008-09-08 11:10:02 +00:00
|
|
|
if (timeout == INFINITE) return NULL;
|
|
|
|
pTime->QuadPart = (ULONGLONG)timeout * -10000;
|
|
|
|
return pTime;
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
struct timer_queue;
|
|
|
|
struct queue_timer
|
|
|
|
{
|
|
|
|
struct timer_queue *q;
|
|
|
|
struct list entry;
|
|
|
|
ULONG runcount; /* number of callbacks pending execution */
|
|
|
|
WAITORTIMERCALLBACKFUNC callback;
|
|
|
|
PVOID param;
|
|
|
|
DWORD period;
|
|
|
|
ULONG flags;
|
|
|
|
ULONGLONG expire;
|
2015-11-24 20:35:58 +00:00
|
|
|
BOOL destroy; /* timer should be deleted; once set, never unset */
|
|
|
|
HANDLE event; /* removal event */
|
2008-09-08 11:10:02 +00:00
|
|
|
};
|
2004-07-08 00:40:31 +00:00
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
struct timer_queue
|
|
|
|
{
|
2015-11-24 20:35:58 +00:00
|
|
|
DWORD magic;
|
2008-09-08 11:10:02 +00:00
|
|
|
RTL_CRITICAL_SECTION cs;
|
2015-11-24 20:35:58 +00:00
|
|
|
struct list timers; /* sorted by expiration time */
|
|
|
|
BOOL quit; /* queue should be deleted; once set, never unset */
|
2008-09-08 11:10:02 +00:00
|
|
|
HANDLE event;
|
|
|
|
HANDLE thread;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define EXPIRE_NEVER (~(ULONGLONG) 0)
|
2015-11-24 20:35:58 +00:00
|
|
|
#define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */
|
2008-09-08 11:10:02 +00:00
|
|
|
|
|
|
|
static void queue_remove_timer(struct queue_timer *t)
|
2004-07-08 00:40:31 +00:00
|
|
|
{
|
2008-09-08 11:10:02 +00:00
|
|
|
/* We MUST hold the queue cs while calling this function. This ensures
|
|
|
|
that we cannot queue another callback for this timer. The runcount
|
|
|
|
being zero makes sure we don't have any already queued. */
|
|
|
|
struct timer_queue *q = t->q;
|
|
|
|
|
|
|
|
assert(t->runcount == 0);
|
|
|
|
assert(t->destroy);
|
|
|
|
|
|
|
|
list_remove(&t->entry);
|
|
|
|
if (t->event)
|
|
|
|
NtSetEvent(t->event, NULL);
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, t);
|
|
|
|
|
2015-11-24 20:35:58 +00:00
|
|
|
if (q->quit && list_empty(&q->timers))
|
2008-09-08 11:10:02 +00:00
|
|
|
NtSetEvent(q->event, NULL);
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
static void timer_cleanup_callback(struct queue_timer *t)
|
|
|
|
{
|
|
|
|
struct timer_queue *q = t->q;
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
2004-07-08 00:40:31 +00:00
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
assert(0 < t->runcount);
|
|
|
|
--t->runcount;
|
|
|
|
|
|
|
|
if (t->destroy && t->runcount == 0)
|
|
|
|
queue_remove_timer(t);
|
|
|
|
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
}
|
|
|
|
|
2015-11-24 20:35:58 +00:00
|
|
|
static VOID WINAPI timer_callback_wrapper(LPVOID p)
|
2004-07-08 00:40:31 +00:00
|
|
|
{
|
2008-09-08 11:10:02 +00:00
|
|
|
struct queue_timer *t = p;
|
|
|
|
t->callback(t->param, TRUE);
|
|
|
|
timer_cleanup_callback(t);
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
static inline ULONGLONG queue_current_time(void)
|
|
|
|
{
|
2015-11-24 20:35:58 +00:00
|
|
|
LARGE_INTEGER now, freq;
|
|
|
|
NtQueryPerformanceCounter(&now, &freq);
|
|
|
|
return now.QuadPart * 1000 / freq.QuadPart;
|
2008-09-08 11:10:02 +00:00
|
|
|
}
|
2004-07-08 00:40:31 +00:00
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
static void queue_add_timer(struct queue_timer *t, ULONGLONG time,
|
|
|
|
BOOL set_event)
|
|
|
|
{
|
|
|
|
/* We MUST hold the queue cs while calling this function. */
|
|
|
|
struct timer_queue *q = t->q;
|
|
|
|
struct list *ptr = &q->timers;
|
|
|
|
|
|
|
|
assert(!q->quit || (t->destroy && time == EXPIRE_NEVER));
|
|
|
|
|
|
|
|
if (time != EXPIRE_NEVER)
|
|
|
|
LIST_FOR_EACH(ptr, &q->timers)
|
|
|
|
{
|
|
|
|
struct queue_timer *cur = LIST_ENTRY(ptr, struct queue_timer, entry);
|
|
|
|
if (time < cur->expire)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
list_add_before(ptr, &t->entry);
|
|
|
|
|
|
|
|
t->expire = time;
|
|
|
|
|
|
|
|
/* If we insert at the head of the list, we need to expire sooner
|
|
|
|
than expected. */
|
|
|
|
if (set_event && &t->entry == list_head(&q->timers))
|
|
|
|
NtSetEvent(q->event, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time,
|
|
|
|
BOOL set_event)
|
|
|
|
{
|
|
|
|
/* We MUST hold the queue cs while calling this function. */
|
|
|
|
list_remove(&t->entry);
|
|
|
|
queue_add_timer(t, time, set_event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void queue_timer_expire(struct timer_queue *q)
|
|
|
|
{
|
|
|
|
struct queue_timer *t = NULL;
|
|
|
|
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
|
|
|
if (list_head(&q->timers))
|
|
|
|
{
|
2015-11-24 20:35:58 +00:00
|
|
|
ULONGLONG now, next;
|
2008-09-08 11:10:02 +00:00
|
|
|
t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
|
2015-11-24 20:35:58 +00:00
|
|
|
if (!t->destroy && t->expire <= ((now = queue_current_time())))
|
2008-09-08 11:10:02 +00:00
|
|
|
{
|
|
|
|
++t->runcount;
|
2015-11-24 20:35:58 +00:00
|
|
|
if (t->period)
|
|
|
|
{
|
|
|
|
next = t->expire + t->period;
|
|
|
|
/* avoid trigger cascade if overloaded / hibernated */
|
|
|
|
if (next < now)
|
|
|
|
next = now + t->period;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
next = EXPIRE_NEVER;
|
|
|
|
queue_move_timer(t, next, FALSE);
|
2008-09-08 11:10:02 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
t = NULL;
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
|
|
|
|
if (t)
|
|
|
|
{
|
|
|
|
if (t->flags & WT_EXECUTEINTIMERTHREAD)
|
|
|
|
timer_callback_wrapper(t);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ULONG flags
|
|
|
|
= (t->flags
|
|
|
|
& (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD
|
|
|
|
| WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION));
|
2015-11-24 20:35:58 +00:00
|
|
|
NTSTATUS status = RtlQueueWorkItem(timer_callback_wrapper, t, flags);
|
2008-09-08 11:10:02 +00:00
|
|
|
if (status != STATUS_SUCCESS)
|
|
|
|
timer_cleanup_callback(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG queue_get_timeout(struct timer_queue *q)
|
|
|
|
{
|
|
|
|
struct queue_timer *t;
|
|
|
|
ULONG timeout = INFINITE;
|
|
|
|
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
|
|
|
if (list_head(&q->timers))
|
|
|
|
{
|
|
|
|
t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
|
|
|
|
assert(!t->destroy || t->expire == EXPIRE_NEVER);
|
|
|
|
|
|
|
|
if (t->expire != EXPIRE_NEVER)
|
|
|
|
{
|
|
|
|
ULONGLONG time = queue_current_time();
|
2011-09-19 19:31:21 +00:00
|
|
|
timeout = t->expire < time ? 0 : (ULONG)(t->expire - time);
|
2008-09-08 11:10:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
|
|
|
|
return timeout;
|
|
|
|
}
|
|
|
|
|
2015-11-24 20:35:58 +00:00
|
|
|
static DWORD WINAPI timer_queue_thread_proc(LPVOID p)
|
2008-09-08 11:10:02 +00:00
|
|
|
{
|
|
|
|
struct timer_queue *q = p;
|
|
|
|
ULONG timeout_ms;
|
|
|
|
|
|
|
|
timeout_ms = INFINITE;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
LARGE_INTEGER timeout;
|
|
|
|
NTSTATUS status;
|
|
|
|
BOOL done = FALSE;
|
|
|
|
|
|
|
|
status = NtWaitForSingleObject(
|
|
|
|
q->event, FALSE, get_nt_timeout(&timeout, timeout_ms));
|
|
|
|
|
|
|
|
if (status == STATUS_WAIT_0)
|
|
|
|
{
|
|
|
|
/* There are two possible ways to trigger the event. Either
|
|
|
|
we are quitting and the last timer got removed, or a new
|
|
|
|
timer got put at the head of the list so we need to adjust
|
|
|
|
our timeout. */
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
2015-11-24 20:35:58 +00:00
|
|
|
if (q->quit && list_empty(&q->timers))
|
2008-09-08 11:10:02 +00:00
|
|
|
done = TRUE;
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
}
|
|
|
|
else if (status == STATUS_TIMEOUT)
|
|
|
|
queue_timer_expire(q);
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
|
|
|
|
timeout_ms = queue_get_timeout(q);
|
|
|
|
}
|
|
|
|
|
|
|
|
NtClose(q->event);
|
|
|
|
RtlDeleteCriticalSection(&q->cs);
|
2015-11-24 20:35:58 +00:00
|
|
|
q->magic = 0;
|
2008-09-08 11:10:02 +00:00
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, q);
|
[RTL/NTDLL/KERNEL32]: Rtl provides worker queue and timer queue functionality, which queues a worker thread associated with a caller-supplied callback. In Windows, Rtl by default calls RtlCreateUserThread, but as soon as kernel32 loads, it's DllMain calls an exported function RtlSetThreadPoolStartFunc which changes that default to a special Base function that calls CreateRemoteThread instead. The net result is that Win32 processes using the Rtl functionality get their threads properly registered with CSRSS. In ReactOS, this did not happen, so when those threads called into CSRSS, CSRSS had no CSR_THREAD structure/state for them, which is why CsrCreateThread (and the API loop) are so badly hacked. This commit implements RtlSetThreadPoolStartFunc, implements the kernel32 base functions which wrap CreateRemoteThread, and implements the rtl functions which wrap RtlCreateUserThread. Services, Setup, and any ReactOS application using RPC now have the worker threads correctly registered.
svn path=/trunk/; revision=55706
2012-02-19 10:06:31 +00:00
|
|
|
RtlpExitThreadFunc(STATUS_SUCCESS);
|
2015-11-24 20:35:58 +00:00
|
|
|
return 0;
|
2008-09-08 11:10:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void queue_destroy_timer(struct queue_timer *t)
|
|
|
|
{
|
|
|
|
/* We MUST hold the queue cs while calling this function. */
|
|
|
|
t->destroy = TRUE;
|
|
|
|
if (t->runcount == 0)
|
|
|
|
/* Ensure a timer is promptly removed. If callbacks are pending,
|
|
|
|
it will be removed after the last one finishes by the callback
|
|
|
|
cleanup wrapper. */
|
|
|
|
queue_remove_timer(t);
|
|
|
|
else
|
|
|
|
/* Make sure no destroyed timer masks an active timer at the head
|
|
|
|
of the sorted list. */
|
|
|
|
queue_move_timer(t, EXPIRE_NEVER, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* RtlCreateTimerQueue (NTDLL.@)
|
|
|
|
*
|
|
|
|
* Creates a timer queue object and returns a handle to it.
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* NewTimerQueue [O] The newly created queue.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* Success: STATUS_SUCCESS.
|
|
|
|
* Failure: Any NTSTATUS code.
|
2004-07-08 00:40:31 +00:00
|
|
|
*/
|
2008-09-08 11:10:02 +00:00
|
|
|
NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue)
|
2004-07-08 00:40:31 +00:00
|
|
|
{
|
2008-09-08 11:10:02 +00:00
|
|
|
NTSTATUS status;
|
|
|
|
struct timer_queue *q = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *q);
|
|
|
|
if (!q)
|
|
|
|
return STATUS_NO_MEMORY;
|
|
|
|
|
|
|
|
RtlInitializeCriticalSection(&q->cs);
|
|
|
|
list_init(&q->timers);
|
|
|
|
q->quit = FALSE;
|
2015-11-24 20:35:58 +00:00
|
|
|
q->magic = TIMER_QUEUE_MAGIC;
|
|
|
|
status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
|
2008-09-08 11:10:02 +00:00
|
|
|
if (status != STATUS_SUCCESS)
|
|
|
|
{
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, q);
|
|
|
|
return status;
|
|
|
|
}
|
2015-11-24 20:35:58 +00:00
|
|
|
status = RtlpStartThreadFunc(timer_queue_thread_proc, q, &q->thread);
|
2008-09-08 11:10:02 +00:00
|
|
|
if (status != STATUS_SUCCESS)
|
|
|
|
{
|
|
|
|
NtClose(q->event);
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, q);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
[RTL/NTDLL/KERNEL32]: Rtl provides worker queue and timer queue functionality, which queues a worker thread associated with a caller-supplied callback. In Windows, Rtl by default calls RtlCreateUserThread, but as soon as kernel32 loads, it's DllMain calls an exported function RtlSetThreadPoolStartFunc which changes that default to a special Base function that calls CreateRemoteThread instead. The net result is that Win32 processes using the Rtl functionality get their threads properly registered with CSRSS. In ReactOS, this did not happen, so when those threads called into CSRSS, CSRSS had no CSR_THREAD structure/state for them, which is why CsrCreateThread (and the API loop) are so badly hacked. This commit implements RtlSetThreadPoolStartFunc, implements the kernel32 base functions which wrap CreateRemoteThread, and implements the rtl functions which wrap RtlCreateUserThread. Services, Setup, and any ReactOS application using RPC now have the worker threads correctly registered.
svn path=/trunk/; revision=55706
2012-02-19 10:06:31 +00:00
|
|
|
NtResumeThread(q->thread, NULL);
|
2008-09-08 11:10:02 +00:00
|
|
|
*NewTimerQueue = q;
|
|
|
|
return STATUS_SUCCESS;
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* RtlDeleteTimerQueueEx (NTDLL.@)
|
|
|
|
*
|
|
|
|
* Deletes a timer queue object.
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* TimerQueue [I] The timer queue to destroy.
|
|
|
|
* CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
|
|
|
|
* wait until all timers are finished firing before
|
|
|
|
* returning. Otherwise, return immediately and set the
|
|
|
|
* event when all timers are done.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not.
|
|
|
|
* Failure: Any NTSTATUS code.
|
|
|
|
*/
|
|
|
|
NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
|
|
|
|
{
|
|
|
|
struct timer_queue *q = TimerQueue;
|
|
|
|
struct queue_timer *t, *temp;
|
|
|
|
HANDLE thread;
|
|
|
|
NTSTATUS status;
|
2004-07-08 00:40:31 +00:00
|
|
|
|
2015-11-24 20:35:58 +00:00
|
|
|
if (!q || q->magic != TIMER_QUEUE_MAGIC)
|
2008-09-08 11:10:02 +00:00
|
|
|
return STATUS_INVALID_HANDLE;
|
|
|
|
|
|
|
|
thread = q->thread;
|
|
|
|
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
|
|
|
q->quit = TRUE;
|
|
|
|
if (list_head(&q->timers))
|
|
|
|
/* When the last timer is removed, it will signal the timer thread to
|
|
|
|
exit... */
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(t, temp, &q->timers, struct queue_timer, entry)
|
|
|
|
queue_destroy_timer(t);
|
|
|
|
else
|
|
|
|
/* However if we have none, we must do it ourselves. */
|
|
|
|
NtSetEvent(q->event, NULL);
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
|
|
|
|
if (CompletionEvent == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
NtWaitForSingleObject(thread, FALSE, NULL);
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (CompletionEvent)
|
|
|
|
{
|
|
|
|
DPRINT1("asynchronous return on completion event unimplemented\n");
|
|
|
|
NtWaitForSingleObject(thread, FALSE, NULL);
|
|
|
|
NtSetEvent(CompletionEvent, NULL);
|
|
|
|
}
|
|
|
|
status = STATUS_PENDING;
|
|
|
|
}
|
|
|
|
|
|
|
|
NtClose(thread);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct timer_queue *get_timer_queue(HANDLE TimerQueue)
|
|
|
|
{
|
2015-11-24 20:35:58 +00:00
|
|
|
static struct timer_queue *default_timer_queue;
|
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
if (TimerQueue)
|
|
|
|
return TimerQueue;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!default_timer_queue)
|
|
|
|
{
|
|
|
|
HANDLE q;
|
|
|
|
NTSTATUS status = RtlCreateTimerQueue(&q);
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
|
|
{
|
2011-02-10 11:47:17 +00:00
|
|
|
PVOID p = InterlockedCompareExchangePointer(
|
2008-09-08 11:10:02 +00:00
|
|
|
(void **) &default_timer_queue, q, NULL);
|
|
|
|
if (p)
|
|
|
|
/* Got beat to the punch. */
|
2015-11-24 20:35:58 +00:00
|
|
|
RtlDeleteTimerQueueEx(q, NULL);
|
2008-09-08 11:10:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return default_timer_queue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* RtlCreateTimer (NTDLL.@)
|
|
|
|
*
|
|
|
|
* Creates a new timer associated with the given queue.
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* NewTimer [O] The newly created timer.
|
|
|
|
* TimerQueue [I] The queue to hold the timer.
|
|
|
|
* Callback [I] The callback to fire.
|
|
|
|
* Parameter [I] The argument for the callback.
|
|
|
|
* DueTime [I] The delay, in milliseconds, before first firing the
|
|
|
|
* timer.
|
|
|
|
* Period [I] The period, in milliseconds, at which to fire the timer
|
|
|
|
* after the first callback. If zero, the timer will only
|
|
|
|
* fire once. It still needs to be deleted with
|
|
|
|
* RtlDeleteTimer.
|
2015-11-24 20:35:58 +00:00
|
|
|
* Flags [I] Flags controlling the execution of the callback. In
|
2008-09-08 11:10:02 +00:00
|
|
|
* addition to the WT_* thread pool flags (see
|
|
|
|
* RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
|
|
|
|
* WT_EXECUTEONLYONCE are supported.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* Success: STATUS_SUCCESS.
|
|
|
|
* Failure: Any NTSTATUS code.
|
2004-07-08 00:40:31 +00:00
|
|
|
*/
|
2008-09-08 11:10:02 +00:00
|
|
|
NTSTATUS WINAPI RtlCreateTimer(HANDLE TimerQueue, PHANDLE NewTimer,
|
|
|
|
WAITORTIMERCALLBACKFUNC Callback,
|
|
|
|
PVOID Parameter, DWORD DueTime, DWORD Period,
|
|
|
|
ULONG Flags)
|
2004-07-08 00:40:31 +00:00
|
|
|
{
|
2008-09-08 11:10:02 +00:00
|
|
|
NTSTATUS status;
|
|
|
|
struct queue_timer *t;
|
|
|
|
struct timer_queue *q = get_timer_queue(TimerQueue);
|
2015-11-24 20:35:58 +00:00
|
|
|
|
|
|
|
if (!q) return STATUS_NO_MEMORY;
|
|
|
|
if (q->magic != TIMER_QUEUE_MAGIC) return STATUS_INVALID_HANDLE;
|
2008-09-08 11:10:02 +00:00
|
|
|
|
|
|
|
t = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *t);
|
|
|
|
if (!t)
|
|
|
|
return STATUS_NO_MEMORY;
|
|
|
|
|
|
|
|
t->q = q;
|
|
|
|
t->runcount = 0;
|
|
|
|
t->callback = Callback;
|
|
|
|
t->param = Parameter;
|
|
|
|
t->period = Period;
|
|
|
|
t->flags = Flags;
|
|
|
|
t->destroy = FALSE;
|
|
|
|
t->event = NULL;
|
|
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
|
|
|
if (q->quit)
|
|
|
|
status = STATUS_INVALID_HANDLE;
|
|
|
|
else
|
|
|
|
queue_add_timer(t, queue_current_time() + DueTime, TRUE);
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
|
|
*NewTimer = t;
|
|
|
|
else
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, t);
|
|
|
|
|
|
|
|
return status;
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
|
|
|
|
2019-09-21 10:46:01 +00:00
|
|
|
NTSTATUS
|
|
|
|
WINAPI
|
|
|
|
RtlSetTimer(
|
|
|
|
HANDLE TimerQueue,
|
|
|
|
PHANDLE NewTimer,
|
|
|
|
WAITORTIMERCALLBACKFUNC Callback,
|
|
|
|
PVOID Parameter,
|
|
|
|
DWORD DueTime,
|
|
|
|
DWORD Period,
|
|
|
|
ULONG Flags)
|
|
|
|
{
|
|
|
|
return RtlCreateTimer(TimerQueue,
|
|
|
|
NewTimer,
|
|
|
|
Callback,
|
|
|
|
Parameter,
|
|
|
|
DueTime,
|
|
|
|
Period,
|
|
|
|
Flags);
|
|
|
|
}
|
|
|
|
|
2008-09-08 11:10:02 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* RtlUpdateTimer (NTDLL.@)
|
|
|
|
*
|
|
|
|
* Changes the time at which a timer expires.
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* TimerQueue [I] The queue that holds the timer.
|
|
|
|
* Timer [I] The timer to update.
|
|
|
|
* DueTime [I] The delay, in milliseconds, before next firing the timer.
|
|
|
|
* Period [I] The period, in milliseconds, at which to fire the timer
|
|
|
|
* after the first callback. If zero, the timer will not
|
|
|
|
* refire once. It still needs to be deleted with
|
|
|
|
* RtlDeleteTimer.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* Success: STATUS_SUCCESS.
|
|
|
|
* Failure: Any NTSTATUS code.
|
|
|
|
*/
|
|
|
|
NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer,
|
|
|
|
DWORD DueTime, DWORD Period)
|
|
|
|
{
|
|
|
|
struct queue_timer *t = Timer;
|
|
|
|
struct timer_queue *q = t->q;
|
|
|
|
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
|
|
|
/* Can't change a timer if it was once-only or destroyed. */
|
|
|
|
if (t->expire != EXPIRE_NEVER)
|
|
|
|
{
|
|
|
|
t->period = Period;
|
|
|
|
queue_move_timer(t, queue_current_time() + DueTime, TRUE);
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* RtlDeleteTimer (NTDLL.@)
|
|
|
|
*
|
|
|
|
* Cancels a timer-queue timer.
|
|
|
|
*
|
|
|
|
* PARAMS
|
|
|
|
* TimerQueue [I] The queue that holds the timer.
|
|
|
|
* Timer [I] The timer to update.
|
|
|
|
* CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
|
|
|
|
* wait until the timer is finished firing all pending
|
|
|
|
* callbacks before returning. Otherwise, return
|
|
|
|
* immediately and set the timer is done.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not,
|
|
|
|
or if the completion event is NULL.
|
|
|
|
* Failure: Any NTSTATUS code.
|
|
|
|
*/
|
|
|
|
NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer,
|
|
|
|
HANDLE CompletionEvent)
|
|
|
|
{
|
|
|
|
struct queue_timer *t = Timer;
|
2009-01-28 14:08:13 +00:00
|
|
|
struct timer_queue *q;
|
2008-09-08 11:10:02 +00:00
|
|
|
NTSTATUS status = STATUS_PENDING;
|
|
|
|
HANDLE event = NULL;
|
|
|
|
|
2009-01-28 14:08:13 +00:00
|
|
|
if (!Timer)
|
|
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
|
|
q = t->q;
|
2008-09-08 11:10:02 +00:00
|
|
|
if (CompletionEvent == INVALID_HANDLE_VALUE)
|
2015-11-24 20:35:58 +00:00
|
|
|
{
|
|
|
|
status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
|
|
status = STATUS_PENDING;
|
|
|
|
}
|
2008-09-08 11:10:02 +00:00
|
|
|
else if (CompletionEvent)
|
|
|
|
event = CompletionEvent;
|
|
|
|
|
|
|
|
RtlEnterCriticalSection(&q->cs);
|
|
|
|
t->event = event;
|
|
|
|
if (t->runcount == 0 && event)
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
queue_destroy_timer(t);
|
|
|
|
RtlLeaveCriticalSection(&q->cs);
|
|
|
|
|
|
|
|
if (CompletionEvent == INVALID_HANDLE_VALUE && event)
|
|
|
|
{
|
|
|
|
if (status == STATUS_PENDING)
|
2015-11-24 20:35:58 +00:00
|
|
|
{
|
2008-09-08 11:10:02 +00:00
|
|
|
NtWaitForSingleObject(event, FALSE, NULL);
|
2015-11-24 20:35:58 +00:00
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
2008-09-08 11:10:02 +00:00
|
|
|
NtClose(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
2004-07-08 00:40:31 +00:00
|
|
|
|
2019-09-21 10:46:01 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
RtlCancelTimer(HANDLE TimerQueue, HANDLE Timer)
|
|
|
|
{
|
|
|
|
return RtlDeleteTimer(TimerQueue, Timer, NULL);
|
|
|
|
}
|
|
|
|
|
2004-07-08 00:40:31 +00:00
|
|
|
/*
|
2008-09-08 11:10:02 +00:00
|
|
|
* @implemented
|
2004-07-08 00:40:31 +00:00
|
|
|
*/
|
|
|
|
NTSTATUS
|
2005-10-19 17:03:38 +00:00
|
|
|
NTAPI
|
2008-09-08 11:10:02 +00:00
|
|
|
RtlDeleteTimerQueue(HANDLE TimerQueue)
|
2004-07-08 00:40:31 +00:00
|
|
|
{
|
2008-09-08 11:10:02 +00:00
|
|
|
return RtlDeleteTimerQueueEx(TimerQueue, INVALID_HANDLE_VALUE);
|
2004-07-08 00:40:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|