Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/ex/timer.c * PURPOSE: Executive Timer Implementation * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>#define NDEBUG#include <debug.h>
/* GLOBALS *******************************************************************/
/* Timer Object Type */POBJECT_TYPE ExTimerType = NULL;
KSPIN_LOCK ExpWakeListLock;LIST_ENTRY ExpWakeList;
/* Timer Mapping */static GENERIC_MAPPING ExpTimerMapping ={ STANDARD_RIGHTS_READ | TIMER_QUERY_STATE, STANDARD_RIGHTS_WRITE | TIMER_MODIFY_STATE, STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, TIMER_ALL_ACCESS};
/* Timer Information Classes */static const INFORMATION_CLASS_INFO ExTimerInfoClass[] ={ /* TimerBasicInformation */ IQS_SAME(TIMER_BASIC_INFORMATION, ULONG, ICIF_QUERY),};
/* PRIVATE FUNCTIONS *********************************************************/
VOIDNTAPIExTimerRundown(VOID){ PETHREAD Thread = PsGetCurrentThread(); KIRQL OldIrql; PLIST_ENTRY CurrentEntry; PETIMER Timer; ULONG DerefsToDo;
/* Lock the Thread's Active Timer List and loop it */ KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql); CurrentEntry = Thread->ActiveTimerListHead.Flink; while (CurrentEntry != &Thread->ActiveTimerListHead) { /* Get the timer */ Timer = CONTAINING_RECORD(CurrentEntry, ETIMER, ActiveTimerListEntry);
/* Reference it */ ObReferenceObject(Timer); DerefsToDo = 1;
/* Unlock the list */ KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql);
/* Lock the Timer */ KeAcquireSpinLock(&Timer->Lock, &OldIrql);
/* Lock the list again */ KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
/* Make sure that the timer is valid */ if ((Timer->ApcAssociated) && (&Thread->Tcb == Timer->TimerApc.Thread)) { /* Remove it from the list */ RemoveEntryList(&Timer->ActiveTimerListEntry); Timer->ApcAssociated = FALSE;
/* Cancel the timer and remove its DPC and APC */ KeCancelTimer(&Timer->KeTimer); KeRemoveQueueDpc(&Timer->TimerDpc); if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
/* Add another dereference to do */ DerefsToDo++; }
/* Unlock the list */ KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
/* Unlock the Timer */ KeReleaseSpinLock(&Timer->Lock, OldIrql);
/* Dereference it */ ObDereferenceObjectEx(Timer, DerefsToDo);
/* Loop again */ KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql); CurrentEntry = Thread->ActiveTimerListHead.Flink; }
/* Release lock and return */ KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql);}
VOIDNTAPIExpDeleteTimer(IN PVOID ObjectBody){ KIRQL OldIrql; PETIMER Timer = ObjectBody;
/* Check if it has a Wait List */ if (Timer->WakeTimerListEntry.Flink) { /* Lock the Wake List */ KeAcquireSpinLock(&ExpWakeListLock, &OldIrql);
/* Check again, since it might've changed before we locked */ if (Timer->WakeTimerListEntry.Flink) { /* Remove it from the Wait List */ RemoveEntryList(&Timer->WakeTimerListEntry); Timer->WakeTimerListEntry.Flink = NULL; }
/* Release the Wake List */ KeReleaseSpinLock(&ExpWakeListLock, OldIrql); }
/* Tell the Kernel to cancel the Timer and flush all queued DPCs */ KeCancelTimer(&Timer->KeTimer); KeFlushQueuedDpcs();}
_Function_class_(KDEFERRED_ROUTINE)VOIDNTAPIExpTimerDpcRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2){ PETIMER Timer = DeferredContext; BOOLEAN Inserted = FALSE;
/* Reference the timer */ if (!ObReferenceObjectSafe(Timer)) return;
/* Lock the Timer */ KeAcquireSpinLockAtDpcLevel(&Timer->Lock);
/* Check if the timer is associated */ if (Timer->ApcAssociated) { /* Queue the APC */ Inserted = KeInsertQueueApc(&Timer->TimerApc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT); }
/* Release the Timer */ KeReleaseSpinLockFromDpcLevel(&Timer->Lock);
/* Dereference it if we couldn't queue the APC */ if (!Inserted) ObDereferenceObject(Timer);}
VOIDNTAPIExpTimerApcKernelRoutine(IN PKAPC Apc, IN OUT PKNORMAL_ROUTINE* NormalRoutine, IN OUT PVOID* NormalContext, IN OUT PVOID* SystemArgument1, IN OUT PVOID* SystemArguemnt2){ PETIMER Timer; KIRQL OldIrql; ULONG DerefsToDo = 1; PETHREAD Thread = PsGetCurrentThread();
/* We need to find out which Timer we are */ Timer = CONTAINING_RECORD(Apc, ETIMER, TimerApc);
/* Lock the Timer */ KeAcquireSpinLock(&Timer->Lock, &OldIrql);
/* Lock the Thread's Active Timer List*/ KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
/* Make sure that the Timer is valid, and that it belongs to this thread */ if ((Timer->ApcAssociated) && (&Thread->Tcb == Timer->TimerApc.Thread)) { /* Check if it's not periodic */ if (!Timer->Period) { /* Remove it from the Active Timers List */ RemoveEntryList(&Timer->ActiveTimerListEntry);
/* Disable it */ Timer->ApcAssociated = FALSE; DerefsToDo++; } } else { /* Clear the normal routine */ *NormalRoutine = NULL; }
/* Release locks */ KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock); KeReleaseSpinLock(&Timer->Lock, OldIrql);
/* Dereference as needed */ ObDereferenceObjectEx(Timer, DerefsToDo);}
CODE_SEG("INIT")BOOLEANNTAPIExpInitializeTimerImplementation(VOID){ OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; UNICODE_STRING Name; NTSTATUS Status;
/* Create the Timer Object Type */ RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); RtlInitUnicodeString(&Name, L"Timer"); ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER); ObjectTypeInitializer.GenericMapping = ExpTimerMapping; ObjectTypeInitializer.PoolType = NonPagedPool; ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS; ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer; Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExTimerType); if (!NT_SUCCESS(Status)) return FALSE;
/* Initialize the Wait List and Lock */ KeInitializeSpinLock(&ExpWakeListLock); InitializeListHead(&ExpWakeList); return TRUE;}
/* PUBLIC FUNCTIONS **********************************************************/
NTSTATUSNTAPINtCancelTimer(IN HANDLE TimerHandle, OUT PBOOLEAN CurrentState OPTIONAL){ PETIMER Timer; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); BOOLEAN State; KIRQL OldIrql; PETHREAD TimerThread; ULONG DerefsToDo = 1; NTSTATUS Status; PAGED_CODE();
/* Check if we need to probe */ if ((CurrentState) && (PreviousMode != KernelMode)) { _SEH2_TRY { /* Make sure the pointer is valid */ ProbeForWriteBoolean(CurrentState); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; }
/* Get the Timer Object */ Status = ObReferenceObjectByHandle(TimerHandle, TIMER_MODIFY_STATE, ExTimerType, PreviousMode, (PVOID*)&Timer, NULL); if (NT_SUCCESS(Status)) { /* Lock the Timer */ KeAcquireSpinLock(&Timer->Lock, &OldIrql);
/* Check if it's enabled */ if (Timer->ApcAssociated) { /* Get the Thread. */ TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread, ETHREAD, Tcb);
/* Lock its active list */ KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock);
/* Remove it */ RemoveEntryList(&Timer->ActiveTimerListEntry); Timer->ApcAssociated = FALSE;
/* Unlock the list */ KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock);
/* Cancel the Timer */ KeCancelTimer(&Timer->KeTimer); KeRemoveQueueDpc(&Timer->TimerDpc); if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++; DerefsToDo++; } else { /* If timer was disabled, we still need to cancel it */ KeCancelTimer(&Timer->KeTimer); }
/* Handle a Wake Timer */ if (Timer->WakeTimerListEntry.Flink) { /* Lock the Wake List */ KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock);
/* Check again, since it might've changed before we locked */ if (Timer->WakeTimerListEntry.Flink) { /* Remove it from the Wait List */ RemoveEntryList(&Timer->WakeTimerListEntry); Timer->WakeTimerListEntry.Flink = NULL; }
/* Release the Wake List */ KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock); }
/* Unlock the Timer */ KeReleaseSpinLock(&Timer->Lock, OldIrql);
/* Read the old State */ State = KeReadStateTimer(&Timer->KeTimer);
/* Dereference the Object */ ObDereferenceObjectEx(Timer, DerefsToDo);
/* Check if caller wants the state */ if (CurrentState) { _SEH2_TRY { /* Return the Timer State */ *CurrentState = State; } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Do nothing */ (void)0; } _SEH2_END; } }
/* Return to Caller */ return Status;}
NTSTATUSNTAPINtCreateTimer(OUT PHANDLE TimerHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN TIMER_TYPE TimerType){ PETIMER Timer; HANDLE hTimer; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status; PAGED_CODE();
/* Check for correct timer type */ if ((TimerType != NotificationTimer) && (TimerType != SynchronizationTimer)) { /* Fail */ return STATUS_INVALID_PARAMETER_4; }
/* Check if we need to probe */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Make sure the pointer is valid */ ProbeForWriteHandle(TimerHandle); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; }
/* Create the Object */ Status = ObCreateObject(PreviousMode, ExTimerType, ObjectAttributes, PreviousMode, NULL, sizeof(ETIMER), 0, 0, (PVOID*)&Timer); if (NT_SUCCESS(Status)) { /* Initialize the DPC */ KeInitializeDpc(&Timer->TimerDpc, ExpTimerDpcRoutine, Timer);
/* Initialize the Kernel Timer */ KeInitializeTimerEx(&Timer->KeTimer, TimerType);
/* Initialize the timer fields */ KeInitializeSpinLock(&Timer->Lock); Timer->ApcAssociated = FALSE; Timer->WakeTimer = FALSE; Timer->WakeTimerListEntry.Flink = NULL;
/* Insert the Timer */ Status = ObInsertObject((PVOID)Timer, NULL, DesiredAccess, 0, NULL, &hTimer);
/* Check for success */ if (NT_SUCCESS(Status)) { /* Enter SEH */ _SEH2_TRY { /* Return the Timer Handle */ *TimerHandle = hTimer; } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Do nothing */ (void)0; } _SEH2_END; } }
/* Return to Caller */ return Status;}
NTSTATUSNTAPINtOpenTimer(OUT PHANDLE TimerHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes){ HANDLE hTimer; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status; PAGED_CODE();
/* Check Parameter Validity */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Make sure the pointer is valid */ ProbeForWriteHandle(TimerHandle); } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; }
/* Open the Timer */ Status = ObOpenObjectByName(ObjectAttributes, ExTimerType, PreviousMode, NULL, DesiredAccess, NULL, &hTimer); if (NT_SUCCESS(Status)) { /* Enter SEH */ _SEH2_TRY { /* Return the Timer Handle */ *TimerHandle = hTimer; } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Do nothing */ (void)0; } _SEH2_END; }
/* Return to Caller */ return Status;}
NTSTATUSNTAPINtQueryTimer(IN HANDLE TimerHandle, IN TIMER_INFORMATION_CLASS TimerInformationClass, OUT PVOID TimerInformation, IN ULONG TimerInformationLength, OUT PULONG ReturnLength OPTIONAL){ PETIMER Timer; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status; PTIMER_BASIC_INFORMATION BasicInfo = TimerInformation; PAGED_CODE();
/* Check Validity */ Status = DefaultQueryInfoBufferCheck(TimerInformationClass, ExTimerInfoClass, sizeof(ExTimerInfoClass) / sizeof(ExTimerInfoClass[0]), ICIF_PROBE_READ_WRITE, TimerInformation, TimerInformationLength, ReturnLength, NULL, PreviousMode); if (!NT_SUCCESS(Status)) return Status;
/* Get the Timer Object */ Status = ObReferenceObjectByHandle(TimerHandle, TIMER_QUERY_STATE, ExTimerType, PreviousMode, (PVOID*)&Timer, NULL); if (NT_SUCCESS(Status)) { /* Return the Basic Information */ _SEH2_TRY { /* Return the remaining time, corrected */ BasicInfo->TimeRemaining.QuadPart = Timer-> KeTimer.DueTime.QuadPart - KeQueryInterruptTime();
/* Return the current state */ BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer);
/* Return the buffer length if requested */ if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Get the exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END;
/* Dereference Object */ ObDereferenceObject(Timer); }
/* Return Status */ return Status;}
NTSTATUSNTAPINtSetTimer(IN HANDLE TimerHandle, IN PLARGE_INTEGER DueTime, IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL, IN PVOID TimerContext OPTIONAL, IN BOOLEAN WakeTimer, IN LONG Period OPTIONAL, OUT PBOOLEAN PreviousState OPTIONAL){ PETIMER Timer; KIRQL OldIrql; BOOLEAN State; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); LARGE_INTEGER TimerDueTime; PETHREAD TimerThread; ULONG DerefsToDo = 1; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE();
/* Check for a valid Period */ if (Period < 0) return STATUS_INVALID_PARAMETER_6;
/* Check if we need to probe */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Probe and capture the due time */ TimerDueTime = ProbeForReadLargeInteger(DueTime);
/* Probe the state pointer if one was passed */ if (PreviousState) ProbeForWriteBoolean(PreviousState); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { /* Capture the time directly */ TimerDueTime = *DueTime; }
/* Get the Timer Object */ Status = ObReferenceObjectByHandle(TimerHandle, 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 * functionality required to support them, make this check dependent * on the actual PM capabilities */ if (NT_SUCCESS(Status) && WakeTimer) { Status = STATUS_TIMER_RESUME_IGNORED; }
/* Check status */ if (NT_SUCCESS(Status)) { /* Lock the Timer */ KeAcquireSpinLock(&Timer->Lock, &OldIrql);
/* Cancel Running Timer */ if (Timer->ApcAssociated) { /* Get the Thread. */ TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread, ETHREAD, Tcb);
/* Lock its active list */ KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock);
/* Remove it */ RemoveEntryList(&Timer->ActiveTimerListEntry); Timer->ApcAssociated = FALSE;
/* Unlock the list */ KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock);
/* Cancel the Timer */ KeCancelTimer(&Timer->KeTimer); KeRemoveQueueDpc(&Timer->TimerDpc); if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++; DerefsToDo++; } else { /* If timer was disabled, we still need to cancel it */ KeCancelTimer(&Timer->KeTimer); }
/* Read the State */ State = KeReadStateTimer(&Timer->KeTimer);
/* Handle Wake Timers */ Timer->WakeTimer = WakeTimer; KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock); if ((WakeTimer) && !(Timer->WakeTimerListEntry.Flink)) { /* Insert it into the list */ InsertTailList(&ExpWakeList, &Timer->WakeTimerListEntry); } else if (!(WakeTimer) && (Timer->WakeTimerListEntry.Flink)) { /* Remove it from the list */ RemoveEntryList(&Timer->WakeTimerListEntry); Timer->WakeTimerListEntry.Flink = NULL; } KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock);
/* Set up the APC Routine if specified */ Timer->Period = Period; if (TimerApcRoutine) { /* Initialize the APC */ KeInitializeApc(&Timer->TimerApc, &Thread->Tcb, CurrentApcEnvironment, ExpTimerApcKernelRoutine, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)TimerApcRoutine, PreviousMode, TimerContext);
/* Lock the Thread's Active List and Insert */ KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock); InsertTailList(&Thread->ActiveTimerListHead, &Timer->ActiveTimerListEntry); Timer->ApcAssociated = TRUE; KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
/* One less dereference to do */ DerefsToDo--; }
/* Enable and Set the Timer */ KeSetTimerEx(&Timer->KeTimer, TimerDueTime, Period, TimerApcRoutine ? &Timer->TimerDpc : NULL);
/* Unlock the Timer */ KeReleaseSpinLock(&Timer->Lock, OldIrql);
/* Dereference if it was previously enabled */ if (DerefsToDo) ObDereferenceObjectEx(Timer, DerefsToDo);
/* Check if we need to return the State */ if (PreviousState) { /* Enter SEH */ _SEH2_TRY { /* Return the Timer State */ *PreviousState = State; } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Do nothing */ (void)0; } _SEH2_END; } }
/* Return to Caller */ return Status;}