ntdll: Implement RtlDeleteTimer for kernel32's DeleteTimerQueueTimer.
This commit is contained in:
parent
396e47ef5c
commit
4c64d0cdfa
|
@ -1131,15 +1131,16 @@ BOOL WINAPI ChangeTimerQueueTimer( HANDLE TimerQueue, HANDLE Timer,
|
|||
*
|
||||
* RETURNS
|
||||
* nonzero on success or zero on failure
|
||||
*
|
||||
* BUGS
|
||||
* Unimplemented
|
||||
*/
|
||||
BOOL WINAPI DeleteTimerQueueTimer( HANDLE TimerQueue, HANDLE Timer,
|
||||
HANDLE CompletionEvent )
|
||||
{
|
||||
FIXME("stub\n");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
NTSTATUS status = RtlDeleteTimer(TimerQueue, Timer, CompletionEvent);
|
||||
if (status != STATUS_SUCCESS)
|
||||
{
|
||||
SetLastError( RtlNtStatusToDosError(status) );
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -566,11 +566,8 @@ static void CALLBACK timer_queue_cb2(PVOID p, BOOLEAN timedOut)
|
|||
/* Note, XP SP2 does *not* do any deadlock checking, so passing
|
||||
INVALID_HANDLE_VALUE here will just hang. */
|
||||
ret = pDeleteTimerQueueTimer(d->q, d->t, NULL);
|
||||
todo_wine
|
||||
{
|
||||
ok(!ret, "DeleteTimerQueueTimer\n");
|
||||
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,12 +601,20 @@ static void CALLBACK timer_queue_cb4(PVOID p, BOOLEAN timedOut)
|
|||
}
|
||||
}
|
||||
|
||||
static void CALLBACK timer_queue_cb5(PVOID p, BOOLEAN timedOut)
|
||||
{
|
||||
DWORD delay = (DWORD) p;
|
||||
ok(timedOut, "Timer callbacks should always time out\n");
|
||||
if (delay)
|
||||
Sleep(delay);
|
||||
}
|
||||
|
||||
static void test_timer_queue(void)
|
||||
{
|
||||
HANDLE q, t1, t2, t3, t4, t5;
|
||||
int n1, n2, n3, n4, n5;
|
||||
struct timer_queue_data1 d2, d3, d4;
|
||||
HANDLE e;
|
||||
HANDLE e, et1, et2;
|
||||
BOOL ret;
|
||||
|
||||
if (!pChangeTimerQueueTimer || !pCreateTimerQueue || !pCreateTimerQueueTimer
|
||||
|
@ -675,6 +680,14 @@ static void test_timer_queue(void)
|
|||
/* Give them a chance to do some work. */
|
||||
Sleep(500);
|
||||
|
||||
/* Test deleting a once-only timer. */
|
||||
ret = pDeleteTimerQueueTimer(q, t1, INVALID_HANDLE_VALUE);
|
||||
ok(ret, "DeleteTimerQueueTimer\n");
|
||||
|
||||
/* A periodic timer. */
|
||||
ret = pDeleteTimerQueueTimer(q, t2, INVALID_HANDLE_VALUE);
|
||||
ok(ret, "DeleteTimerQueueTimer\n");
|
||||
|
||||
ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE);
|
||||
ok(ret, "DeleteTimerQueueEx\n");
|
||||
ok(n1 == 1, "Timer callback 1\n");
|
||||
|
@ -682,9 +695,11 @@ static void test_timer_queue(void)
|
|||
ok(n4 == 0, "Timer callback 4\n");
|
||||
ok(n5 == 1, "Timer callback 5\n");
|
||||
|
||||
/* Test synchronous deletion of the queue with event trigger. */
|
||||
/* Test synchronous deletion of the timer/queue with event trigger. */
|
||||
e = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!e)
|
||||
et1 = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
et2 = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!e || !et1 || !et2)
|
||||
{
|
||||
skip("Failed to create timer queue descruction event\n");
|
||||
return;
|
||||
|
@ -693,12 +708,69 @@ static void test_timer_queue(void)
|
|||
q = pCreateTimerQueue();
|
||||
ok(q != NULL, "CreateTimerQueue\n");
|
||||
|
||||
/* Run once and finish quickly (should be done when we delete it). */
|
||||
t1 = NULL;
|
||||
ret = pCreateTimerQueueTimer(&t1, q, timer_queue_cb5, (PVOID) 0, 0,
|
||||
0, 0);
|
||||
ok(ret, "CreateTimerQueueTimer\n");
|
||||
ok(t1 != NULL, "CreateTimerQueueTimer\n");
|
||||
|
||||
/* Run once and finish slowly (shouldn't be done when we delete it). */
|
||||
t2 = NULL;
|
||||
ret = pCreateTimerQueueTimer(&t2, q, timer_queue_cb5, (PVOID) 1000, 0,
|
||||
0, 0);
|
||||
ok(ret, "CreateTimerQueueTimer\n");
|
||||
ok(t2 != NULL, "CreateTimerQueueTimer\n");
|
||||
|
||||
/* Run once and finish quickly (should be done when we delete it). */
|
||||
t3 = NULL;
|
||||
ret = pCreateTimerQueueTimer(&t3, q, timer_queue_cb5, (PVOID) 0, 0,
|
||||
0, 0);
|
||||
ok(ret, "CreateTimerQueueTimer\n");
|
||||
ok(t3 != NULL, "CreateTimerQueueTimer\n");
|
||||
|
||||
/* Run once and finish slowly (shouldn't be done when we delete it). */
|
||||
t4 = NULL;
|
||||
ret = pCreateTimerQueueTimer(&t4, q, timer_queue_cb5, (PVOID) 1000, 0,
|
||||
0, 0);
|
||||
ok(ret, "CreateTimerQueueTimer\n");
|
||||
ok(t4 != NULL, "CreateTimerQueueTimer\n");
|
||||
|
||||
/* Give them a chance to start. */
|
||||
Sleep(400);
|
||||
|
||||
/* DeleteTimerQueueTimer always returns PENDING with a NULL event,
|
||||
even if the timer is finished. */
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = pDeleteTimerQueueTimer(q, t1, NULL);
|
||||
ok(!ret, "DeleteTimerQueueTimer\n");
|
||||
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");
|
||||
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = pDeleteTimerQueueTimer(q, t2, NULL);
|
||||
ok(!ret, "DeleteTimerQueueTimer\n");
|
||||
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");
|
||||
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = pDeleteTimerQueueTimer(q, t3, et1);
|
||||
ok(ret, "DeleteTimerQueueTimer\n");
|
||||
ok(GetLastError() == 0xdeadbeef, "DeleteTimerQueueTimer\n");
|
||||
ok(WaitForSingleObject(et1, 250) == WAIT_OBJECT_0,
|
||||
"Timer destruction event not triggered\n");
|
||||
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = pDeleteTimerQueueTimer(q, t4, et2);
|
||||
ok(!ret, "DeleteTimerQueueTimer\n");
|
||||
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");
|
||||
ok(WaitForSingleObject(et2, 1000) == WAIT_OBJECT_0,
|
||||
"Timer destruction event not triggered\n");
|
||||
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = pDeleteTimerQueueEx(q, e);
|
||||
ok(!ret, "DeleteTimerQueueEx\n");
|
||||
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueEx\n");
|
||||
ok(WaitForSingleObject(e, 250) == WAIT_OBJECT_0,
|
||||
"Timer destruction event not triggered\n");
|
||||
"Queue destruction event not triggered\n");
|
||||
CloseHandle(e);
|
||||
|
||||
/* Test deleting/changing a timer in execution. */
|
||||
|
@ -749,10 +821,7 @@ static void test_timer_queue(void)
|
|||
ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE);
|
||||
ok(ret, "DeleteTimerQueueEx\n");
|
||||
ok(n1 == 1, "ChangeTimerQueueTimer\n");
|
||||
todo_wine
|
||||
{
|
||||
ok(d2.num_calls == d2.max_calls, "DeleteTimerQueueTimer\n");
|
||||
}
|
||||
ok(d3.num_calls == d3.max_calls, "ChangeTimerQueueTimer\n");
|
||||
ok(d4.num_calls == 1, "Timer flagged for deletion incorrectly\n");
|
||||
}
|
||||
|
|
|
@ -518,7 +518,7 @@
|
|||
@ stdcall RtlDeleteRegistryValue(long ptr ptr)
|
||||
@ stdcall RtlDeleteResource(ptr)
|
||||
@ stdcall RtlDeleteSecurityObject(ptr)
|
||||
# @ stub RtlDeleteTimer
|
||||
@ stdcall RtlDeleteTimer(ptr ptr ptr)
|
||||
# @ stub RtlDeleteTimerQueue
|
||||
@ stdcall RtlDeleteTimerQueueEx(ptr ptr)
|
||||
@ stdcall RtlDeregisterWait(ptr)
|
||||
|
|
|
@ -545,6 +545,7 @@ struct queue_timer
|
|||
ULONG flags;
|
||||
ULONGLONG expire;
|
||||
BOOL destroy; /* timer should be deleted; once set, never unset */
|
||||
HANDLE event; /* removal event */
|
||||
};
|
||||
|
||||
struct timer_queue
|
||||
|
@ -569,6 +570,8 @@ static void queue_remove_timer(struct queue_timer *t)
|
|||
assert(t->destroy);
|
||||
|
||||
list_remove(&t->entry);
|
||||
if (t->event)
|
||||
NtSetEvent(t->event, NULL);
|
||||
RtlFreeHeap(GetProcessHeap(), 0, t);
|
||||
|
||||
if (q->quit && list_count(&q->timers) == 0)
|
||||
|
@ -891,6 +894,7 @@ NTSTATUS WINAPI RtlCreateTimer(PHANDLE NewTimer, HANDLE TimerQueue,
|
|||
t->period = Period;
|
||||
t->flags = Flags;
|
||||
t->destroy = FALSE;
|
||||
t->event = NULL;
|
||||
|
||||
status = STATUS_SUCCESS;
|
||||
RtlEnterCriticalSection(&q->cs);
|
||||
|
@ -943,3 +947,51 @@ NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer,
|
|||
|
||||
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 timer_queue *q = TimerQueue;
|
||||
struct queue_timer *t = Timer;
|
||||
NTSTATUS status = STATUS_PENDING;
|
||||
HANDLE event = NULL;
|
||||
|
||||
if (CompletionEvent == INVALID_HANDLE_VALUE)
|
||||
status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, FALSE, FALSE);
|
||||
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);
|
||||
NtClose(event);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -2129,6 +2129,7 @@ NTSYSAPI NTSTATUS WINAPI RtlDeleteCriticalSection(RTL_CRITICAL_SECTION *);
|
|||
NTSYSAPI NTSTATUS WINAPI RtlDeleteRegistryValue(ULONG, PCWSTR, PCWSTR);
|
||||
NTSYSAPI void WINAPI RtlDeleteResource(LPRTL_RWLOCK);
|
||||
NTSYSAPI NTSTATUS WINAPI RtlDeleteSecurityObject(PSECURITY_DESCRIPTOR*);
|
||||
NTSYSAPI NTSTATUS WINAPI RtlDeleteTimer(HANDLE, HANDLE, HANDLE);
|
||||
NTSYSAPI NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE, HANDLE);
|
||||
NTSYSAPI PRTL_USER_PROCESS_PARAMETERS WINAPI RtlDeNormalizeProcessParams(RTL_USER_PROCESS_PARAMETERS*);
|
||||
NTSYSAPI NTSTATUS WINAPI RtlDeregisterWait(HANDLE);
|
||||
|
|
Loading…
Reference in New Issue