mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 22:56:00 +00:00
[RTL] Partial revert of Wine timer code in thread pooling (#8229)
* [SDK] Partial Revert of 0bf42067d2
It was found that the timer code from WINE doesn't work as well as ours,
This is a revert from that part of the PR as it doesn't change any of the real threadpool code in all reality. Thanks to the investigation of Julenuri and Simone
JIRA issue: CORE-20245
This commit is contained in:
parent
3917c60749
commit
02343c22f6
4 changed files with 774 additions and 1 deletions
|
@ -89,6 +89,7 @@ list(APPEND SOURCE
|
||||||
unicodeprefix.c
|
unicodeprefix.c
|
||||||
vectoreh.c
|
vectoreh.c
|
||||||
version.c
|
version.c
|
||||||
|
wait.c
|
||||||
workitem.c
|
workitem.c
|
||||||
rtl.h)
|
rtl.h)
|
||||||
|
|
||||||
|
|
|
@ -837,6 +837,8 @@ static void WINAPI timer_queue_thread_proc(LPVOID p)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef __REACTOS__
|
||||||
|
|
||||||
static void queue_destroy_timer(struct queue_timer *t)
|
static void queue_destroy_timer(struct queue_timer *t)
|
||||||
{
|
{
|
||||||
/* We MUST hold the queue cs while calling this function. */
|
/* We MUST hold the queue cs while calling this function. */
|
||||||
|
@ -1137,7 +1139,7 @@ NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer,
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* timerqueue_thread_proc (internal)
|
* timerqueue_thread_proc (internal)
|
||||||
*/
|
*/
|
||||||
|
@ -3380,6 +3382,7 @@ NTSTATUS WINAPI TpQueryPoolStackInformation( TP_POOL *pool, TP_POOL_STACK_INFORM
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef __REACTOS__
|
||||||
static void CALLBACK rtl_wait_callback( TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result )
|
static void CALLBACK rtl_wait_callback( TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result )
|
||||||
{
|
{
|
||||||
struct threadpool_object *object = impl_from_TP_WAIT(wait);
|
struct threadpool_object *object = impl_from_TP_WAIT(wait);
|
||||||
|
@ -3502,6 +3505,7 @@ NTSTATUS WINAPI RtlDeregisterWait(HANDLE WaitHandle)
|
||||||
{
|
{
|
||||||
return RtlDeregisterWaitEx(WaitHandle, NULL);
|
return RtlDeregisterWaitEx(WaitHandle, NULL);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __REACTOS__
|
#ifdef __REACTOS__
|
||||||
VOID
|
VOID
|
||||||
|
|
|
@ -64,6 +64,405 @@ struct timer_queue
|
||||||
#define EXPIRE_NEVER (~(ULONGLONG) 0)
|
#define EXPIRE_NEVER (~(ULONGLONG) 0)
|
||||||
#define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */
|
#define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */
|
||||||
|
|
||||||
|
static void queue_remove_timer(struct queue_timer *t)
|
||||||
|
{
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
if (q->quit && list_empty(&q->timers))
|
||||||
|
NtSetEvent(q->event, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timer_cleanup_callback(struct queue_timer *t)
|
||||||
|
{
|
||||||
|
struct timer_queue *q = t->q;
|
||||||
|
RtlEnterCriticalSection(&q->cs);
|
||||||
|
|
||||||
|
assert(0 < t->runcount);
|
||||||
|
--t->runcount;
|
||||||
|
|
||||||
|
if (t->destroy && t->runcount == 0)
|
||||||
|
queue_remove_timer(t);
|
||||||
|
|
||||||
|
RtlLeaveCriticalSection(&q->cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VOID WINAPI timer_callback_wrapper(LPVOID p)
|
||||||
|
{
|
||||||
|
struct queue_timer *t = p;
|
||||||
|
t->callback(t->param, TRUE);
|
||||||
|
timer_cleanup_callback(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ULONGLONG queue_current_time(void)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER now, freq;
|
||||||
|
NtQueryPerformanceCounter(&now, &freq);
|
||||||
|
return now.QuadPart * 1000 / freq.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
ULONGLONG now, next;
|
||||||
|
t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
|
||||||
|
if (!t->destroy && t->expire <= ((now = queue_current_time())))
|
||||||
|
{
|
||||||
|
++t->runcount;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
NTSTATUS status = RtlQueueWorkItem(timer_callback_wrapper, t, flags);
|
||||||
|
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();
|
||||||
|
timeout = t->expire < time ? 0 : (ULONG)(t->expire - time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RtlLeaveCriticalSection(&q->cs);
|
||||||
|
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD WINAPI timer_queue_thread_proc(LPVOID p)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
if (q->quit && list_empty(&q->timers))
|
||||||
|
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);
|
||||||
|
q->magic = 0;
|
||||||
|
RtlFreeHeap(RtlGetProcessHeap(), 0, q);
|
||||||
|
RtlpExitThreadFunc(STATUS_SUCCESS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
q->magic = TIMER_QUEUE_MAGIC;
|
||||||
|
status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
|
||||||
|
if (status != STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
RtlFreeHeap(RtlGetProcessHeap(), 0, q);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = RtlpStartThreadFunc(timer_queue_thread_proc, q, &q->thread);
|
||||||
|
if (status != STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
NtClose(q->event);
|
||||||
|
RtlFreeHeap(RtlGetProcessHeap(), 0, q);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
NtResumeThread(q->thread, NULL);
|
||||||
|
*NewTimerQueue = q;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
if (!q || q->magic != TIMER_QUEUE_MAGIC)
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
static struct timer_queue *default_timer_queue;
|
||||||
|
|
||||||
|
if (TimerQueue)
|
||||||
|
return TimerQueue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!default_timer_queue)
|
||||||
|
{
|
||||||
|
HANDLE q;
|
||||||
|
NTSTATUS status = RtlCreateTimerQueue(&q);
|
||||||
|
if (status == STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
PVOID p = InterlockedCompareExchangePointer(
|
||||||
|
(void **) &default_timer_queue, q, NULL);
|
||||||
|
if (p)
|
||||||
|
/* Got beat to the punch. */
|
||||||
|
RtlDeleteTimerQueueEx(q, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
* Flags [I] Flags controlling the execution of the callback. In
|
||||||
|
* addition to the WT_* thread pool flags (see
|
||||||
|
* RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
|
||||||
|
* WT_EXECUTEONLYONCE are supported.
|
||||||
|
*
|
||||||
|
* RETURNS
|
||||||
|
* Success: STATUS_SUCCESS.
|
||||||
|
* Failure: Any NTSTATUS code.
|
||||||
|
*/
|
||||||
|
NTSTATUS WINAPI RtlCreateTimer(HANDLE TimerQueue, PHANDLE NewTimer,
|
||||||
|
WAITORTIMERCALLBACKFUNC Callback,
|
||||||
|
PVOID Parameter, DWORD DueTime, DWORD Period,
|
||||||
|
ULONG Flags)
|
||||||
|
{
|
||||||
|
NTSTATUS status;
|
||||||
|
struct queue_timer *t;
|
||||||
|
struct timer_queue *q = get_timer_queue(TimerQueue);
|
||||||
|
|
||||||
|
if (!q) return STATUS_NO_MEMORY;
|
||||||
|
if (q->magic != TIMER_QUEUE_MAGIC) return STATUS_INVALID_HANDLE;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
WINAPI
|
WINAPI
|
||||||
RtlSetTimer(
|
RtlSetTimer(
|
||||||
|
@ -84,6 +483,100 @@ RtlSetTimer(
|
||||||
Flags);
|
Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* 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;
|
||||||
|
struct timer_queue *q;
|
||||||
|
NTSTATUS status = STATUS_PENDING;
|
||||||
|
HANDLE event = NULL;
|
||||||
|
|
||||||
|
if (!Timer)
|
||||||
|
return STATUS_INVALID_PARAMETER_1;
|
||||||
|
q = t->q;
|
||||||
|
if (CompletionEvent == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
|
||||||
|
if (status == STATUS_SUCCESS)
|
||||||
|
status = STATUS_PENDING;
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
NtWaitForSingleObject(event, FALSE, NULL);
|
||||||
|
status = STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
NtClose(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @implemented
|
* @implemented
|
||||||
*/
|
*/
|
||||||
|
|
275
sdk/lib/rtl/wait.c
Normal file
275
sdk/lib/rtl/wait.c
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
/*
|
||||||
|
* COPYRIGHT: See COPYING in the top level directory
|
||||||
|
* PROJECT: ReactOS system libraries
|
||||||
|
* PURPOSE: Rtl user wait functions
|
||||||
|
* FILE: lib/rtl/wait.c
|
||||||
|
* PROGRAMERS:
|
||||||
|
* Alex Ionescu (alex@relsoft.net)
|
||||||
|
* Eric Kohl
|
||||||
|
* KJK::Hyperion
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* INCLUDES *****************************************************************/
|
||||||
|
|
||||||
|
#include <rtl.h>
|
||||||
|
|
||||||
|
#define NDEBUG
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
|
typedef struct _RTLP_WAIT
|
||||||
|
{
|
||||||
|
HANDLE Object;
|
||||||
|
BOOLEAN CallbackInProgress;
|
||||||
|
HANDLE CancelEvent;
|
||||||
|
LONG DeleteCount;
|
||||||
|
HANDLE CompletionEvent;
|
||||||
|
ULONG Flags;
|
||||||
|
WAITORTIMERCALLBACKFUNC Callback;
|
||||||
|
PVOID Context;
|
||||||
|
ULONG Milliseconds;
|
||||||
|
} RTLP_WAIT, *PRTLP_WAIT;
|
||||||
|
|
||||||
|
/* PRIVATE FUNCTIONS *******************************************************/
|
||||||
|
|
||||||
|
static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout )
|
||||||
|
{
|
||||||
|
if (timeout == INFINITE) return NULL;
|
||||||
|
pTime->QuadPart = (ULONGLONG)timeout * -10000;
|
||||||
|
return pTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VOID
|
||||||
|
NTAPI
|
||||||
|
Wait_thread_proc(LPVOID Arg)
|
||||||
|
{
|
||||||
|
PRTLP_WAIT Wait = (PRTLP_WAIT) Arg;
|
||||||
|
NTSTATUS Status;
|
||||||
|
BOOLEAN alertable = (Wait->Flags & WT_EXECUTEINIOTHREAD) != 0;
|
||||||
|
HANDLE handles[2] = { Wait->CancelEvent, Wait->Object };
|
||||||
|
LARGE_INTEGER timeout;
|
||||||
|
HANDLE completion_event;
|
||||||
|
|
||||||
|
// TRACE("\n");
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
Status = NtWaitForMultipleObjects( 2,
|
||||||
|
handles,
|
||||||
|
WaitAny,
|
||||||
|
alertable,
|
||||||
|
get_nt_timeout( &timeout, Wait->Milliseconds ) );
|
||||||
|
|
||||||
|
if (Status == STATUS_WAIT_1 || Status == STATUS_TIMEOUT)
|
||||||
|
{
|
||||||
|
BOOLEAN TimerOrWaitFired;
|
||||||
|
|
||||||
|
if (Status == STATUS_WAIT_1)
|
||||||
|
{
|
||||||
|
// TRACE( "object %p signaled, calling callback %p with context %p\n",
|
||||||
|
// Wait->Object, Wait->Callback,
|
||||||
|
// Wait->Context );
|
||||||
|
TimerOrWaitFired = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TRACE( "wait for object %p timed out, calling callback %p with context %p\n",
|
||||||
|
// Wait->Object, Wait->Callback,
|
||||||
|
// Wait->Context );
|
||||||
|
TimerOrWaitFired = TRUE;
|
||||||
|
}
|
||||||
|
Wait->CallbackInProgress = TRUE;
|
||||||
|
Wait->Callback( Wait->Context, TimerOrWaitFired );
|
||||||
|
Wait->CallbackInProgress = FALSE;
|
||||||
|
|
||||||
|
if (Wait->Flags & WT_EXECUTEONLYONCE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (Status != STATUS_USER_APC)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
completion_event = Wait->CompletionEvent;
|
||||||
|
if (completion_event) NtSetEvent( completion_event, NULL );
|
||||||
|
|
||||||
|
if (InterlockedIncrement( &Wait->DeleteCount ) == 2 )
|
||||||
|
{
|
||||||
|
NtClose( Wait->CancelEvent );
|
||||||
|
RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* FUNCTIONS ***************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* RtlRegisterWait
|
||||||
|
*
|
||||||
|
* Registers a wait for a handle to become signaled.
|
||||||
|
*
|
||||||
|
* PARAMS
|
||||||
|
* NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it.
|
||||||
|
* Object [I] Object to wait to become signaled.
|
||||||
|
* Callback [I] Callback function to execute when the wait times out or the handle is signaled.
|
||||||
|
* Context [I] Context to pass to the callback function when it is executed.
|
||||||
|
* Milliseconds [I] Number of milliseconds to wait before timing out.
|
||||||
|
* Flags [I] Flags. See notes.
|
||||||
|
*
|
||||||
|
* RETURNS
|
||||||
|
* Success: STATUS_SUCCESS.
|
||||||
|
* Failure: Any NTSTATUS code.
|
||||||
|
*
|
||||||
|
* NOTES
|
||||||
|
* Flags can be one or more of the following:
|
||||||
|
*|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
|
||||||
|
*|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
|
||||||
|
*|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
|
||||||
|
*|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
|
||||||
|
*|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
RtlRegisterWait(PHANDLE NewWaitObject,
|
||||||
|
HANDLE Object,
|
||||||
|
WAITORTIMERCALLBACKFUNC Callback,
|
||||||
|
PVOID Context,
|
||||||
|
ULONG Milliseconds,
|
||||||
|
ULONG Flags)
|
||||||
|
{
|
||||||
|
PRTLP_WAIT Wait;
|
||||||
|
NTSTATUS Status;
|
||||||
|
|
||||||
|
//TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags );
|
||||||
|
|
||||||
|
Wait = RtlAllocateHeap( RtlGetProcessHeap(), 0, sizeof(RTLP_WAIT) );
|
||||||
|
if (!Wait)
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
|
||||||
|
Wait->Object = Object;
|
||||||
|
Wait->Callback = Callback;
|
||||||
|
Wait->Context = Context;
|
||||||
|
Wait->Milliseconds = Milliseconds;
|
||||||
|
Wait->Flags = Flags;
|
||||||
|
Wait->CallbackInProgress = FALSE;
|
||||||
|
Wait->DeleteCount = 0;
|
||||||
|
Wait->CompletionEvent = NULL;
|
||||||
|
|
||||||
|
Status = NtCreateEvent( &Wait->CancelEvent,
|
||||||
|
EVENT_ALL_ACCESS,
|
||||||
|
NULL,
|
||||||
|
NotificationEvent,
|
||||||
|
FALSE );
|
||||||
|
|
||||||
|
if (Status != STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Flags = Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD |
|
||||||
|
WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION);
|
||||||
|
|
||||||
|
Status = RtlQueueWorkItem( Wait_thread_proc,
|
||||||
|
Wait,
|
||||||
|
Flags );
|
||||||
|
|
||||||
|
if (Status != STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
NtClose( Wait->CancelEvent );
|
||||||
|
RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
*NewWaitObject = Wait;
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* RtlDeregisterWaitEx
|
||||||
|
*
|
||||||
|
* Cancels a wait operation and frees the resources associated with calling
|
||||||
|
* RtlRegisterWait().
|
||||||
|
*
|
||||||
|
* PARAMS
|
||||||
|
* WaitObject [I] Handle to the wait object to free.
|
||||||
|
*
|
||||||
|
* RETURNS
|
||||||
|
* Success: STATUS_SUCCESS.
|
||||||
|
* Failure: Any NTSTATUS code.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
RtlDeregisterWaitEx(HANDLE WaitHandle,
|
||||||
|
HANDLE CompletionEvent)
|
||||||
|
{
|
||||||
|
PRTLP_WAIT Wait = (PRTLP_WAIT) WaitHandle;
|
||||||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
//TRACE( "(%p)\n", WaitHandle );
|
||||||
|
|
||||||
|
NtSetEvent( Wait->CancelEvent, NULL );
|
||||||
|
if (Wait->CallbackInProgress)
|
||||||
|
{
|
||||||
|
if (CompletionEvent != NULL)
|
||||||
|
{
|
||||||
|
if (CompletionEvent == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
Status = NtCreateEvent( &CompletionEvent,
|
||||||
|
EVENT_ALL_ACCESS,
|
||||||
|
NULL,
|
||||||
|
NotificationEvent,
|
||||||
|
FALSE );
|
||||||
|
|
||||||
|
if (Status != STATUS_SUCCESS)
|
||||||
|
return Status;
|
||||||
|
|
||||||
|
(void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent );
|
||||||
|
|
||||||
|
if (Wait->CallbackInProgress)
|
||||||
|
NtWaitForSingleObject( CompletionEvent, FALSE, NULL );
|
||||||
|
|
||||||
|
NtClose( CompletionEvent );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent );
|
||||||
|
|
||||||
|
if (Wait->CallbackInProgress)
|
||||||
|
Status = STATUS_PENDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Status = STATUS_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InterlockedIncrement( &Wait->DeleteCount ) == 2 )
|
||||||
|
{
|
||||||
|
Status = STATUS_SUCCESS;
|
||||||
|
NtClose( Wait->CancelEvent );
|
||||||
|
RtlFreeHeap( RtlGetProcessHeap(), 0, Wait );
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* RtlDeregisterWait
|
||||||
|
*
|
||||||
|
* Cancels a wait operation and frees the resources associated with calling
|
||||||
|
* RtlRegisterWait().
|
||||||
|
*
|
||||||
|
* PARAMS
|
||||||
|
* WaitObject [I] Handle to the wait object to free.
|
||||||
|
*
|
||||||
|
* RETURNS
|
||||||
|
* Success: STATUS_SUCCESS.
|
||||||
|
* Failure: Any NTSTATUS code.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
RtlDeregisterWait(HANDLE WaitHandle)
|
||||||
|
{
|
||||||
|
return RtlDeregisterWaitEx(WaitHandle, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EOF */
|
Loading…
Add table
Add a link
Reference in a new issue