- Optimize locking so that the Wake Lock is only entered after a first attempt determines the timer is a wake timer. After the lock, a second check is done to make sure. However, since the first check will fail 99% of times, we will not acquire/release a spinlock, and thus decrease contention. - Fix Object Type Initializer to actually use ExpTimerDelete when the object is deleted. - Fix Initializer to report correct memory usage of timer object. - Fix Initializer to report OBJ_OPENLINK as an invalid attribute for timer objects. - Use correct access masks when modifying or querying timer objects. - Handle wake timers in NtCancelTimer. - Return warning NTSTATUS code if a wake timer is requested but not supported by the system (default on ROS). - Check for valid timer type in NtCreateTimer. - Check for valid period in NtSetTimer. - Don't dereference the timer in NtSetTimer three times. - Return the correct Due Time in NtQueryTimer by substracting the stable interrupt time. - Harmonize formatting, fix some comments. Modified: trunk/reactos/ntoskrnl/ex/timer.c _____
Modified: trunk/reactos/ntoskrnl/ex/timer.c --- trunk/reactos/ntoskrnl/ex/timer.c 2005-08-05 06:13:43 UTC (rev 17056) +++ trunk/reactos/ntoskrnl/ex/timer.c 2005-08-05 06:53:56 UTC (rev 17057) @@ -2,23 +2,23 @@
* COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/timer.c - * PURPOSE: User-mode timers + * PURPOSE: Executive Timer Implementation * - * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Reimplemented + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) * David Welch (welch@mcmail.com) */
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h> - #define NDEBUG #include <internal/debug.h>
/* TYPES ********************************************************************/
/* Executive Timer Object */ -typedef struct _ETIMER { +typedef struct _ETIMER +{ KTIMER KeTimer; KAPC TimerApc; KDPC TimerDpc; @@ -38,7 +38,8 @@ LIST_ENTRY ExpWakeList;
/* Timer Mapping */ -static GENERIC_MAPPING ExpTimerMapping = { +static GENERIC_MAPPING ExpTimerMapping = +{ STANDARD_RIGHTS_READ | TIMER_QUERY_STATE, STANDARD_RIGHTS_WRITE | TIMER_MODIFY_STATE, STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, @@ -46,8 +47,8 @@ };
/* Timer Information Classes */ -static const INFORMATION_CLASS_INFO ExTimerInfoClass[] = { - +static const INFORMATION_CLASS_INFO ExTimerInfoClass[] = +{ /* TimerBasicInformation */ ICI_SQ_SAME( sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), }; @@ -68,42 +69,42 @@
while (!IsListEmpty(&Thread->ActiveTimerListHead)) { - /* Remove a Timer */ - CurrentEntry = RemoveTailList(&Thread->ActiveTimerListHead); + CurrentEntry = RemoveTailList(&Thread->ActiveTimerListHead);
/* Get the Timer */ Timer = CONTAINING_RECORD(CurrentEntry, ETIMER, ActiveTimerListEntry); + DPRINT("Timer, ThreadList: 0x%p, 0x%p\n", Timer, Thread);
+ /* Mark it as deassociated */ ASSERT (Timer->ApcAssociated); - Timer->ApcAssociated = FALSE; + Timer->ApcAssociated = FALSE;
- DPRINT("Timer, ThreadList: 0x%p, 0x%p\n", Timer, Thread); - /* Unlock the list */ KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
/* Lock the Timer */ KeAcquireSpinLockAtDpcLevel(&Timer->Lock);
- ASSERT (&Thread->Tcb == Timer->TimerApc.Thread); - + /* Cancel the timer and remove its DPC and APC */ + ASSERT(&Thread->Tcb == Timer->TimerApc.Thread); KeCancelTimer(&Timer->KeTimer); KeRemoveQueueDpc(&Timer->TimerDpc); KeRemoveQueueApc(&Timer->TimerApc);
- /* Unlock the Timer */ KeReleaseSpinLock(&Timer->Lock, OldIrql);
- /* Dereference it, if needed */ + /* Dereference it */ ObDereferenceObject(Timer);
/* Loop again */ KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql); }
+ /* Release lock and return */ KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql); + return; }
VOID @@ -112,24 +113,27 @@ { KIRQL OldIrql; PETIMER Timer = ObjectBody; - DPRINT("ExpDeleteTimer(Timer: 0x%p)\n", Timer);
- /* Lock the Wake List */ - KeAcquireSpinLock(&ExpWakeListLock, &OldIrql); - /* Check if it has a Wait List */ - if (Timer->WakeTimer) { + if (Timer->WakeTimer) + { + /* Lock the Wake List */ + KeAcquireSpinLock(&ExpWakeListLock, &OldIrql);
- /* Remove it from the Wait List */ - DPRINT("Removing wake list\n"); - RemoveEntryList(&Timer->WakeTimerListEntry); - Timer->WakeTimer = FALSE; + /* Check again, since it might've changed before we locked */ + if (Timer->WakeTimer) + { + /* Remove it from the Wait List */ + DPRINT("Removing wake list\n"); + RemoveEntryList(&Timer->WakeTimerListEntry); + Timer->WakeTimer = FALSE; + } + + /* Release the Wake List */ + KeReleaseSpinLock(&ExpWakeListLock, OldIrql); }
- /* Release the Wake List */ - KeReleaseSpinLock(&ExpWakeListLock, OldIrql); - /* Tell the Kernel to cancel the Timer */ DPRINT("Cancelling Timer\n"); KeCancelTimer(&Timer->KeTimer); @@ -154,8 +158,8 @@ KeAcquireSpinLock(&Timer->Lock, &OldIrql);
/* Queue the APC */ - if(Timer->ApcAssociated) { - + if(Timer->ApcAssociated) + { DPRINT("Queuing APC\n"); KeInsertQueueApc(&Timer->TimerApc, SystemArgument1, @@ -165,6 +169,7 @@
/* Release the Timer */ KeReleaseSpinLock(&Timer->Lock, OldIrql); + return; }
@@ -196,8 +201,8 @@ */ if ((Timer->ApcAssociated) && (&CurrentThread->Tcb == Timer->TimerApc.Thread) && - (!Timer->KeTimer.Period)) { - + (!Timer->KeTimer.Period)) + { /* Remove it from the Active Timers List */ DPRINT("Removing Timer\n"); RemoveEntryList(&Timer->ActiveTimerListEntry); @@ -233,11 +238,12 @@ RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); RtlInitUnicodeString(&Name, L"Timer"); ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); - ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KTIMER); + ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; + ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER); ObjectTypeInitializer.GenericMapping = ExpTimerMapping; ObjectTypeInitializer.PoolType = NonPagedPool; ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS; - ObjectTypeInitializer.UseDefaultObject = TRUE; + ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer; ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ExTimerType);
/* Initialize the Wait List and Lock */ @@ -245,7 +251,6 @@ InitializeListHead(&ExpWakeList); }
- NTSTATUS STDCALL NtCancelTimer(IN HANDLE TimerHandle, @@ -258,44 +263,46 @@ PETHREAD TimerThread; BOOLEAN KillTimer = FALSE; NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); DPRINT("NtCancelTimer(0x%p, 0x%x)\n", TimerHandle, CurrentState);
/* Check Parameter Validity */ - if(CurrentState != NULL && PreviousMode != KernelMode) { - _SEH_TRY { + if(CurrentState && PreviousMode != KernelMode) + { + _SEH_TRY + { ProbeForWrite(CurrentState, sizeof(BOOLEAN), sizeof(BOOLEAN)); - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); - } _SEH_END; + } + _SEH_END;
- if(!NT_SUCCESS(Status)) { - return Status; - } + if(!NT_SUCCESS(Status)) return Status; }
/* Get the Timer Object */ Status = ObReferenceObjectByHandle(TimerHandle, - TIMER_ALL_ACCESS, + TIMER_MODIFY_STATE, ExTimerType, PreviousMode, (PVOID*)&Timer, NULL);
/* Check for success */ - if(NT_SUCCESS(Status)) { - + if(NT_SUCCESS(Status)) + { DPRINT("Timer Referenced: 0x%p\n", Timer);
/* Lock the Timer */ KeAcquireSpinLock(&Timer->Lock, &OldIrql);
/* Check if it's enabled */ - if (Timer->ApcAssociated) { - + if (Timer->ApcAssociated) + { /* * First, remove it from the Thread's Active List * Get the Thread. @@ -318,39 +325,58 @@ KeRemoveQueueApc(&Timer->TimerApc); Timer->ApcAssociated = FALSE; KillTimer = TRUE; - - } else { - + } + else + { /* If timer was disabled, we still need to cancel it */ DPRINT("APC was not Associated. Cancelling Timer\n"); KeCancelTimer(&Timer->KeTimer); }
+ /* Handle a Wake Timer */ + if (Timer->WakeTimer) + { + /* Lock the Wake List */ + KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock); + + /* Check again, since it might've changed before we locked */ + if (Timer->WakeTimer) + { + /* Remove it from the Wait List */ + DPRINT("Removing wake list\n"); + RemoveEntryList(&Timer->WakeTimerListEntry); + Timer->WakeTimer = FALSE; + } + + /* Release the Wake List */ + KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock); + } + + /* Unlock the Timer */ + KeReleaseSpinLock(&Timer->Lock, OldIrql); + /* Read the old State */ State = KeReadStateTimer(&Timer->KeTimer);
/* Dereference the Object */ ObDereferenceObject(Timer);
- /* Unlock the Timer */ - KeReleaseSpinLock(&Timer->Lock, OldIrql); - /* Dereference if it was previously enabled */ if (KillTimer) ObDereferenceObject(Timer); DPRINT1("Timer disabled\n");
/* Make sure it's safe to write to the handle */ - if(CurrentState != NULL) { - - _SEH_TRY { - + if(CurrentState) + { + _SEH_TRY + { *CurrentState = State; - - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); - - } _SEH_END; + } + _SEH_END; } }
@@ -358,7 +384,6 @@ return Status; }
- NTSTATUS STDCALL NtCreateTimer(OUT PHANDLE TimerHandle, @@ -370,41 +395,48 @@ HANDLE hTimer; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); DPRINT("NtCreateTimer(Handle: 0x%p, Type: %d)\n", TimerHandle, TimerType);
/* Check Parameter Validity */ - if (PreviousMode != KernelMode) { - - _SEH_TRY { - + if (PreviousMode != KernelMode) + { + _SEH_TRY + { ProbeForWrite(TimerHandle, sizeof(HANDLE), sizeof(ULONG)); - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); + } + _SEH_END;
- } _SEH_END; - if(!NT_SUCCESS(Status)) return Status; }
+ /* Check for correct timer type */ + if ((TimerType != NotificationTimer) && (TimerType != SynchronizationTimer)) + { + DPRINT1("Invalid Timer Type!\n"); + return STATUS_INVALID_PARAMETER_4; + } + /* Create the Object */ Status = ObCreateObject(PreviousMode, - ExTimerType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(ETIMER), - 0, - 0, - (PVOID*)&Timer); + ExTimerType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(ETIMER), + 0, + 0, + (PVOID*)&Timer);
/* Check for Success */ - if(NT_SUCCESS(Status)) { - + if(NT_SUCCESS(Status)) + { /* Initialize the Kernel Timer */ DPRINT("Initializing Timer: 0x%p\n", Timer); KeInitializeTimerEx(&Timer->KeTimer, TimerType); @@ -428,24 +460,22 @@ &hTimer); DPRINT("Timer Inserted\n");
- /* Make sure it's safe to write to the handle */ - _SEH_TRY { - + _SEH_TRY + { *TimerHandle = hTimer; - - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); - - } _SEH_END; + } + _SEH_END; }
/* Return to Caller */ return Status; }
- NTSTATUS STDCALL NtOpenTimer(OUT PHANDLE TimerHandle, @@ -455,25 +485,24 @@ HANDLE hTimer; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); DPRINT("NtOpenTimer(TimerHandle: 0x%p)\n", TimerHandle);
/* Check Parameter Validity */ - if (PreviousMode != KernelMode) { - - _SEH_TRY { - + if (PreviousMode != KernelMode) + { + _SEH_TRY + { ProbeForWrite(TimerHandle, sizeof(HANDLE), sizeof(ULONG)); - - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); + } + _SEH_END;
- } _SEH_END; - if(!NT_SUCCESS(Status)) return Status; }
@@ -487,18 +516,18 @@ &hTimer);
/* Check for success */ - if(NT_SUCCESS(Status)) { - + if(NT_SUCCESS(Status)) + { /* Make sure it's safe to write to the handle */ - _SEH_TRY { - + _SEH_TRY + { *TimerHandle = hTimer; - - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); - - } _SEH_END; + } + _SEH_END; }
/* Return to Caller */ @@ -518,7 +547,6 @@ KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; PTIMER_BASIC_INFORMATION BasicInfo = (PTIMER_BASIC_INFORMATION)TimerInformation; - PAGED_CODE(); DPRINT("NtQueryTimer(TimerHandle: 0x%p, Class: %d)\n", TimerHandle, TimerInformationClass);
@@ -530,8 +558,8 @@ ReturnLength, PreviousMode, &Status); - if(!NT_SUCCESS(Status)) { - + if(!NT_SUCCESS(Status)) + { DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status); return Status; } @@ -545,24 +573,30 @@ NULL);
/* Check for Success */ - if(NT_SUCCESS(Status)) { - + if(NT_SUCCESS(Status)) + { /* Return the Basic Information */ - _SEH_TRY { + _SEH_TRY + { + /* Return the remaining time, corrected */ + BasicInfo->TimeRemaining.QuadPart = Timer->KeTimer.DueTime.QuadPart - + KeQueryInterruptTime();
- /* FIXME: Interrupt correction based on Interrupt Time */ - DPRINT("Returning Information for Timer: 0x%p. Time Remaining: %I64x\n", Timer, Timer->KeTimer.DueTime.QuadPart); - BasicInfo->TimeRemaining.QuadPart = Timer->KeTimer.DueTime.QuadPart; + /* Return the current state */ BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer);
+ /* Return the buffer length if requested */ if(ReturnLength != NULL) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
- } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + DPRINT("Returning Information for Timer: 0x%p. Time Remaining: %I64x\n", + Timer, BasicInfo->TimeRemaining.QuadPart); + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); + } + _SEH_END;
- } _SEH_END; - /* Dereference Object */ ObDereferenceObject(Timer); } @@ -590,54 +624,69 @@ PETHREAD TimerThread; BOOLEAN KillTimer = FALSE; NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); - DPRINT("NtSetTimer(TimerHandle: 0x%p, DueTime: %I64x, Apc: 0x%p, Period: %d)\n", TimerHandle, DueTime->QuadPart, TimerApcRoutine, Period); + DPRINT("NtSetTimer(TimerHandle: 0x%p, DueTime: %I64x, Apc: 0x%p, Period: %d)\n", + TimerHandle, DueTime->QuadPart, TimerApcRoutine, Period);
/* Check Parameter Validity */ - if (PreviousMode != KernelMode) { - - _SEH_TRY { - + if (PreviousMode != KernelMode) + { + _SEH_TRY + { ProbeForRead(DueTime, sizeof(LARGE_INTEGER), sizeof(ULONG)); TimerDueTime = *DueTime;
- if(PreviousState != NULL) { - + if(PreviousState) + { ProbeForWrite(PreviousState, sizeof(BOOLEAN), sizeof(BOOLEAN)); } - - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); + } + _SEH_END;
- } _SEH_END; - if(!NT_SUCCESS(Status)) return Status; }
+ /* Check for a valid Period */ + if (Period < 0) + { + DPRINT1("Invalid Period for timer\n"); + return STATUS_INVALID_PARAMETER_6; + } + /* Get the Timer Object */ Status = ObReferenceObjectByHandle(TimerHandle, - TIMER_ALL_ACCESS, + TIMER_MODIFY_STATE, ExTimerType, PreviousMode, (PVOID*)&Timer, NULL);
+ /* + * Tell the user we don't support Wake Timers... + * when we have the ability to use/detect the Power Management + * functionatliy required to support them, make this check dependent + * on the actual PM capabilities + */ + if (WakeTimer) Status = STATUS_TIMER_RESUME_IGNORED; + /* Check status */ - if (NT_SUCCESS(Status)) { - + if (NT_SUCCESS(Status)) + { /* Lock the Timer */ DPRINT("Timer Referencced: 0x%p\n", Timer); KeAcquireSpinLock(&Timer->Lock, &OldIrql);
/* Cancel Running Timer */ - if (Timer->ApcAssociated) { - + if (Timer->ApcAssociated) + { /* * First, remove it from the Thread's Active List * Get the Thread. @@ -674,13 +723,14 @@ /* Handle Wake Timers */ DPRINT("Doing Wake Semantics\n"); KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock); - if (WakeTimer && !Timer->WakeTimer) { - + if (WakeTimer && !Timer->WakeTimer) + { /* Insert it into the list */ Timer->WakeTimer = TRUE; InsertTailList(&ExpWakeList, &Timer->WakeTimerListEntry); - } else if (!WakeTimer && Timer->WakeTimer) { - + } + else if (!WakeTimer && Timer->WakeTimer) + { /* Remove it from the list */ RemoveEntryList(&Timer->WakeTimerListEntry); Timer->WakeTimer = FALSE; @@ -688,8 +738,8 @@ KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock);
/* Set up the APC Routine if specified */ - if (TimerApcRoutine) { - + if (TimerApcRoutine) + { /* Initialize the APC */ DPRINT("Initializing APC: 0x%p\n", Timer->TimerApc); KeInitializeApc(&Timer->TimerApc, @@ -720,26 +770,23 @@ /* Unlock the Timer */ KeReleaseSpinLock(&Timer->Lock, OldIrql);
- /* Dereference the Object */ - ObDereferenceObject(Timer); - /* Dereference if it was previously enabled */ if (!TimerApcRoutine) ObDereferenceObject(Timer); if (KillTimer) ObDereferenceObject(Timer); DPRINT("Finished Setting the Timer\n");
/* Make sure it's safe to write to the handle */ - if(PreviousState != NULL) { - - _SEH_TRY { - + if(PreviousState != NULL) + { + _SEH_TRY + { *PreviousState = State; - - } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { - + } + _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) + { Status = _SEH_GetExceptionCode(); - - } _SEH_END; + } + _SEH_END; } }