Commit 2b287a44 authored by Dan Hipschman's avatar Dan Hipschman Committed by Alexandre Julliard

ntdll: Fix a small timer-queue bug whereby a deleted timer may still expire.

parent a0abf988
......@@ -609,11 +609,45 @@ static void CALLBACK timer_queue_cb5(PVOID p, BOOLEAN timedOut)
Sleep(delay);
}
static void CALLBACK timer_queue_cb6(PVOID p, BOOLEAN timedOut)
{
struct timer_queue_data1 *d = p;
ok(timedOut, "Timer callbacks should always time out\n");
/* This tests an original implementation bug where a deleted timer may get
to run, but it is tricky to set up. */
if (d->q && d->num_calls++ == 0)
{
/* First run: delete ourselves, then insert and remove a timer
that goes in front of us in the sorted timeout list. Once
removed, we will still timeout at the faster timer's due time,
but this should be a no-op if we are bug-free. There should
not be a second run. We can test the value of num_calls later. */
BOOL ret;
HANDLE t;
/* The delete will pend while we are in this callback. */
SetLastError(0xdeadbeef);
ret = pDeleteTimerQueueTimer(d->q, d->t, NULL);
ok(!ret, "DeleteTimerQueueTimer\n");
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");
ret = pCreateTimerQueueTimer(&t, d->q, timer_queue_cb1, NULL, 100, 0, 0);
ok(ret, "CreateTimerQueueTimer\n");
ok(t != NULL, "CreateTimerQueueTimer\n");
ret = pDeleteTimerQueueTimer(d->q, t, INVALID_HANDLE_VALUE);
ok(ret, "DeleteTimerQueueTimer\n");
/* Now we stay alive by hanging around in the callback. */
Sleep(500);
}
}
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;
struct timer_queue_data1 d1, d2, d3, d4;
HANDLE e, et1, et2;
BOOL ret;
......@@ -824,6 +858,28 @@ static void test_timer_queue(void)
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");
/* Test an obscure bug that was in the original implementation. */
q = pCreateTimerQueue();
ok(q != NULL, "CreateTimerQueue\n");
/* All the work is done in the callback. */
d1.t = t1 = NULL;
d1.num_calls = 0;
d1.q = q;
ret = pCreateTimerQueueTimer(&t1, q, timer_queue_cb6, &d1, 100,
100, WT_EXECUTELONGFUNCTION);
d1.t = t1;
ok(ret, "CreateTimerQueueTimer\n");
ok(t1 != NULL, "CreateTimerQueueTimer\n");
Sleep(750);
SetLastError(0xdeadbeef);
ret = pDeleteTimerQueueEx(q, NULL);
ok(!ret, "DeleteTimerQueueEx\n");
ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueEx\n");
ok(d1.num_calls == 1, "DeleteTimerQueueTimer\n");
}
START_TEST(sync)
......
......@@ -643,7 +643,7 @@ static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time,
static void queue_timer_expire(struct timer_queue *q)
{
struct queue_timer *t;
struct queue_timer *t = NULL;
RtlEnterCriticalSection(&q->cs);
if (list_head(&q->timers))
......@@ -656,9 +656,9 @@ static void queue_timer_expire(struct timer_queue *q)
t, t->period ? queue_current_time() + t->period : EXPIRE_NEVER,
FALSE);
}
else
t = NULL;
}
else
t = NULL;
RtlLeaveCriticalSection(&q->cs);
if (t)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment