ntdll: Implement RtlDeleteTimer for kernel32's DeleteTimerQueueTimer.

This commit is contained in:
Dan Hipschman 2008-07-24 16:27:54 -07:00 committed by Alexandre Julliard
parent 396e47ef5c
commit 4c64d0cdfa
5 changed files with 139 additions and 16 deletions

View File

@ -1131,15 +1131,16 @@ BOOL WINAPI ChangeTimerQueueTimer( HANDLE TimerQueue, HANDLE Timer,
* *
* RETURNS * RETURNS
* nonzero on success or zero on failure * nonzero on success or zero on failure
*
* BUGS
* Unimplemented
*/ */
BOOL WINAPI DeleteTimerQueueTimer( HANDLE TimerQueue, HANDLE Timer, BOOL WINAPI DeleteTimerQueueTimer( HANDLE TimerQueue, HANDLE Timer,
HANDLE CompletionEvent ) HANDLE CompletionEvent )
{ {
FIXME("stub\n"); NTSTATUS status = RtlDeleteTimer(TimerQueue, Timer, CompletionEvent);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED); if (status != STATUS_SUCCESS)
{
SetLastError( RtlNtStatusToDosError(status) );
return FALSE;
}
return TRUE; return TRUE;
} }

View File

@ -566,13 +566,10 @@ static void CALLBACK timer_queue_cb2(PVOID p, BOOLEAN timedOut)
/* Note, XP SP2 does *not* do any deadlock checking, so passing /* Note, XP SP2 does *not* do any deadlock checking, so passing
INVALID_HANDLE_VALUE here will just hang. */ INVALID_HANDLE_VALUE here will just hang. */
ret = pDeleteTimerQueueTimer(d->q, d->t, NULL); ret = pDeleteTimerQueueTimer(d->q, d->t, NULL);
todo_wine
{
ok(!ret, "DeleteTimerQueueTimer\n"); ok(!ret, "DeleteTimerQueueTimer\n");
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n"); ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");
} }
} }
}
static void CALLBACK timer_queue_cb3(PVOID p, BOOLEAN timedOut) static void CALLBACK timer_queue_cb3(PVOID p, BOOLEAN timedOut)
{ {
@ -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) static void test_timer_queue(void)
{ {
HANDLE q, t1, t2, t3, t4, t5; HANDLE q, t1, t2, t3, t4, t5;
int n1, n2, n3, n4, n5; int n1, n2, n3, n4, n5;
struct timer_queue_data1 d2, d3, d4; struct timer_queue_data1 d2, d3, d4;
HANDLE e; HANDLE e, et1, et2;
BOOL ret; BOOL ret;
if (!pChangeTimerQueueTimer || !pCreateTimerQueue || !pCreateTimerQueueTimer if (!pChangeTimerQueueTimer || !pCreateTimerQueue || !pCreateTimerQueueTimer
@ -675,6 +680,14 @@ static void test_timer_queue(void)
/* Give them a chance to do some work. */ /* Give them a chance to do some work. */
Sleep(500); 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); ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE);
ok(ret, "DeleteTimerQueueEx\n"); ok(ret, "DeleteTimerQueueEx\n");
ok(n1 == 1, "Timer callback 1\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(n4 == 0, "Timer callback 4\n");
ok(n5 == 1, "Timer callback 5\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); 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"); skip("Failed to create timer queue descruction event\n");
return; return;
@ -693,12 +708,69 @@ static void test_timer_queue(void)
q = pCreateTimerQueue(); q = pCreateTimerQueue();
ok(q != NULL, "CreateTimerQueue\n"); 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); SetLastError(0xdeadbeef);
ret = pDeleteTimerQueueEx(q, e); ret = pDeleteTimerQueueEx(q, e);
ok(!ret, "DeleteTimerQueueEx\n"); ok(!ret, "DeleteTimerQueueEx\n");
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueEx\n"); ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueEx\n");
ok(WaitForSingleObject(e, 250) == WAIT_OBJECT_0, ok(WaitForSingleObject(e, 250) == WAIT_OBJECT_0,
"Timer destruction event not triggered\n"); "Queue destruction event not triggered\n");
CloseHandle(e); CloseHandle(e);
/* Test deleting/changing a timer in execution. */ /* Test deleting/changing a timer in execution. */
@ -749,10 +821,7 @@ static void test_timer_queue(void)
ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE); ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE);
ok(ret, "DeleteTimerQueueEx\n"); ok(ret, "DeleteTimerQueueEx\n");
ok(n1 == 1, "ChangeTimerQueueTimer\n"); ok(n1 == 1, "ChangeTimerQueueTimer\n");
todo_wine
{
ok(d2.num_calls == d2.max_calls, "DeleteTimerQueueTimer\n"); ok(d2.num_calls == d2.max_calls, "DeleteTimerQueueTimer\n");
}
ok(d3.num_calls == d3.max_calls, "ChangeTimerQueueTimer\n"); ok(d3.num_calls == d3.max_calls, "ChangeTimerQueueTimer\n");
ok(d4.num_calls == 1, "Timer flagged for deletion incorrectly\n"); ok(d4.num_calls == 1, "Timer flagged for deletion incorrectly\n");
} }

View File

@ -518,7 +518,7 @@
@ stdcall RtlDeleteRegistryValue(long ptr ptr) @ stdcall RtlDeleteRegistryValue(long ptr ptr)
@ stdcall RtlDeleteResource(ptr) @ stdcall RtlDeleteResource(ptr)
@ stdcall RtlDeleteSecurityObject(ptr) @ stdcall RtlDeleteSecurityObject(ptr)
# @ stub RtlDeleteTimer @ stdcall RtlDeleteTimer(ptr ptr ptr)
# @ stub RtlDeleteTimerQueue # @ stub RtlDeleteTimerQueue
@ stdcall RtlDeleteTimerQueueEx(ptr ptr) @ stdcall RtlDeleteTimerQueueEx(ptr ptr)
@ stdcall RtlDeregisterWait(ptr) @ stdcall RtlDeregisterWait(ptr)

View File

@ -545,6 +545,7 @@ struct queue_timer
ULONG flags; ULONG flags;
ULONGLONG expire; ULONGLONG expire;
BOOL destroy; /* timer should be deleted; once set, never unset */ BOOL destroy; /* timer should be deleted; once set, never unset */
HANDLE event; /* removal event */
}; };
struct timer_queue struct timer_queue
@ -569,6 +570,8 @@ static void queue_remove_timer(struct queue_timer *t)
assert(t->destroy); assert(t->destroy);
list_remove(&t->entry); list_remove(&t->entry);
if (t->event)
NtSetEvent(t->event, NULL);
RtlFreeHeap(GetProcessHeap(), 0, t); RtlFreeHeap(GetProcessHeap(), 0, t);
if (q->quit && list_count(&q->timers) == 0) if (q->quit && list_count(&q->timers) == 0)
@ -891,6 +894,7 @@ NTSTATUS WINAPI RtlCreateTimer(PHANDLE NewTimer, HANDLE TimerQueue,
t->period = Period; t->period = Period;
t->flags = Flags; t->flags = Flags;
t->destroy = FALSE; t->destroy = FALSE;
t->event = NULL;
status = STATUS_SUCCESS; status = STATUS_SUCCESS;
RtlEnterCriticalSection(&q->cs); RtlEnterCriticalSection(&q->cs);
@ -943,3 +947,51 @@ NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer,
return STATUS_SUCCESS; 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;
}

View File

@ -2129,6 +2129,7 @@ NTSYSAPI NTSTATUS WINAPI RtlDeleteCriticalSection(RTL_CRITICAL_SECTION *);
NTSYSAPI NTSTATUS WINAPI RtlDeleteRegistryValue(ULONG, PCWSTR, PCWSTR); NTSYSAPI NTSTATUS WINAPI RtlDeleteRegistryValue(ULONG, PCWSTR, PCWSTR);
NTSYSAPI void WINAPI RtlDeleteResource(LPRTL_RWLOCK); NTSYSAPI void WINAPI RtlDeleteResource(LPRTL_RWLOCK);
NTSYSAPI NTSTATUS WINAPI RtlDeleteSecurityObject(PSECURITY_DESCRIPTOR*); NTSYSAPI NTSTATUS WINAPI RtlDeleteSecurityObject(PSECURITY_DESCRIPTOR*);
NTSYSAPI NTSTATUS WINAPI RtlDeleteTimer(HANDLE, HANDLE, HANDLE);
NTSYSAPI NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE, HANDLE); NTSYSAPI NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE, HANDLE);
NTSYSAPI PRTL_USER_PROCESS_PARAMETERS WINAPI RtlDeNormalizeProcessParams(RTL_USER_PROCESS_PARAMETERS*); NTSYSAPI PRTL_USER_PROCESS_PARAMETERS WINAPI RtlDeNormalizeProcessParams(RTL_USER_PROCESS_PARAMETERS*);
NTSYSAPI NTSTATUS WINAPI RtlDeregisterWait(HANDLE); NTSYSAPI NTSTATUS WINAPI RtlDeregisterWait(HANDLE);