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
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue