Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntoskrnl.exe/ntoskrnl.c | 8 ++++++-- include/ddk/wdm.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index bfac3970117..d50eac6c821 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -3079,9 +3079,13 @@ MM_SYSTEMSIZE WINAPI MmQuerySystemSize(void) /*********************************************************************** * KeInitializeDpc (NTOSKRNL.EXE.@) */ -VOID WINAPI KeInitializeDpc(PRKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine, PVOID DeferredContext) +void WINAPI KeInitializeDpc(KDPC *dpc, PKDEFERRED_ROUTINE deferred_routine, void *deferred_context) { - FIXME("stub\n"); + FIXME("dpc %p, deferred_routine %p, deferred_context %p semi-stub.\n", + dpc, deferred_routine, deferred_context); + + dpc->DeferredRoutine = deferred_routine; + dpc->DeferredContext = deferred_context; }
/*********************************************************************** diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 8368f8c6e9f..9dce98516b6 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1695,6 +1695,7 @@ void WINAPI KeEnterCriticalRegion(void); void WINAPI KeGenericCallDpc(PKDEFERRED_ROUTINE,PVOID); ULONG WINAPI KeGetCurrentProcessorNumber(void); PKTHREAD WINAPI KeGetCurrentThread(void); +void WINAPI KeInitializeDpc(KDPC*,PKDEFERRED_ROUTINE,void*); void WINAPI KeInitializeEvent(PRKEVENT,EVENT_TYPE,BOOLEAN); void WINAPI KeInitializeMutex(PRKMUTEX,ULONG); void WINAPI KeInitializeSemaphore(PRKSEMAPHORE,LONG,LONG);
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +- dlls/ntoskrnl.exe/sync.c | 7 +++++++ include/ddk/wdm.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index ec409d33240..72a620a3f7a 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -642,7 +642,7 @@ @ stdcall KeSetSystemAffinityThreadEx(long) @ stdcall KeSetTargetProcessorDpc(ptr long) @ stub KeSetTimeIncrement -@ stub KeSetTimer +@ stdcall KeSetTimer(ptr int64 ptr) @ stdcall KeSetTimerEx(ptr int64 long ptr) @ stub KeStackAttachProcess @ stub KeSynchronizeExecution diff --git a/dlls/ntoskrnl.exe/sync.c b/dlls/ntoskrnl.exe/sync.c index 647d427ee5d..d5a635efad7 100644 --- a/dlls/ntoskrnl.exe/sync.c +++ b/dlls/ntoskrnl.exe/sync.c @@ -1258,3 +1258,10 @@ void WINAPI IoReleaseRemoveLockAndWaitEx( IO_REMOVE_LOCK *lock, void *tag, ULONG else if (count > 0) KeWaitForSingleObject( &lock->Common.RemoveEvent, Executive, KernelMode, FALSE, NULL ); } + +BOOLEAN WINAPI KeSetTimer(KTIMER *timer, LARGE_INTEGER duetime, KDPC *dpc) +{ + TRACE("timer %p, duetime %I64x, dpc %p.\n", timer, duetime.QuadPart, dpc); + + return KeSetTimerEx(timer, duetime, 0, dpc); +} diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 9dce98516b6..38a3098994b 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1720,6 +1720,7 @@ LONG WINAPI KeSetEvent(PRKEVENT,KPRIORITY,BOOLEAN); KPRIORITY WINAPI KeSetPriorityThread(PKTHREAD,KPRIORITY); void WINAPI KeSetSystemAffinityThread(KAFFINITY); KAFFINITY WINAPI KeSetSystemAffinityThreadEx(KAFFINITY affinity); +BOOLEAN WINAPI KeSetTimer(KTIMER*,LARGE_INTEGER,KDPC*); BOOLEAN WINAPI KeSetTimerEx(KTIMER*,LARGE_INTEGER,LONG,KDPC*); void WINAPI KeSignalCallDpcDone(void*); BOOLEAN WINAPI KeSignalCallDpcSynchronize(void*);
This is needed to call the DPC callbacks later as SetWaitableTimer() will call the callback only when the thread is in alertable wait state.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntoskrnl.exe/sync.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/dlls/ntoskrnl.exe/sync.c b/dlls/ntoskrnl.exe/sync.c index d5a635efad7..1954b8befb7 100644 --- a/dlls/ntoskrnl.exe/sync.c +++ b/dlls/ntoskrnl.exe/sync.c @@ -388,6 +388,15 @@ LONG WINAPI KeReleaseMutex( PRKMUTEX mutex, BOOLEAN wait ) return ret; }
+static void CALLBACK ke_timer_complete_proc(PTP_CALLBACK_INSTANCE instance, void *timer_, PTP_TIMER tp_timer) +{ + KTIMER *timer = timer_; + + TRACE("instance %p, timer %p, tp_timer %p.\n", instance, timer, tp_timer); + + SetEvent(timer->Header.WaitListHead.Blink); +} + /*********************************************************************** * KeInitializeTimerEx (NTOSKRNL.EXE.@) */ @@ -431,9 +440,18 @@ BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period,
ret = timer->Header.Inserted; timer->Header.Inserted = TRUE; - timer->Header.WaitListHead.Blink = CreateWaitableTimerW( NULL, timer->Header.Type == TYPE_MANUAL_TIMER, NULL ); - SetWaitableTimer( timer->Header.WaitListHead.Blink, &duetime, period, NULL, NULL, FALSE ); + timer->Header.WaitListHead.Blink = CreateEventW( NULL, timer->Header.Type == TYPE_MANUAL_TIMER, FALSE, NULL );
+ if (!timer->TimerListEntry.Blink) + timer->TimerListEntry.Blink = (void *)CreateThreadpoolTimer(ke_timer_complete_proc, timer, NULL); + + if (!timer->TimerListEntry.Blink) + ERR("Could not create thread pool timer.\n"); + + timer->DueTime.QuadPart = duetime.QuadPart; + timer->Period = period; + + SetThreadpoolTimer((TP_TIMER *)timer->TimerListEntry.Blink, (FILETIME *)&duetime, period, 0); LeaveCriticalSection( &sync_cs );
return ret; @@ -446,6 +464,21 @@ BOOLEAN WINAPI KeCancelTimer( KTIMER *timer ) TRACE("timer %p.\n", timer);
EnterCriticalSection( &sync_cs ); + if (timer->TimerListEntry.Blink) + { + SetThreadpoolTimer((TP_TIMER *)timer->TimerListEntry.Blink, NULL, 0, 0); + + LeaveCriticalSection( &sync_cs ); + WaitForThreadpoolTimerCallbacks((TP_TIMER *)timer->TimerListEntry.Blink, TRUE); + EnterCriticalSection( &sync_cs ); + + if (timer->TimerListEntry.Blink) + { + CloseThreadpoolTimer((TP_TIMER *)timer->TimerListEntry.Blink); + timer->TimerListEntry.Blink = NULL; + } + } + ret = timer->Header.Inserted; timer->Header.Inserted = FALSE; CloseHandle(timer->Header.WaitListHead.Blink);
As the consequent testing shows we can't just close the events on timer reset. That would result in wrong wait results for current waiters.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntoskrnl.exe/sync.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/dlls/ntoskrnl.exe/sync.c b/dlls/ntoskrnl.exe/sync.c index 1954b8befb7..912dc046bac 100644 --- a/dlls/ntoskrnl.exe/sync.c +++ b/dlls/ntoskrnl.exe/sync.c @@ -83,9 +83,11 @@ NTSTATUS WINAPI KeWaitForMultipleObjects(ULONG count, void *pobjs[], { switch (objs[i]->Type) { + case TYPE_MANUAL_TIMER: case TYPE_MANUAL_EVENT: objs[i]->WaitListHead.Blink = CreateEventW( NULL, TRUE, objs[i]->SignalState, NULL ); break; + case TYPE_AUTO_TIMER: case TYPE_AUTO_EVENT: objs[i]->WaitListHead.Blink = CreateEventW( NULL, FALSE, objs[i]->SignalState, NULL ); break; @@ -99,9 +101,6 @@ NTSTATUS WINAPI KeWaitForMultipleObjects(ULONG count, void *pobjs[], semaphore->Header.SignalState, semaphore->Limit, NULL ); break; } - case TYPE_MANUAL_TIMER: - case TYPE_AUTO_TIMER: - break; } }
@@ -137,6 +136,8 @@ NTSTATUS WINAPI KeWaitForMultipleObjects(ULONG count, void *pobjs[], { switch (objs[i]->Type) { + case TYPE_AUTO_TIMER: + case TYPE_MANUAL_TIMER: case TYPE_MANUAL_EVENT: case TYPE_AUTO_EVENT: case TYPE_SEMAPHORE: @@ -394,7 +395,11 @@ static void CALLBACK ke_timer_complete_proc(PTP_CALLBACK_INSTANCE instance, void
TRACE("instance %p, timer %p, tp_timer %p.\n", instance, timer, tp_timer);
- SetEvent(timer->Header.WaitListHead.Blink); + EnterCriticalSection( &sync_cs ); + timer->Header.SignalState = TRUE; + if (timer->Header.WaitListHead.Blink) + SetEvent(timer->Header.WaitListHead.Blink); + LeaveCriticalSection( &sync_cs ); }
/*********************************************************************** @@ -440,7 +445,6 @@ BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period,
ret = timer->Header.Inserted; timer->Header.Inserted = TRUE; - timer->Header.WaitListHead.Blink = CreateEventW( NULL, timer->Header.Type == TYPE_MANUAL_TIMER, FALSE, NULL );
if (!timer->TimerListEntry.Blink) timer->TimerListEntry.Blink = (void *)CreateThreadpoolTimer(ke_timer_complete_proc, timer, NULL); @@ -478,11 +482,15 @@ BOOLEAN WINAPI KeCancelTimer( KTIMER *timer ) timer->TimerListEntry.Blink = NULL; } } + timer->Header.SignalState = FALSE; + if (timer->Header.WaitListHead.Blink && !*((ULONG_PTR *)&timer->Header.WaitListHead.Flink)) + { + CloseHandle(timer->Header.WaitListHead.Blink); + timer->Header.WaitListHead.Blink = NULL; + }
ret = timer->Header.Inserted; timer->Header.Inserted = FALSE; - CloseHandle(timer->Header.WaitListHead.Blink); - timer->Header.WaitListHead.Blink = NULL; LeaveCriticalSection( &sync_cs );
return ret;
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntoskrnl.exe/sync.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dlls/ntoskrnl.exe/sync.c b/dlls/ntoskrnl.exe/sync.c index 912dc046bac..36fa12fe000 100644 --- a/dlls/ntoskrnl.exe/sync.c +++ b/dlls/ntoskrnl.exe/sync.c @@ -443,7 +443,9 @@ BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period,
EnterCriticalSection( &sync_cs );
- ret = timer->Header.Inserted; + if ((ret = timer->Header.Inserted)) + KeCancelTimer(timer); + timer->Header.Inserted = TRUE;
if (!timer->TimerListEntry.Blink)
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntoskrnl.exe/sync.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/dlls/ntoskrnl.exe/sync.c b/dlls/ntoskrnl.exe/sync.c index 36fa12fe000..5e06ab2f2b6 100644 --- a/dlls/ntoskrnl.exe/sync.c +++ b/dlls/ntoskrnl.exe/sync.c @@ -392,9 +392,15 @@ LONG WINAPI KeReleaseMutex( PRKMUTEX mutex, BOOLEAN wait ) static void CALLBACK ke_timer_complete_proc(PTP_CALLBACK_INSTANCE instance, void *timer_, PTP_TIMER tp_timer) { KTIMER *timer = timer_; + KDPC *dpc = timer->Dpc;
TRACE("instance %p, timer %p, tp_timer %p.\n", instance, timer, tp_timer);
+ if (dpc && dpc->DeferredRoutine) + { + TRACE("Calling dpc->DeferredRoutine %p, dpc->DeferredContext %p.\n", dpc->DeferredRoutine, dpc->DeferredContext); + dpc->DeferredRoutine(dpc, dpc->DeferredContext, dpc->SystemArgument1, dpc->SystemArgument2); + } EnterCriticalSection( &sync_cs ); timer->Header.SignalState = TRUE; if (timer->Header.WaitListHead.Blink) @@ -435,12 +441,6 @@ BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period, TRACE("timer %p, duetime %s, period %d, dpc %p.\n", timer, wine_dbgstr_longlong(duetime.QuadPart), period, dpc);
- if (dpc) - { - FIXME("Unhandled DPC %p.\n", dpc); - return FALSE; - } - EnterCriticalSection( &sync_cs );
if ((ret = timer->Header.Inserted)) @@ -456,6 +456,7 @@ BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period,
timer->DueTime.QuadPart = duetime.QuadPart; timer->Period = period; + timer->Dpc = dpc;
SetThreadpoolTimer((TP_TIMER *)timer->TimerListEntry.Blink, (FILETIME *)&duetime, period, 0); LeaveCriticalSection( &sync_cs );
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntoskrnl.exe/tests/driver.c | 88 ++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 3 deletions(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index dc576bfb1b5..e762ad0ccd7 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -382,6 +382,14 @@ static void test_critical_region(BOOL is_dispatcher) "KeAreApcsDisabled returned %x\n", result); }
+static void sleep_1ms(void) +{ + LARGE_INTEGER timeout; + + timeout.QuadPart = -1 * 10000; + KeDelayExecutionThread( KernelMode, FALSE, &timeout ); +} + static void sleep(void) { LARGE_INTEGER timeout; @@ -447,18 +455,32 @@ static void WINAPI remove_lock_thread(void *arg) PsTerminateSystemThread(STATUS_SUCCESS); }
+struct test_sync_dpc_context +{ + BOOL called; +}; + +static void WINAPI test_sync_dpc(KDPC *dpc, void *context, void *system_argument1, void *system_argument2) +{ + struct test_sync_dpc_context *c = context; + + c->called = TRUE; +} + static void test_sync(void) { static const ULONG wine_tag = 0x454e4957; /* WINE */ - KSEMAPHORE semaphore, semaphore2; + struct test_sync_dpc_context dpc_context; KEVENT manual_event, auto_event, *event; - KTIMER timer; + KSEMAPHORE semaphore, semaphore2; IO_REMOVE_LOCK remove_lock; LARGE_INTEGER timeout; OBJECT_ATTRIBUTES attr; HANDLE handle, thread; void *objs[2]; + KTIMER timer; NTSTATUS ret; + KDPC dpc; int i;
KeInitializeEvent(&manual_event, NotificationEvent, FALSE); @@ -711,13 +733,19 @@ static void test_sync(void) KeCancelTimer(&timer); KeInitializeTimerEx(&timer, SynchronizationTimer);
- KeSetTimerEx(&timer, timeout, 0, NULL); + memset(&dpc_context, 0, sizeof(dpc_context)); + KeInitializeDpc(&dpc, test_sync_dpc, &dpc_context); + + KeSetTimerEx(&timer, timeout, 0, &dpc);
ret = wait_single(&timer, 0); ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + ok(!dpc_context.called, "DPC was called unexpectedly.\n");
ret = wait_single(&timer, -40 * 10000); ok(ret == 0, "got %#x\n", ret); + sleep_1ms(); + ok(dpc_context.called, "DPC was not called.\n");
ret = wait_single(&timer, -40 * 10000); ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); @@ -742,6 +770,60 @@ static void test_sync(void)
KeCancelTimer(&timer);
+ /* Test cancelling timer. */ + dpc_context.called = 0; + KeSetTimerEx(&timer, timeout, 0, &dpc); + + ret = wait_single(&timer, 0); + ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + ok(!dpc_context.called, "DPC was called.\n"); + + KeCancelTimer(&timer); + dpc_context.called = 0; + ret = wait_single(&timer, -40 * 10000); + ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + ok(!dpc_context.called, "DPC was called.\n"); + + KeSetTimerEx(&timer, timeout, 20, &dpc); + KeSetTimerEx(&timer, timeout, 0, &dpc); + ret = wait_single(&timer, 0); + ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + + ret = wait_single(&timer, -40 * 10000); + ok(ret == 0, "got %#x\n", ret); + + ret = wait_single(&timer, 0); + ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + + ret = wait_single(&timer, -40 * 10000); + ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + + ret = wait_single(&timer, -40 * 10000); + ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + + KeCancelTimer(&timer); + /* Test reinitializing timer. */ + KeSetTimerEx(&timer, timeout, 0, &dpc); + KeInitializeTimerEx(&timer, SynchronizationTimer); + dpc_context.called = 0; + ret = wait_single(&timer, -40 * 10000); + ok(ret == 0, "got %#x\n", ret); + sleep_1ms(); + todo_wine ok(dpc_context.called, "DPC was not called.\n"); + + ret = wait_single(&timer, 0); + ok(ret == WAIT_TIMEOUT, "got %#x\n", ret); + sleep_1ms(); + todo_wine ok(dpc_context.called, "DPC was not called.\n"); + + dpc_context.called = 0; + KeSetTimerEx(&timer, timeout, 0, &dpc); + ret = wait_single(&timer, -40 * 10000); + ok(ret == 0, "got %#x\n", ret); + sleep_1ms(); + ok(dpc_context.called, "DPC was not called.\n"); + + KeCancelTimer(&timer); /* remove locks */
IoInitializeRemoveLockEx(&remove_lock, wine_tag, 0, 0, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));