From 3f700c132c68fa5c863a6f4a79ac550ee7a0abfd Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Mon, 14 Feb 2005 05:36:04 +0000 Subject: [PATCH] Reimplemented Kernel and Executive Timers. Use Microsoft documented structures, added locks to protect against race conditions, use dispatcher database, implement new features like wake timers, use thread timer lists, simplify timer list, add more comments and debug info, actually add apc/dpc functions which do what the should do, fix a couple of bugs, simplfiy absolute vs relative timers. I will do more testing and see wether an experimental Timer Tree patch (which greatly speeds up timer processing) is necessary. svn path=/trunk/; revision=13554 --- reactos/include/ddk/kefuncs.h | 6 + reactos/ntoskrnl/Makefile | 1 + reactos/ntoskrnl/ex/timer.c | 1038 +++++++++++---------- reactos/ntoskrnl/include/internal/ke.h | 4 +- reactos/ntoskrnl/ke/clock.c | 410 +++++++++ reactos/ntoskrnl/ke/i386/kernel.c | 2 +- reactos/ntoskrnl/ke/kthread.c | 59 ++ reactos/ntoskrnl/ke/profile.c | 56 ++ reactos/ntoskrnl/ke/timer.c | 1139 +++++++----------------- reactos/ntoskrnl/ke/wait.c | 35 +- 10 files changed, 1458 insertions(+), 1292 deletions(-) create mode 100644 reactos/ntoskrnl/ke/clock.c diff --git a/reactos/include/ddk/kefuncs.h b/reactos/include/ddk/kefuncs.h index 038bbd26cd3..e28a5bae9f3 100644 --- a/reactos/include/ddk/kefuncs.h +++ b/reactos/include/ddk/kefuncs.h @@ -246,6 +246,12 @@ KeQueryTimeIncrement ( VOID ); +ULONGLONG +STDCALL +KeQueryInterruptTime( + VOID + ); + VOID STDCALL KeRaiseIrql ( diff --git a/reactos/ntoskrnl/Makefile b/reactos/ntoskrnl/Makefile index 63d323aacf8..6ae59eb607d 100644 --- a/reactos/ntoskrnl/Makefile +++ b/reactos/ntoskrnl/Makefile @@ -96,6 +96,7 @@ OBJECTS_KE = \ ke/apc.o \ ke/bug.o \ ke/catch.o \ + ke/clock.o \ ke/critical.o \ ke/dpc.o \ ke/device.o \ diff --git a/reactos/ntoskrnl/ex/timer.c b/reactos/ntoskrnl/ex/timer.c index 3deeff2518c..00ba8cb94e2 100644 --- a/reactos/ntoskrnl/ex/timer.c +++ b/reactos/ntoskrnl/ex/timer.c @@ -5,7 +5,8 @@ * FILE: ntoskrnl/ex/timer.c * PURPOSE: User-mode timers * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Reimplemented + * David Welch (welch@mcmail.com) */ /* INCLUDES *****************************************************************/ @@ -13,528 +14,665 @@ #include #include - /* TYPES ********************************************************************/ -typedef struct _NTTIMER -{ - KTIMER Timer; - KDPC Dpc; - KAPC Apc; - BOOLEAN Running; -} NTTIMER, *PNTTIMER; - +/* Executive Timer Object */ +typedef struct _ETIMER { + KTIMER KeTimer; + KAPC TimerApc; + KDPC TimerDpc; + LIST_ENTRY ActiveTimerListEntry; + KSPIN_LOCK Lock; + LONG Period; + BOOLEAN ApcAssociated; + BOOLEAN WakeTimer; + LIST_ENTRY WakeTimerListEntry; +} ETIMER, *PETIMER; /* GLOBALS ******************************************************************/ +/* Timer Object Type */ POBJECT_TYPE ExTimerType = NULL; -static GENERIC_MAPPING ExpTimerMapping = { - STANDARD_RIGHTS_READ | TIMER_QUERY_STATE, - STANDARD_RIGHTS_WRITE | TIMER_MODIFY_STATE, - STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, - TIMER_ALL_ACCESS}; +KSPIN_LOCK ExpWakeListLock; +LIST_ENTRY ExpWakeList; -static const INFORMATION_CLASS_INFO ExTimerInfoClass[] = -{ - ICI_SQ_SAME( sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* TimerBasicInformation */ +/* 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 */ + ICI_SQ_SAME( sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), }; /* FUNCTIONS *****************************************************************/ -NTSTATUS STDCALL -ExpCreateTimer(PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - POBJECT_ATTRIBUTES ObjectAttributes) -{ - DPRINT("ExpCreateTimer(ObjectBody %x, Parent %x, RemainingPath %S)\n", - ObjectBody, Parent, RemainingPath); - - if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL) - { - return(STATUS_UNSUCCESSFUL); - } - - return(STATUS_SUCCESS); -} - - -VOID STDCALL +VOID +STDCALL ExpDeleteTimer(PVOID ObjectBody) { - KIRQL OldIrql; - PNTTIMER Timer = ObjectBody; + KIRQL OldIrql; + PETIMER Timer = ObjectBody; - DPRINT("ExpDeleteTimer()\n"); + DPRINT("ExpDeleteTimer(Timer: %x)\n", Timer); - OldIrql = KeRaiseIrqlToDpcLevel(); + /* Lock the Wake List */ + KeAcquireSpinLock(&ExpWakeListLock, &OldIrql); + + /* Check if it has a Wait List */ + if (!IsListEmpty(&Timer->WakeTimerListEntry)) { + + /* Remove it from the Wait List */ + DPRINT("Removing wake list\n"); + RemoveEntryList(&Timer->WakeTimerListEntry); + } + + /* Release the Wake List */ + KeReleaseSpinLock(&ExpWakeListLock, OldIrql); - KeCancelTimer(&Timer->Timer); - KeRemoveQueueDpc(&Timer->Dpc); - KeRemoveQueueApc(&Timer->Apc); - Timer->Running = FALSE; - - KeLowerIrql(OldIrql); + /* Tell the Kernel to cancel the Timer */ + DPRINT("Cancelling Timer\n"); + KeCancelTimer(&Timer->KeTimer); } - -VOID STDCALL +VOID +STDCALL ExpTimerDpcRoutine(PKDPC Dpc, - PVOID DeferredContext, - PVOID SystemArgument1, - PVOID SystemArgument2) + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2) { - PNTTIMER Timer; + PETIMER Timer; + KIRQL OldIrql; - DPRINT("ExpTimerDpcRoutine()\n"); + DPRINT("ExpTimerDpcRoutine(Dpc: %x)\n", Dpc); - Timer = (PNTTIMER)DeferredContext; + /* Get the Timer Object */ + Timer = (PETIMER)DeferredContext; - if ( Timer->Running ) - { - KeInsertQueueApc(&Timer->Apc, - SystemArgument1, - SystemArgument2, - IO_NO_INCREMENT); - } + /* Lock the Timer */ + KeAcquireSpinLock(&Timer->Lock, &OldIrql); + + /* Queue the APC */ + if(Timer->ApcAssociated) { + + DPRINT("Queuing APC\n"); + KeInsertQueueApc(&Timer->TimerApc, + SystemArgument1, + SystemArgument2, + IO_NO_INCREMENT); + } + + /* Release the Timer */ + KeReleaseSpinLock(&Timer->Lock, OldIrql); } -VOID STDCALL +VOID +STDCALL ExpTimerApcKernelRoutine(PKAPC Apc, - PKNORMAL_ROUTINE* NormalRoutine, - PVOID* NormalContext, - PVOID* SystemArgument1, - PVOID* SystemArguemnt2) + PKNORMAL_ROUTINE* NormalRoutine, + PVOID* NormalContext, + PVOID* SystemArgument1, + PVOID* SystemArguemnt2) { - DPRINT("ExpTimerApcKernelRoutine()\n"); - + PETIMER Timer; + PETHREAD CurrentThread = PsGetCurrentThread(); + KIRQL OldIrql; + + /* We need to find out which Timer we are */ + Timer = CONTAINING_RECORD(Apc, ETIMER, TimerApc); + DPRINT("ExpTimerApcKernelRoutine(Apc: %x. Timer: %x)\n", Apc, Timer); + + /* Lock the Timer */ + KeAcquireSpinLock(&Timer->Lock, &OldIrql); + + /* Lock the Thread's Active Timer List*/ + KeAcquireSpinLockAtDpcLevel(&CurrentThread->ActiveTimerListLock); + + /* + * Make sure that the Timer is still valid, and that it belongs to this thread + * Remove it if it's not periodic + */ + if ((Timer->ApcAssociated) && + (&CurrentThread->Tcb == Timer->TimerApc.Thread) && + (!Timer->Period)) { + + /* Remove it from the Active Timers List */ + DPRINT("Removing Timer\n"); + RemoveEntryList(&Timer->ActiveTimerListEntry); + + /* Disable it */ + Timer->ApcAssociated = FALSE; + + /* Release spinlocks */ + KeReleaseSpinLockFromDpcLevel(&CurrentThread->ActiveTimerListLock); + KeReleaseSpinLock(&Timer->Lock, OldIrql); + + /* Dereference the Timer Object */ + ObDereferenceObject(Timer); + return; + } + + /* Release spinlocks */ + KeReleaseSpinLockFromDpcLevel(&CurrentThread->ActiveTimerListLock); + KeReleaseSpinLock(&Timer->Lock, OldIrql); } - -VOID INIT_FUNCTION +VOID +INIT_FUNCTION ExpInitializeTimerImplementation(VOID) { - ASSERT(!ExTimerType) - ExTimerType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); + DPRINT("ExpInitializeTimerImplementation()\n"); + + /* Allocate Memory for the Timer */ + ExTimerType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); - RtlpCreateUnicodeString(&ExTimerType->TypeName, L"Timer", NonPagedPool); - - ExTimerType->Tag = TAG('T', 'I', 'M', 'T'); - ExTimerType->PeakObjects = 0; - ExTimerType->PeakHandles = 0; - ExTimerType->TotalObjects = 0; - ExTimerType->TotalHandles = 0; - ExTimerType->PagedPoolCharge = 0; - ExTimerType->NonpagedPoolCharge = sizeof(NTTIMER); - ExTimerType->Mapping = &ExpTimerMapping; - ExTimerType->Dump = NULL; - ExTimerType->Open = NULL; - ExTimerType->Close = NULL; - ExTimerType->Delete = ExpDeleteTimer; - ExTimerType->Parse = NULL; - ExTimerType->Security = NULL; - ExTimerType->QueryName = NULL; - ExTimerType->OkayToClose = NULL; - ExTimerType->Create = ExpCreateTimer; - ExTimerType->DuplicationNotify = NULL; - - ObpCreateTypeObject(ExTimerType); + /* Create the Executive Timer Object */ + RtlpCreateUnicodeString(&ExTimerType->TypeName, L"Timer", NonPagedPool); + ExTimerType->Tag = TAG('T', 'I', 'M', 'T'); + ExTimerType->PeakObjects = 0; + ExTimerType->PeakHandles = 0; + ExTimerType->TotalObjects = 0; + ExTimerType->TotalHandles = 0; + ExTimerType->PagedPoolCharge = 0; + ExTimerType->NonpagedPoolCharge = sizeof(ETIMER); + ExTimerType->Mapping = &ExpTimerMapping; + ExTimerType->Dump = NULL; + ExTimerType->Open = NULL; + ExTimerType->Close = NULL; + ExTimerType->Delete = ExpDeleteTimer; + ExTimerType->Parse = NULL; + ExTimerType->Security = NULL; + ExTimerType->QueryName = NULL; + ExTimerType->OkayToClose = NULL; + ExTimerType->Create = NULL; + ExTimerType->DuplicationNotify = NULL; + ObpCreateTypeObject(ExTimerType); + + /* Initialize the Wait List and Lock */ + KeInitializeSpinLock(&ExpWakeListLock); + InitializeListHead(&ExpWakeList); } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtCancelTimer(IN HANDLE TimerHandle, - OUT PBOOLEAN CurrentState OPTIONAL) + OUT PBOOLEAN CurrentState OPTIONAL) { - PNTTIMER Timer; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PETIMER Timer; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN State; + KIRQL OldIrql; + BOOLEAN KillTimer = FALSE; + PETHREAD TimerThread; - PreviousMode = ExGetPreviousMode(); + DPRINT("NtCancelTimer(0x%x, 0x%x)\n", TimerHandle, CurrentState); - DPRINT("NtCancelTimer(0x%x, 0x%x)\n", TimerHandle, CurrentState); - - if(CurrentState != NULL && PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(CurrentState, - sizeof(BOOLEAN), - sizeof(BOOLEAN)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Check Parameter Validity */ + if(CurrentState != NULL && PreviousMode != KernelMode) { + _SEH_TRY { + ProbeForWrite(CurrentState, + sizeof(BOOLEAN), + sizeof(BOOLEAN)); + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; - if(!NT_SUCCESS(Status)) - { - return Status; - } - } + if(!NT_SUCCESS(Status)) { + return Status; + } + } - Status = ObReferenceObjectByHandle(TimerHandle, - TIMER_ALL_ACCESS, - ExTimerType, - PreviousMode, - (PVOID*)&Timer, - NULL); - if(NT_SUCCESS(Status)) - { - BOOLEAN State; - KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); + /* Get the Timer Object */ + Status = ObReferenceObjectByHandle(TimerHandle, + TIMER_ALL_ACCESS, + ExTimerType, + PreviousMode, + (PVOID*)&Timer, + NULL); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + DPRINT("Timer Referencced: %x\n", Timer); + + /* Lock the Timer */ + KeAcquireSpinLock(&Timer->Lock, &OldIrql); + + /* Check if it's enabled */ + if (Timer->ApcAssociated) { + + /* + * First, remove it from the Thread's Active List + * Get the Thread. + */ + TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread, ETHREAD, Tcb); + DPRINT("Removing from Thread: %x\n", TimerThread); + + /* Lock its active list */ + KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock); + + /* Remove it */ + RemoveEntryList(&TimerThread->ActiveTimerListHead); + + /* Unlock the list */ + KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock); + + /* Cancel the Timer */ + KeCancelTimer(&Timer->KeTimer); + KeRemoveQueueDpc(&Timer->TimerDpc); + KeRemoveQueueApc(&Timer->TimerApc); + Timer->ApcAssociated = FALSE; + KillTimer = TRUE; + + } else { + + /* If timer was disabled, we still need to cancel it */ + DPRINT("APC was not Associated. Cancelling Timer\n"); + KeCancelTimer(&Timer->KeTimer); + } + + /* 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"); - State = KeCancelTimer(&Timer->Timer); - KeRemoveQueueDpc(&Timer->Dpc); - KeRemoveQueueApc(&Timer->Apc); - Timer->Running = FALSE; + /* Make sure it's safe to write to the handle */ + if(CurrentState != NULL) { + _SEH_TRY { + *CurrentState = State; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + } - KeLowerIrql(OldIrql); - ObDereferenceObject(Timer); - - if(CurrentState != NULL) - { - _SEH_TRY - { - *CurrentState = State; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; + /* Return to Caller */ + return Status; } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtCreateTimer(OUT PHANDLE TimerHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN TIMER_TYPE TimerType) + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN TIMER_TYPE TimerType) { - PNTTIMER Timer; - HANDLE hTimer; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PETIMER Timer; + HANDLE hTimer; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; - DPRINT("NtCreateTimer()\n"); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(TimerHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + DPRINT("NtCreateTimer(Handle: %x, Type: %d)\n", TimerHandle, TimerType); - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObCreateObject(PreviousMode, - ExTimerType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(NTTIMER), - 0, - 0, - (PVOID*)&Timer); - if(NT_SUCCESS(Status)) - { - KeInitializeTimerEx(&Timer->Timer, - TimerType); - - KeInitializeDpc(&Timer->Dpc, - &ExpTimerDpcRoutine, - Timer); - - Timer->Running = FALSE; - - Status = ObInsertObject ((PVOID)Timer, - NULL, - DesiredAccess, - 0, - NULL, - &hTimer); - ObDereferenceObject(Timer); + /* Check Parameter Validity */ + if (PreviousMode != KernelMode) { + _SEH_TRY { + ProbeForWrite(TimerHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *TimerHandle = hTimer; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; -} - - -NTSTATUS STDCALL -NtOpenTimer(OUT PHANDLE TimerHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes) -{ - HANDLE hTimer; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - DPRINT("NtOpenTimer()\n"); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(TimerHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObOpenObjectByName(ObjectAttributes, - ExTimerType, - NULL, - PreviousMode, - DesiredAccess, - NULL, - &hTimer); - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *TimerHandle = hTimer; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } + if(!NT_SUCCESS(Status)) { + return Status; + } + } + + /* Create the Object */ + Status = ObCreateObject(PreviousMode, + ExTimerType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(ETIMER), + 0, + 0, + (PVOID*)&Timer); - return Status; + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Initialize the Kernel Timer */ + DPRINT("Initializing Timer: %x\n", Timer); + KeInitializeTimerEx(&Timer->KeTimer, TimerType); + + /* Initialize the Timer Lock */ + KeInitializeSpinLock(&Timer->Lock); + + /* Initialize the DPC */ + KeInitializeDpc(&Timer->TimerDpc, ExpTimerDpcRoutine, Timer); + + /* Set Initial State */ + Timer->ApcAssociated = FALSE; + InitializeListHead(&Timer->WakeTimerListEntry); + Timer->WakeTimer = FALSE; + + /* Insert the Timer */ + Status = ObInsertObject((PVOID)Timer, + NULL, + DesiredAccess, + 0, + NULL, + &hTimer); + DPRINT("Timer Inserted\n"); + + + /* Make sure it's safe to write to the handle */ + _SEH_TRY { + *TimerHandle = hTimer; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + + /* Return to Caller */ + return Status; } -NTSTATUS STDCALL +NTSTATUS +STDCALL +NtOpenTimer(OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes) +{ + HANDLE hTimer; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("NtOpenTimer(TimerHandle: %x)\n", TimerHandle); + + /* Check Parameter Validity */ + if (PreviousMode != KernelMode) { + _SEH_TRY { + ProbeForWrite(TimerHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if(!NT_SUCCESS(Status)) { + return Status; + } + } + + /* Open the Timer */ + Status = ObOpenObjectByName(ObjectAttributes, + ExTimerType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hTimer); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + /* Make sure it's safe to write to the handle */ + _SEH_TRY { + *TimerHandle = hTimer; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + + /* Return to Caller */ + return Status; +} + + +NTSTATUS +STDCALL NtQueryTimer(IN HANDLE TimerHandle, - IN TIMER_INFORMATION_CLASS TimerInformationClass, - OUT PVOID TimerInformation, - IN ULONG TimerInformationLength, - OUT PULONG ReturnLength OPTIONAL) + IN TIMER_INFORMATION_CLASS TimerInformationClass, + OUT PVOID TimerInformation, + IN ULONG TimerInformationLength, + OUT PULONG ReturnLength OPTIONAL) { - PNTTIMER Timer; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PETIMER Timer; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + PTIMER_BASIC_INFORMATION BasicInfo = (PTIMER_BASIC_INFORMATION)TimerInformation; - PreviousMode = ExGetPreviousMode(); + DPRINT("NtQueryTimer(TimerHandle: %x, Class: %d)\n", TimerHandle, TimerInformationClass); + + /* Check Validity */ + DefaultQueryInfoBufferCheck(TimerInformationClass, + ExTimerInfoClass, + TimerInformation, + TimerInformationLength, + ReturnLength, + PreviousMode, + &Status); + if(!NT_SUCCESS(Status)) { + + DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status); + return Status; + } - DefaultQueryInfoBufferCheck(TimerInformationClass, - ExTimerInfoClass, - TimerInformation, - TimerInformationLength, - ReturnLength, - PreviousMode, - &Status); - if(!NT_SUCCESS(Status)) - { - DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status); - return Status; - } + /* Get the Timer Object */ + Status = ObReferenceObjectByHandle(TimerHandle, + TIMER_QUERY_STATE, + ExTimerType, + PreviousMode, + (PVOID*)&Timer, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Return the Basic Information */ + _SEH_TRY { + + /* FIXME: Interrupt correction based on Interrupt Time */ + DPRINT("Returning Information for Timer: %x. Time Remaining: %d\n", Timer, Timer->KeTimer.DueTime.QuadPart); + BasicInfo->TimeRemaining.QuadPart = Timer->KeTimer.DueTime.QuadPart; + BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer); + ObDereferenceObject(Timer); - Status = ObReferenceObjectByHandle(TimerHandle, - TIMER_QUERY_STATE, - ExTimerType, - PreviousMode, - (PVOID*)&Timer, - NULL); - if(NT_SUCCESS(Status)) - { - switch(TimerInformationClass) - { - case TimerBasicInformation: - { - PTIMER_BASIC_INFORMATION BasicInfo = (PTIMER_BASIC_INFORMATION)TimerInformation; - - _SEH_TRY - { - /* FIXME - interrupt correction */ - BasicInfo->TimeRemaining.QuadPart = Timer->Timer.DueTime.QuadPart; - BasicInfo->SignalState = (BOOLEAN)Timer->Timer.Header.SignalState; - - if(ReturnLength != NULL) - { - *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - default: - Status = STATUS_NOT_IMPLEMENTED; - break; - } - - ObDereferenceObject(Timer); - } - - return Status; + if(ReturnLength != NULL) { + *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); + } + + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + + /* Return Status */ + return Status; } - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtSetTimer(IN HANDLE TimerHandle, - IN PLARGE_INTEGER DueTime, - IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL, - IN PVOID TimerContext OPTIONAL, - IN BOOLEAN ResumeTimer, - IN LONG Period OPTIONAL, - OUT PBOOLEAN PreviousState OPTIONAL) + 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) { - PNTTIMER Timer; - BOOLEAN Result; - BOOLEAN State; - LARGE_INTEGER TimerDueTime; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PETIMER Timer; + KIRQL OldIrql; + BOOLEAN KillTimer; + BOOLEAN State; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PETHREAD CurrentThread = PsGetCurrentThread(); + NTSTATUS Status = STATUS_SUCCESS; + LARGE_INTEGER TimerDueTime; + PETHREAD TimerThread; - DPRINT("NtSetTimer()\n"); + DPRINT("NtSetTimer(TimerHandle: %x, DueTime: %d, Apc: %x, Period: %d)\n", TimerHandle, DueTime->QuadPart, TimerApcRoutine, Period); - PreviousMode = ExGetPreviousMode(); + /* Check Parameter Validity */ + if (PreviousMode != KernelMode) { + _SEH_TRY { + ProbeForRead(DueTime, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + TimerDueTime = *DueTime; + + if(PreviousState != NULL) { + ProbeForWrite(PreviousState, + sizeof(BOOLEAN), + sizeof(BOOLEAN)); + } + + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if(!NT_SUCCESS(Status)) { + return Status; + } + } + + /* Get the Timer Object */ + Status = ObReferenceObjectByHandle(TimerHandle, + TIMER_ALL_ACCESS, + ExTimerType, + PreviousMode, + (PVOID*)&Timer, + NULL); + + /* Check status */ + if (NT_SUCCESS(Status)) { + + /* Lock the Timer */ + DPRINT("Timer Referencced: %x\n", Timer); + KeAcquireSpinLock(&Timer->Lock, &OldIrql); + + /* Cancel Running Timer */ + if (Timer->ApcAssociated) { + + /* + * First, remove it from the Thread's Active List + * Get the Thread. + */ + TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread, ETHREAD, Tcb); + DPRINT("Thread already running. Removing from Thread: %x\n", TimerThread); + + /* Lock its active list */ + KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock); + + /* Remove it */ + RemoveEntryList(&TimerThread->ActiveTimerListHead); + + /* Unlock the list */ + KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock); + + /* Cancel the Timer */ + KeCancelTimer(&Timer->KeTimer); + KeRemoveQueueDpc(&Timer->TimerDpc); + KeRemoveQueueApc(&Timer->TimerApc); + Timer->ApcAssociated = FALSE; + KillTimer = TRUE; + + } else { + + /* If timer was disabled, we still need to cancel it */ + DPRINT("No APCs. Simply cancelling\n"); + KeCancelTimer(&Timer->KeTimer); + } + + /* Read the State */ + State = KeReadStateTimer(&Timer->KeTimer); - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForRead(DueTime, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - TimerDueTime = *DueTime; + /* Handle Wake Timers */ + DPRINT("Doing Wake Semantics\n"); + KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock); + if (WakeTimer) { + + /* Insert it into the list */ + InsertTailList(&ExpWakeList, &Timer->WakeTimerListEntry); + + } else { + + /* Remove it from the list */ + RemoveEntryList(&Timer->WakeTimerListEntry); + Timer->WakeTimerListEntry.Flink = NULL; + } + KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock); + + /* Set up the APC Routine if specified */ + if (TimerApcRoutine) { + + /* Initialize the APC */ + DPRINT("Initializing APC: %x\n", Timer->TimerApc); + KeInitializeApc(&Timer->TimerApc, + &CurrentThread->Tcb, + CurrentApcEnvironment, + &ExpTimerApcKernelRoutine, + (PKRUNDOWN_ROUTINE)NULL, + (PKNORMAL_ROUTINE)TimerApcRoutine, + PreviousMode, + TimerContext); + + /* Lock the Thread's Active List and Insert */ + KeAcquireSpinLockAtDpcLevel(&CurrentThread->ActiveTimerListLock); + InsertTailList(&CurrentThread->ActiveTimerListHead, + &Timer->ActiveTimerListEntry); + KeReleaseSpinLockFromDpcLevel(&CurrentThread->ActiveTimerListLock); + + } - if(PreviousState != NULL) - { - ProbeForWrite(PreviousState, - sizeof(BOOLEAN), - sizeof(BOOLEAN)); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Enable and Set the Timer */ + DPRINT("Setting Kernel Timer\n"); + KeSetTimerEx(&Timer->KeTimer, + TimerDueTime, + Period, + TimerApcRoutine ? &Timer->TimerDpc : 0); + Timer->ApcAssociated = TimerApcRoutine ? TRUE : FALSE; + + /* Unlock the Timer */ + KeReleaseSpinLock(&Timer->Lock, OldIrql); - if(!NT_SUCCESS(Status)) - { - return Status; - } - } + /* Dereference the Object */ + ObDereferenceObject(Timer); + + /* Unlock the Timer */ + KeReleaseSpinLock(&Timer->Lock, OldIrql); + + /* Dereference if it was previously enabled */ + if (!TimerApcRoutine) ObDereferenceObject(Timer); + if (KillTimer) ObDereferenceObject(Timer); + DPRINT("Finished Setting the Timer\n"); - Status = ObReferenceObjectByHandle(TimerHandle, - TIMER_ALL_ACCESS, - ExTimerType, - PreviousMode, - (PVOID*)&Timer, - NULL); - if (!NT_SUCCESS(Status)) - { - return Status; - } + /* Make sure it's safe to write to the handle */ + if(PreviousState != NULL) { + _SEH_TRY { + *PreviousState = State; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + } - State = KeReadStateTimer(&Timer->Timer); - - if (Timer->Running == TRUE) - { - /* cancel running timer */ - const KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); - KeCancelTimer(&Timer->Timer); - KeRemoveQueueDpc(&Timer->Dpc); - KeRemoveQueueApc(&Timer->Apc); - Timer->Running = FALSE; - KeLowerIrql(OldIrql); - } - - if (TimerApcRoutine) - { - KeInitializeApc(&Timer->Apc, - KeGetCurrentThread(), - OriginalApcEnvironment, - &ExpTimerApcKernelRoutine, - (PKRUNDOWN_ROUTINE)NULL, - (PKNORMAL_ROUTINE)TimerApcRoutine, - PreviousMode, - TimerContext); - } - - Result = KeSetTimerEx(&Timer->Timer, - TimerDueTime, - Period, - TimerApcRoutine ? &Timer->Dpc : 0 ); - if (Result) - { - ObDereferenceObject(Timer); - DPRINT1( "KeSetTimer says the timer was already running, this shouldn't be\n" ); - return STATUS_UNSUCCESSFUL; - } - - Timer->Running = TRUE; - - ObDereferenceObject(Timer); - - if (PreviousState != NULL) - { - _SEH_TRY - { - *PreviousState = State; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - - return Status; + /* Return to Caller */ + return Status; } /* EOF */ diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index 14ba678b188..e9e78f6d9ef 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -142,6 +142,8 @@ VOID STDCALL KiDeleteProfile(PVOID ObjectBody); VOID STDCALL KeUpdateSystemTime(PKTRAP_FRAME TrapFrame, KIRQL Irql); VOID STDCALL KeUpdateRunTime(PKTRAP_FRAME TrapFrame, KIRQL Irql); +VOID STDCALL KiExpireTimers(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2); + KIRQL KeAcquireDispatcherDatabaseLock(VOID); VOID KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID); VOID KeReleaseDispatcherDatabaseLock(KIRQL Irql); @@ -193,7 +195,7 @@ VOID KeInitTimer(VOID); VOID KeInitDpc(struct _KPCR* Pcr); VOID KeInitDispatcher(VOID); VOID KeInitializeDispatcher(VOID); -VOID KeInitializeTimerImpl(VOID); +VOID KiInitializeSystemClock(VOID); VOID KeInitializeBugCheck(VOID); VOID Phase1Initialization(PVOID Context); diff --git a/reactos/ntoskrnl/ke/clock.c b/reactos/ntoskrnl/ke/clock.c new file mode 100644 index 00000000000..e9424cbed4f --- /dev/null +++ b/reactos/ntoskrnl/ke/clock.c @@ -0,0 +1,410 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ke/clock.c + * PURPOSE: Handle System Clock + * + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Created + * David Welch & Phillip Susi - Implementation (?) + */ + + /* NOTES ******************************************************************/ +/* + * System time units are 100-nanosecond intervals + */ + +/* INCLUDES ***************************************************************/ + +#include + +#define NDEBUG +#include + +/* GLOBALS ****************************************************************/ + +/* + * Current time + */ +#if defined(__GNUC__) +LARGE_INTEGER SystemBootTime = (LARGE_INTEGER)0LL; +#else +LARGE_INTEGER SystemBootTime = { 0 }; +#endif + +CHAR KiTimerSystemAuditing = 0; +static KDPC KiExpireTimerDpc; +static BOOLEAN KiClockSetupComplete = FALSE; + +/* + * Number of timer interrupts since initialisation + */ +volatile ULONGLONG KeTickCount = 0; +volatile ULONG KiRawTicks = 0; + +extern LIST_ENTRY KiTimerListHead; + +/* + * The increment in the system clock every timer tick (in system time units) + * + * = (1/18.2)*10^9 + * + * RJJ was 54945055 + */ +#define CLOCK_INCREMENT (100000) + +#ifdef __GNUC__ +ULONG EXPORTED KeMaximumIncrement = 100000; +ULONG EXPORTED KeMinimumIncrement = 100000; +#else +/* Microsoft-style declarations */ +EXPORTED ULONG KeMaximumIncrement = 100000; +EXPORTED ULONG KeMinimumIncrement = 100000; +#endif + +#define MICROSECONDS_PER_TICK (10000) +#define TICKS_TO_CALIBRATE (1) +#define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE) + +/* FUNCTIONS **************************************************************/ + +/* + * FUNCTION: Initializes timer irq handling + * NOTE: This is only called once from main() + */ +VOID +INIT_FUNCTION +KiInitializeSystemClock(VOID) +{ + TIME_FIELDS TimeFields; + + DPRINT1("KiInitializeSystemClock()\n"); + InitializeListHead(&KiTimerListHead); + KeInitializeDpc(&KiExpireTimerDpc, (PKDEFERRED_ROUTINE)KiExpireTimers, 0); + + /* Calculate the starting time for the system clock */ + HalQueryRealTimeClock(&TimeFields); + RtlTimeFieldsToTime(&TimeFields, &SystemBootTime); + + /* Set up the Used Shared Data */ + SharedUserData->TickCountLowDeprecated = 0; + SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310 + SharedUserData->InterruptTime.High2Time = 0; + SharedUserData->InterruptTime.LowPart = 0; + SharedUserData->InterruptTime.High1Time = 0; + SharedUserData->SystemTime.High2Time = SystemBootTime.u.HighPart; + SharedUserData->SystemTime.LowPart = SystemBootTime.u.LowPart; + SharedUserData->SystemTime.High1Time = SystemBootTime.u.HighPart; + + KiClockSetupComplete = TRUE; + DPRINT1("Finished KiInitializeSystemClock()\n"); +} + +VOID +KiSetSystemTime(PLARGE_INTEGER NewSystemTime) +{ + LARGE_INTEGER OldSystemTime; + LARGE_INTEGER DeltaTime; + KIRQL OldIrql; + + ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + OldIrql = KeAcquireDispatcherDatabaseLock(); + + do + { + OldSystemTime.u.HighPart = SharedUserData->SystemTime.High1Time; + OldSystemTime.u.LowPart = SharedUserData->SystemTime.LowPart; + } + while (OldSystemTime.u.HighPart != SharedUserData->SystemTime.High2Time); + + /* Set the new system time */ + SharedUserData->SystemTime.LowPart = NewSystemTime->u.LowPart; + SharedUserData->SystemTime.High1Time = NewSystemTime->u.HighPart; + SharedUserData->SystemTime.High2Time = NewSystemTime->u.HighPart; + + /* Calculate the difference between the new and the old time */ + DeltaTime.QuadPart = NewSystemTime->QuadPart - OldSystemTime.QuadPart; + + /* Update system boot time */ + SystemBootTime.QuadPart += DeltaTime.QuadPart; + + /* Update absolute timers */ + DPRINT1("FIXME: TIMER UPDATE NOT DONE!!!\n"); + + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* + * NOTE: Expired timers will be processed at the next clock tick! + */ +} + +/* + * @implemented + */ +ULONG +STDCALL +KeQueryTimeIncrement(VOID) +/* + * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to + * the system clock every time the clock interrupts + * RETURNS: The increment + */ +{ + return KeMaximumIncrement; +} + +/* + * @implemented + */ +VOID +STDCALL +KeQueryTickCount(PLARGE_INTEGER TickCount) +/* + * FUNCTION: Returns the number of ticks since the system was booted + * ARGUMENTS: + * TickCount (OUT) = Points to storage for the number of ticks + */ +{ + TickCount->QuadPart = KeTickCount; +} + +/* + * FUNCTION: Gets the current system time + * ARGUMENTS: + * CurrentTime (OUT) = The routine stores the current time here + * NOTE: The time is the number of 100-nanosecond intervals since the + * 1st of January, 1601. + * + * @implemented + */ +VOID +STDCALL +KeQuerySystemTime(PLARGE_INTEGER CurrentTime) +{ + do { + CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Time; + CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart; + } while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Time); +} + +ULONGLONG +STDCALL +KeQueryInterruptTime(VOID) +{ + LARGE_INTEGER CurrentTime; + + do { + CurrentTime.u.HighPart = SharedUserData->InterruptTime.High1Time; + CurrentTime.u.LowPart = SharedUserData->InterruptTime.LowPart; + } while (CurrentTime.u.HighPart != SharedUserData->InterruptTime.High2Time); + + return CurrentTime.QuadPart; +} + +/* + * @implemented + */ +VOID +STDCALL +KeSetTimeIncrement( + IN ULONG MaxIncrement, + IN ULONG MinIncrement) +{ + /* Set some Internal Variables */ + /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */ + KeMaximumIncrement = MaxIncrement; + KeMinimumIncrement = MinIncrement; +} + +/* + * @unimplemented + */ +VOID +FASTCALL +KeSetTimeUpdateNotifyRoutine( + IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine + ) +{ + UNIMPLEMENTED; +} + +/* + * NOTE: On Windows this function takes exactly one parameter and EBP is + * guaranteed to point to KTRAP_FRAME. The function is used only + * by HAL, so there's no point in keeping that prototype. + * + * @implemented + */ +VOID +STDCALL +KeUpdateRunTime( + IN PKTRAP_FRAME TrapFrame, + IN KIRQL Irql + ) +{ + PKPCR Pcr; + PKTHREAD CurrentThread; + PKPROCESS CurrentProcess; +#if 0 + ULONG DpcLastCount; +#endif + + Pcr = KeGetCurrentKPCR(); + + /* Make sure we don't go further if we're in early boot phase. */ + if (Pcr == NULL || Pcr->PrcbData.CurrentThread == NULL) + return; + + DPRINT("KernelTime %u, UserTime %u \n", Pcr->PrcbData.KernelTime, Pcr->PrcbData.UserTime); + + CurrentThread = Pcr->PrcbData.CurrentThread; + CurrentProcess = CurrentThread->ApcState.Process; + + /* + * Cs bit 0 is always set for user mode if we are in protected mode. + * V86 mode is counted as user time. + */ + if (TrapFrame->Cs & 0x1 || + TrapFrame->Eflags & X86_EFLAGS_VM) + { + InterlockedIncrementUL(&CurrentThread->UserTime); + InterlockedIncrementUL(&CurrentProcess->UserTime); + Pcr->PrcbData.UserTime++; + } + else + { + if (Irql > DISPATCH_LEVEL) + { + Pcr->PrcbData.InterruptTime++; + } + else if (Irql == DISPATCH_LEVEL) + { + Pcr->PrcbData.DpcTime++; + } + else + { + InterlockedIncrementUL(&CurrentThread->KernelTime); + InterlockedIncrementUL(&CurrentProcess->KernelTime); + Pcr->PrcbData.KernelTime++; + } + } + +#if 0 + DpcLastCount = Pcr->PrcbData.DpcLastCount; + Pcr->PrcbData.DpcLastCount = Pcr->PrcbData.DpcCount; + Pcr->PrcbData.DpcRequestRate = ((Pcr->PrcbData.DpcCount - DpcLastCount) + + Pcr->PrcbData.DpcRequestRate) / 2; +#endif + + if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0 && + Pcr->PrcbData.DpcRoutineActive == FALSE && + Pcr->PrcbData.DpcInterruptRequested == FALSE) + { + HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + } + + /* FIXME: Do DPC rate adjustments */ + + /* + * If we're at end of quantum request software interrupt. The rest + * is handled in KiDispatchInterrupt. + */ + if ((CurrentThread->Quantum -= 3) <= 0) + { + Pcr->PrcbData.QuantumEnd = TRUE; + HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + } +} + + +/* + * NOTE: On Windows this function takes exactly zero parameters and EBP is + * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL. + * The function is used only by HAL, so there's no point in keeping + * that prototype. + * + * @implemented + */ +VOID +STDCALL +KeUpdateSystemTime( + IN PKTRAP_FRAME TrapFrame, + IN KIRQL Irql + ) +/* + * FUNCTION: Handles a timer interrupt + */ +{ + LARGE_INTEGER Time; + + ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL); + + KiRawTicks++; + + if (KiClockSetupComplete == FALSE) return; + + /* + * Increment the number of timers ticks + */ + KeTickCount++; + SharedUserData->TickCountLowDeprecated++; + + Time.u.LowPart = SharedUserData->InterruptTime.LowPart; + Time.u.HighPart = SharedUserData->InterruptTime.High1Time; + Time.QuadPart += CLOCK_INCREMENT; + SharedUserData->InterruptTime.High2Time = Time.u.HighPart; + SharedUserData->InterruptTime.LowPart = Time.u.LowPart; + SharedUserData->InterruptTime.High1Time = Time.u.HighPart; + + Time.u.LowPart = SharedUserData->SystemTime.LowPart; + Time.u.HighPart = SharedUserData->SystemTime.High1Time; + Time.QuadPart += CLOCK_INCREMENT; + SharedUserData->SystemTime.High2Time = Time.u.HighPart; + SharedUserData->SystemTime.LowPart = Time.u.LowPart; + SharedUserData->SystemTime.High1Time = Time.u.HighPart; + + /* FIXME: Here we should check for remote debugger break-ins */ + + /* Update process and thread times */ + KeUpdateRunTime(TrapFrame, Irql); + + /* + * Queue a DPC that will expire timers + */ + KeInsertQueueDpc(&KiExpireTimerDpc, (PVOID)TrapFrame->Eip, 0); +} + +/* + * @implemented + */ +ULONG +STDCALL +NtGetTickCount(VOID) +{ + LARGE_INTEGER TickCount; + + KeQueryTickCount(&TickCount); + return TickCount.u.LowPart; +} + +NTSTATUS +STDCALL +NtQueryTimerResolution(OUT PULONG MinimumResolution, + OUT PULONG MaximumResolution, + OUT PULONG ActualResolution) +{ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +STDCALL +NtSetTimerResolution(IN ULONG DesiredResolution, + IN BOOLEAN SetResolution, + OUT PULONG CurrentResolution) +{ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +/* EOF */ diff --git a/reactos/ntoskrnl/ke/i386/kernel.c b/reactos/ntoskrnl/ke/i386/kernel.c index a1ffb830baf..37852a5f0e7 100644 --- a/reactos/ntoskrnl/ke/i386/kernel.c +++ b/reactos/ntoskrnl/ke/i386/kernel.c @@ -341,7 +341,7 @@ KeInit2(VOID) KeInitializeBugCheck(); KeInitializeDispatcher(); - KeInitializeTimerImpl(); + KiInitializeSystemClock(); if (Pcr->PrcbData.FeatureBits & X86_FEATURE_PAE) { diff --git a/reactos/ntoskrnl/ke/kthread.c b/reactos/ntoskrnl/ke/kthread.c index 5a1eaa2b8ab..40b352fb11a 100644 --- a/reactos/ntoskrnl/ke/kthread.c +++ b/reactos/ntoskrnl/ke/kthread.c @@ -72,6 +72,23 @@ KeQueryPriorityThread ( return Thread->Priority; } +/* + * @implemented + */ +ULONG +STDCALL +KeQueryRuntimeThread( + IN PKTHREAD Thread, + OUT PULONG UserTime + ) +{ + /* Return the User Time */ + *UserTime = Thread->UserTime; + + /* Return the Kernel Time */ + return Thread->KernelTime; +} + NTSTATUS KeReleaseThread(PKTHREAD Thread) /* @@ -405,3 +422,45 @@ KeTerminateThread(IN KPRIORITY Increment) /* Call our own internal routine */ PsTerminateCurrentThread(0); } + + +NTSTATUS +STDCALL +NtDelayExecution(IN BOOLEAN Alertable, + IN PLARGE_INTEGER DelayInterval) +{ + KPROCESSOR_MODE PreviousMode; + LARGE_INTEGER SafeInterval; + + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + NTSTATUS Status = STATUS_SUCCESS; + + _SEH_TRY + { + ProbeForRead(DelayInterval, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + /* make a copy on the kernel stack and let DelayInterval point to it so + we don't need to wrap KeDelayExecutionThread in SEH! */ + SafeInterval = *DelayInterval; + DelayInterval = &SafeInterval; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + return KeDelayExecutionThread(PreviousMode, + Alertable, + DelayInterval); +} diff --git a/reactos/ntoskrnl/ke/profile.c b/reactos/ntoskrnl/ke/profile.c index 08b9bb9d968..454e15c3afc 100644 --- a/reactos/ntoskrnl/ke/profile.c +++ b/reactos/ntoskrnl/ke/profile.c @@ -284,3 +284,59 @@ KeSetProfileIrql( { UNIMPLEMENTED; } + +NTSTATUS STDCALL +NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter, + OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL) +{ + LARGE_INTEGER PerfCounter; + LARGE_INTEGER PerfFrequency; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; + + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForWrite(PerformanceCounter, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + if(PerformanceFrequency != NULL) + { + ProbeForWrite(PerformanceFrequency, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + PerfCounter = KeQueryPerformanceCounter(&PerfFrequency); + + _SEH_TRY + { + *PerformanceCounter = PerfCounter; + if(PerformanceFrequency != NULL) + { + *PerformanceFrequency = PerfFrequency; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + return Status; +} diff --git a/reactos/ntoskrnl/ke/timer.c b/reactos/ntoskrnl/ke/timer.c index 46ec4198975..9ef0be57a8f 100644 --- a/reactos/ntoskrnl/ke/timer.c +++ b/reactos/ntoskrnl/ke/timer.c @@ -1,17 +1,11 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/timer.c - * PURPOSE: Handle timers + * PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers) * - * PROGRAMMERS: David Welch (welch@mcmail.com) - * Phillip Susi - */ - -/* NOTES ******************************************************************/ -/* - * System time units are 100-nanosecond intervals + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Reimplemented some parts, fixed many bugs. + * David Welch (welch@mcmail.com) & Phillip Susi - Original Implementation. */ /* INCLUDES ***************************************************************/ @@ -21,451 +15,79 @@ #define NDEBUG #include - /* GLOBALS ****************************************************************/ -/* - * Current time - */ -#if defined(__GNUC__) -LARGE_INTEGER SystemBootTime = (LARGE_INTEGER)0LL; -#else -LARGE_INTEGER SystemBootTime = { 0 }; -#endif +LIST_ENTRY KiTimerListHead; -CHAR KiTimerSystemAuditing = 0; - -/* - * Number of timer interrupts since initialisation - */ -volatile ULONGLONG KeTickCount = 0; -volatile ULONG KiRawTicks = 0; - -/* - * The increment in the system clock every timer tick (in system time units) - * - * = (1/18.2)*10^9 - * - * RJJ was 54945055 - */ -#define CLOCK_INCREMENT (100000) - -#ifdef __GNUC__ -ULONG EXPORTED KeMaximumIncrement = 100000; -ULONG EXPORTED KeMinimumIncrement = 100000; -#else -/* Microsoft-style declarations */ -EXPORTED ULONG KeMaximumIncrement = 100000; -EXPORTED ULONG KeMinimumIncrement = 100000; -#endif - - - -/* - * PURPOSE: List of timers - */ -static LIST_ENTRY AbsoluteTimerListHead; -static LIST_ENTRY RelativeTimerListHead; -static KSPIN_LOCK TimerListLock; -static KDPC ExpireTimerDpc; - -/* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */ - -#define MICROSECONDS_PER_TICK (10000) -#define TICKS_TO_CALIBRATE (1) -#define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE) #define SYSTEM_TIME_UNITS_PER_MSEC (10000) -static BOOLEAN TimerInitDone = FALSE; - /* FUNCTIONS **************************************************************/ - -NTSTATUS STDCALL -NtQueryTimerResolution(OUT PULONG MinimumResolution, - OUT PULONG MaximumResolution, - OUT PULONG ActualResolution) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - - -NTSTATUS STDCALL -NtSetTimerResolution(IN ULONG DesiredResolution, - IN BOOLEAN SetResolution, - OUT PULONG CurrentResolution) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - - -NTSTATUS STDCALL -NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter, - OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL) -{ - LARGE_INTEGER PerfCounter; - LARGE_INTEGER PerfFrequency; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(PerformanceCounter, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - if(PerformanceFrequency != NULL) - { - ProbeForWrite(PerformanceFrequency, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - PerfCounter = KeQueryPerformanceCounter(&PerfFrequency); - - _SEH_TRY - { - *PerformanceCounter = PerfCounter; - if(PerformanceFrequency != NULL) - { - *PerformanceFrequency = PerfFrequency; - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - return Status; -} - - -NTSTATUS STDCALL -NtDelayExecution(IN BOOLEAN Alertable, - IN PLARGE_INTEGER DelayInterval) -{ - KPROCESSOR_MODE PreviousMode; - LARGE_INTEGER SafeInterval; - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - NTSTATUS Status = STATUS_SUCCESS; - - _SEH_TRY - { - ProbeForRead(DelayInterval, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - /* make a copy on the kernel stack and let DelayInterval point to it so - we don't need to wrap KeDelayExecutionThread in SEH! */ - SafeInterval = *DelayInterval; - DelayInterval = &SafeInterval; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - return KeDelayExecutionThread(PreviousMode, - Alertable, - DelayInterval); -} - +BOOLEAN STDCALL KiInsertTimer(PKTIMER Timer, LARGE_INTEGER DueTime); +VOID STDCALL KiHandleExpiredTimer(PKTIMER Timer); /* * @implemented - */ -NTSTATUS STDCALL -KeDelayExecutionThread (KPROCESSOR_MODE WaitMode, - BOOLEAN Alertable, - PLARGE_INTEGER Interval) -/* - * FUNCTION: Puts the current thread into an alertable or nonalertable - * wait state for a given internal - * ARGUMENTS: - * WaitMode = Processor mode in which the caller is waiting - * Altertable = Specifies if the wait is alertable - * Interval = Specifies the interval to wait - * RETURNS: Status - */ -{ - PKTHREAD Thread = KeGetCurrentThread(); - - KeSetTimer(&Thread->Timer, *Interval, NULL); - return (KeWaitForSingleObject(&Thread->Timer, - (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */ - WaitMode, /* TMN: Was UserMode */ - Alertable, - NULL)); -} - - -/* - * @implemented - */ -ULONG STDCALL -KeQueryTimeIncrement(VOID) -/* - * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to - * the system clock every time the clock interrupts - * RETURNS: The increment - */ -{ - return(CLOCK_INCREMENT); -} - - -/* - * FUNCTION: Gets the current system time - * ARGUMENTS: - * CurrentTime (OUT) = The routine stores the current time here - * NOTE: The time is the number of 100-nanosecond intervals since the - * 1st of January, 1601. * - * @implemented - */ -VOID STDCALL -KeQuerySystemTime(PLARGE_INTEGER CurrentTime) -{ - do - { - CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Time; - CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart; - } - while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Time); -} - -ULONGLONG STDCALL -KeQueryInterruptTime(VOID) -{ - LARGE_INTEGER CurrentTime; - - do - { - CurrentTime.u.HighPart = SharedUserData->InterruptTime.High1Time; - CurrentTime.u.LowPart = SharedUserData->InterruptTime.LowPart; - } - while (CurrentTime.u.HighPart != SharedUserData->InterruptTime.High2Time); - - return CurrentTime.QuadPart; -} - -/* - * @implemented - */ -ULONG -STDCALL -NtGetTickCount(VOID) -{ - LARGE_INTEGER TickCount; - KeQueryTickCount(&TickCount); - return TickCount.u.LowPart; -} - -/* - * @implemented - */ -BOOLEAN STDCALL -KeSetTimer (PKTIMER Timer, - LARGE_INTEGER DueTime, - PKDPC Dpc) -/* - * FUNCTION: Sets the absolute or relative interval at which a timer object - * is to be set to the signaled state and optionally supplies a - * CustomTimerDpc to be executed when the timer expires. - * ARGUMENTS: - * Timer = Points to a previously initialized timer object - * DueTimer = If positive then absolute time to expire at - * If negative then the relative time to expire at - * Dpc = If non-NULL then a dpc to be called when the timer expires - * RETURNS: True if the timer was already in the system timer queue - * False otherwise - */ -{ - return(KeSetTimerEx(Timer, DueTime, 0, Dpc)); -} - -/* - * @implemented - */ -BOOLEAN STDCALL -KeSetTimerEx (PKTIMER Timer, - LARGE_INTEGER DueTime, - LONG Period, - PKDPC Dpc) -/* - * FUNCTION: Sets the absolute or relative interval at which a timer object - * is to be set to the signaled state and optionally supplies a - * CustomTimerDpc to be executed when the timer expires. - * ARGUMENTS: - * Timer = Points to a previously initialized timer object - * DueTimer = If positive then absolute time to expire at - * If negative then the relative time to expire at - * Dpc = If non-NULL then a dpc to be called when the timer expires - * RETURNS: True if the timer was already in the system timer queue - * False otherwise - */ -{ - KIRQL oldlvl; - LARGE_INTEGER Time; - BOOLEAN AlreadyInList; - - DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer); - - ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - - KeAcquireSpinLock(&TimerListLock, &oldlvl); - - Timer->Dpc = Dpc; - if (DueTime.QuadPart < 0) - { - Timer->Header.Absolute = 0; - Timer->DueTime.QuadPart = KeQueryInterruptTime() - DueTime.QuadPart; - } - else - { - KeQuerySystemTime(&Time); - Timer->Header.Absolute = 1; - if (DueTime.QuadPart >= Time.QuadPart) - { - Timer->DueTime.QuadPart = DueTime.QuadPart; - } - else - { - Timer->DueTime.QuadPart = Time.QuadPart; - } - } - Timer->Period = Period; - Timer->Header.SignalState = FALSE; - AlreadyInList = (Timer->TimerListEntry.Flink == NULL) ? FALSE : TRUE; - ASSERT((Timer->TimerListEntry.Flink == NULL && Timer->TimerListEntry.Blink == NULL) || - (Timer->TimerListEntry.Flink != NULL && Timer->TimerListEntry.Blink != NULL)); - if (AlreadyInList) - { - RemoveEntryList(&Timer->TimerListEntry); - } - if (Timer->Header.Absolute) - { - InsertAscendingList(&AbsoluteTimerListHead, - KTIMER, - TimerListEntry, - Timer, - DueTime.QuadPart); - - } - else - { - InsertAscendingList(&RelativeTimerListHead, - KTIMER, - TimerListEntry, - Timer, - DueTime.QuadPart); - - } - - KeReleaseSpinLock(&TimerListLock, oldlvl); - - return AlreadyInList; -} - -/* - * @implemented - */ -BOOLEAN STDCALL -KeCancelTimer (PKTIMER Timer) -/* * FUNCTION: Removes a timer from the system timer list * ARGUMENTS: * Timer = timer to cancel * RETURNS: True if the timer was running * False otherwise */ +BOOLEAN +STDCALL +KeCancelTimer(PKTIMER Timer) { - KIRQL oldlvl; + KIRQL OldIrql; + BOOLEAN Inserted = FALSE; - DPRINT("KeCancelTimer(Timer %x)\n",Timer); + DPRINT("KeCancelTimer(Timer %x)\n",Timer); - KeAcquireSpinLock(&TimerListLock, &oldlvl); + /* Lock the Database and Raise IRQL */ + OldIrql = KeAcquireDispatcherDatabaseLock(); - if (Timer->TimerListEntry.Flink == NULL) - { - KeReleaseSpinLock(&TimerListLock, oldlvl); - return(FALSE); - } - if (Timer->Header.Absolute) - { - ASSERT(&Timer->TimerListEntry != &AbsoluteTimerListHead); - } - else - { - ASSERT(&Timer->TimerListEntry != &RelativeTimerListHead); - } - ASSERT(Timer->TimerListEntry.Flink != &Timer->TimerListEntry); - RemoveEntryList(&Timer->TimerListEntry); - Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL; - KeReleaseSpinLock(&TimerListLock, oldlvl); + /* Check if it's inserted, and remove it if it is */ + if ((Inserted = Timer->Header.Inserted)) { + + /* Remove from list */ + DPRINT("Timer was inserted, removing\n"); + RemoveEntryList(&Timer->TimerListEntry); + Timer->Header.Inserted = FALSE; + + } else { + + DPRINT("Timer was not inserted\n"); + } - return(TRUE); + /* Release Dispatcher Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return the old state */ + DPRINT("Old State: %d\n", Inserted); + return(Inserted); } /* * @implemented - */ -BOOLEAN STDCALL -KeReadStateTimer (PKTIMER Timer) -{ - return (BOOLEAN)(Timer->Header.SignalState); -} - -/* - * @implemented - */ -VOID STDCALL -KeInitializeTimer (PKTIMER Timer) -/* + * * FUNCTION: Initalizes a kernel timer object * ARGUMENTS: * Timer = caller supplied storage for the timer * NOTE: This function initializes a notification timer */ +VOID +STDCALL +KeInitializeTimer (PKTIMER Timer) + { - KeInitializeTimerEx(Timer, NotificationTimer); + /* Call the New Function */ + KeInitializeTimerEx(Timer, NotificationTimer); } /* * @implemented - */ -VOID STDCALL -KeInitializeTimerEx (PKTIMER Timer, - TIMER_TYPE Type) -/* + * * FUNCTION: Initializes a kernel timer object * ARGUMENTS: * Timer = caller supplied storage for the timer @@ -475,443 +97,288 @@ KeInitializeTimerEx (PKTIMER Timer, * syncrhonization timer expires its state is set to signalled until a * single waiting thread is released and then the timer is reset. */ -{ - ULONG IType; - - if (Type == NotificationTimer) - { - IType = InternalNotificationTimer; - } - else if (Type == SynchronizationTimer) - { - IType = InternalSynchronizationTimer; - } - else - { - ASSERT(FALSE); - return; - } - - KeInitializeDispatcherHeader(&Timer->Header, - IType, - sizeof(KTIMER) / sizeof(ULONG), - FALSE); - Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL; -} - -/* - * @implemented - */ -VOID STDCALL -KeQueryTickCount(PLARGE_INTEGER TickCount) -/* - * FUNCTION: Returns the number of ticks since the system was booted - * ARGUMENTS: - * TickCount (OUT) = Points to storage for the number of ticks - */ -{ - TickCount->QuadPart = KeTickCount; -} - -/* - * @implemented - */ -ULONG +VOID STDCALL -KeQueryRuntimeThread( - IN PKTHREAD Thread, - OUT PULONG UserTime - ) +KeInitializeTimerEx (PKTIMER Timer, + TIMER_TYPE Type) { - /* Return the User Time */ - *UserTime = Thread->UserTime; - - /* Return the Kernel Time */ - return Thread->KernelTime; + + DPRINT("KeInitializeTimerEx(%x, %d)\n", Timer, Type); + + /* Initialize the Dispatch Header */ + KeInitializeDispatcherHeader(&Timer->Header, + InternalNotificationTimer + Type, + sizeof(KTIMER) / sizeof(ULONG), + FALSE); + + /* Initalize the List Head and other data */ + InitializeListHead(&Timer->Header.WaitListHead); + Timer->DueTime.QuadPart = 0; + Timer->Period = 0; } + /* * @implemented */ +BOOLEAN +STDCALL +KeReadStateTimer (PKTIMER Timer) +{ + /* Return the Signal State */ + return (BOOLEAN)Timer->Header.SignalState; +} + +/* + * @implemented + * + * FUNCTION: Sets the absolute or relative interval at which a timer object + * is to be set to the signaled state and optionally supplies a + * CustomTimerDpc to be executed when the timer expires. + * ARGUMENTS: + * Timer = Points to a previously initialized timer object + * DueTimer = If positive then absolute time to expire at + * If negative then the relative time to expire at + * Dpc = If non-NULL then a dpc to be called when the timer expires + * RETURNS: True if the timer was already in the system timer queue + * False otherwise + */ +BOOLEAN +STDCALL +KeSetTimer (PKTIMER Timer, + LARGE_INTEGER DueTime, + PKDPC Dpc) +{ + /* Call the newer function and supply a period of 0 */ + return KeSetTimerEx(Timer, DueTime, 0, Dpc); +} + +/* + * @implemented + * + * FUNCTION: Sets the absolute or relative interval at which a timer object + * is to be set to the signaled state and optionally supplies a + * CustomTimerDpc to be executed when the timer expires. + * ARGUMENTS: + * Timer = Points to a previously initialized timer object + * DueTimer = If positive then absolute time to expire at + * If negative then the relative time to expire at + * Dpc = If non-NULL then a dpc to be called when the timer expires + * RETURNS: True if the timer was already in the system timer queue + * False otherwise + */ +BOOLEAN +STDCALL +KeSetTimerEx (PKTIMER Timer, + LARGE_INTEGER DueTime, + LONG Period, + PKDPC Dpc) +{ + KIRQL OldIrql; + BOOLEAN Inserted; + + DPRINT("KeSetTimerEx(Timer %x, DueTime %I64d, Period %d, Dpc %x)\n", Timer, DueTime.QuadPart, Period, Dpc); + ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + /* Lock the Database and Raise IRQL */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Check if it's inserted, and remove it if it is */ + if ((Inserted = Timer->Header.Inserted)) { + + /* Remove from list */ + DPRINT("Timer was already inserted\n"); + RemoveEntryList(&Timer->TimerListEntry); + Timer->Header.Inserted = FALSE; + } + + /* Set Default Timer Data */ + Timer->Dpc = Dpc; + Timer->Period = Period; + Timer->Header.SignalState = FALSE; + Timer->Header.Absolute = FALSE; + + /* Insert it */ + if (!KiInsertTimer(Timer, DueTime)) { + + /* FIXME: I will think about how to handle this and fix it ASAP -- Alex */ + DPRINT1("CRITICAL UNHANDLED CASE: TIMER ALREADY EXPIRED!!!\n"); + }; + + /* Release Dispatcher Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return old state */ + DPRINT("Old State: %d\n", Inserted); + return Inserted; +} + VOID STDCALL -KeSetTimeIncrement( - IN ULONG MaxIncrement, - IN ULONG MinIncrement -) +KiExpireTimers(PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2) { - /* Set some Internal Variables */ - /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */ - KeMaximumIncrement = MaxIncrement; - KeMinimumIncrement = MinIncrement; + ULONG Eip = (ULONG)SystemArgument1; + PKTIMER Timer = NULL; + ULONGLONG InterruptTime; + LIST_ENTRY ExpiredTimerList; + PLIST_ENTRY CurrentEntry = NULL; + KIRQL OldIrql; + + DPRINT("KiExpireTimers(Dpc: %x)\n", Dpc); + + /* Lock the Database and Raise IRQL */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Initialize the Expired Timer List */ + InitializeListHead(&ExpiredTimerList); + + /* Query Interrupt Times */ + InterruptTime = KeQueryInterruptTime(); + + /* Loop through the Timer List and remove Expired Timers. Insert them into the Expired Listhead */ + for (CurrentEntry = KiTimerListHead.Flink; CurrentEntry != &KiTimerListHead; CurrentEntry = CurrentEntry->Flink) { + + /* Get the Current Timer */ + Timer = CONTAINING_RECORD(CurrentEntry, KTIMER, TimerListEntry); + DPRINT("Looping for Timer: %x. Duetime: %I64d. InterruptTime %I64d \n", Timer, Timer->DueTime.QuadPart, InterruptTime); + + /* Check if we have to Expire it */ + if (InterruptTime < Timer->DueTime.QuadPart) break; + + /* Remove it from the Timer List, add it to the Expired List */ + RemoveEntryList(&Timer->TimerListEntry); + InsertTailList(&ExpiredTimerList, &Timer->TimerListEntry); + } + + /* Expire the Timers */ + while (!IsListEmpty(&ExpiredTimerList)) { + + /* Get the Next Entry */ + CurrentEntry = RemoveHeadList(&ExpiredTimerList); + + /* Get the Timer */ + Timer = CONTAINING_RECORD(CurrentEntry, KTIMER, TimerListEntry); + DPRINT("Expiring Timer: %x\n", Timer); + + /* Expire it */ + KiHandleExpiredTimer(Timer); + } + + DPRINT("Timers expired\n"); + /* Add a Profile Event */ + KiAddProfileEvent(ProfileTime, Eip); + + /* Release Dispatcher Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); } /* * We enter this function at IRQL DISPATCH_LEVEL, and with the - * TimerListLock held. - */ -STATIC VOID -HandleExpiredTimer(PKTIMER Timer) -{ - DPRINT("HandleExpiredTime(Timer %x)\n", Timer); - if (Timer->Dpc != NULL) - { - DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n", - Timer->Dpc, Timer->Dpc->DeferredRoutine); - KeInsertQueueDpc(Timer->Dpc, - NULL, - NULL); - DPRINT("Finished dpc routine\n"); - } - - ASSERT_IRQL_EQUAL(DISPATCH_LEVEL); - - KeAcquireDispatcherDatabaseLockAtDpcLevel(); - Timer->Header.SignalState = TRUE; - KiDispatcherObjectWake(&Timer->Header, 0); - KeReleaseDispatcherDatabaseLockFromDpcLevel(); - - if (Timer->Period != 0) - { - Timer->DueTime.QuadPart += - Timer->Period * SYSTEM_TIME_UNITS_PER_MSEC; - if (Timer->Header.Absolute) - { - InsertAscendingList(&AbsoluteTimerListHead, - KTIMER, - TimerListEntry, - Timer, - DueTime.QuadPart); - } - else - { - InsertAscendingList(&RelativeTimerListHead, - KTIMER, - TimerListEntry, - Timer, - DueTime.QuadPart); - } - } -} - -VOID STDCALL -KeExpireTimers(PKDPC Dpc, - PVOID Context1, - PVOID Arg1, - PVOID Arg2) -{ - PLIST_ENTRY current_entry = NULL; - PKTIMER current = NULL; - ULONG Eip = (ULONG)Arg1; - LARGE_INTEGER InterruptTime; - LARGE_INTEGER SystemTime; - LIST_ENTRY TimerList; - - DPRINT("KeExpireTimers()\n"); - - ASSERT_IRQL_EQUAL(DISPATCH_LEVEL); - - InitializeListHead(&TimerList); - - KeAcquireSpinLockAtDpcLevel(&TimerListLock); - - InterruptTime.QuadPart = KeQueryInterruptTime(); - KeQuerySystemTime(&SystemTime); - - current_entry = RelativeTimerListHead.Flink; - ASSERT(current_entry); - while (current_entry != &RelativeTimerListHead) - { - current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry); - ASSERT(current); - ASSERT(current_entry != &RelativeTimerListHead); - ASSERT(current_entry->Flink != current_entry); - if ((ULONGLONG)InterruptTime.QuadPart < current->DueTime.QuadPart) - { - break; - } - current_entry = current_entry->Flink; - RemoveEntryList(¤t->TimerListEntry); - InsertTailList(&TimerList, ¤t->TimerListEntry); - } - - current_entry = AbsoluteTimerListHead.Flink; - ASSERT(current_entry); - while (current_entry != &AbsoluteTimerListHead) - { - current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry); - ASSERT(current); - ASSERT(current_entry != &AbsoluteTimerListHead); - ASSERT(current_entry->Flink != current_entry); - if ((ULONGLONG)SystemTime.QuadPart < current->DueTime.QuadPart) - { - break; - } - current_entry = current_entry->Flink; - RemoveEntryList(¤t->TimerListEntry); - InsertTailList(&TimerList, ¤t->TimerListEntry); - } - - while (!IsListEmpty(&TimerList)) - { - current_entry = RemoveHeadList(&TimerList); - current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry); - current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL; - HandleExpiredTimer(current); - } - - KiAddProfileEvent(ProfileTime, Eip); - - KeReleaseSpinLockFromDpcLevel(&TimerListLock); -} - - -VOID INIT_FUNCTION -KeInitializeTimerImpl(VOID) -/* - * FUNCTION: Initializes timer irq handling - * NOTE: This is only called once from main() - */ -{ - TIME_FIELDS TimeFields; - - DPRINT("KeInitializeTimerImpl()\n"); - InitializeListHead(&AbsoluteTimerListHead); - InitializeListHead(&RelativeTimerListHead); - KeInitializeSpinLock(&TimerListLock); - KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0); - /* - * Calculate the starting time for the system clock - */ - HalQueryRealTimeClock(&TimeFields); - RtlTimeFieldsToTime(&TimeFields, &SystemBootTime); - - SharedUserData->TickCountLowDeprecated = 0; - SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310 - SharedUserData->InterruptTime.High2Time = 0; - SharedUserData->InterruptTime.LowPart = 0; - SharedUserData->InterruptTime.High1Time = 0; - SharedUserData->SystemTime.High2Time = SystemBootTime.u.HighPart; - SharedUserData->SystemTime.LowPart = SystemBootTime.u.LowPart; - SharedUserData->SystemTime.High1Time = SystemBootTime.u.HighPart; - - TimerInitDone = TRUE; - DPRINT("Finished KeInitializeTimerImpl()\n"); -} - -/* - * @unimplemented - */ -VOID -FASTCALL -KeSetTimeUpdateNotifyRoutine( - IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine - ) -{ - UNIMPLEMENTED; -} - - -/* - * NOTE: On Windows this function takes exactly one parameter and EBP is - * guaranteed to point to KTRAP_FRAME. The function is used only - * by HAL, so there's no point in keeping that prototype. - * - * @implemented - */ -VOID -STDCALL -KeUpdateRunTime( - IN PKTRAP_FRAME TrapFrame, - IN KIRQL Irql - ) -{ - PKPCR Pcr; - PKTHREAD CurrentThread; - PKPROCESS CurrentProcess; -#if 0 - ULONG DpcLastCount; -#endif - - Pcr = KeGetCurrentKPCR(); - - /* Make sure we don't go further if we're in early boot phase. */ - if (Pcr == NULL || Pcr->PrcbData.CurrentThread == NULL) - return; - - DPRINT("KernelTime %u, UserTime %u \n", Pcr->PrcbData.KernelTime, Pcr->PrcbData.UserTime); - - CurrentThread = Pcr->PrcbData.CurrentThread; - CurrentProcess = CurrentThread->ApcState.Process; - - /* - * Cs bit 0 is always set for user mode if we are in protected mode. - * V86 mode is counted as user time. - */ - if (TrapFrame->Cs & 0x1 || - TrapFrame->Eflags & X86_EFLAGS_VM) - { - InterlockedIncrementUL(&CurrentThread->UserTime); - InterlockedIncrementUL(&CurrentProcess->UserTime); - Pcr->PrcbData.UserTime++; - } - else - { - if (Irql > DISPATCH_LEVEL) - { - Pcr->PrcbData.InterruptTime++; - } - else if (Irql == DISPATCH_LEVEL) - { - Pcr->PrcbData.DpcTime++; - } - else - { - InterlockedIncrementUL(&CurrentThread->KernelTime); - InterlockedIncrementUL(&CurrentProcess->KernelTime); - Pcr->PrcbData.KernelTime++; - } - } - -#if 0 - DpcLastCount = Pcr->PrcbData.DpcLastCount; - Pcr->PrcbData.DpcLastCount = Pcr->PrcbData.DpcCount; - Pcr->PrcbData.DpcRequestRate = ((Pcr->PrcbData.DpcCount - DpcLastCount) + - Pcr->PrcbData.DpcRequestRate) / 2; -#endif - - if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0 && - Pcr->PrcbData.DpcRoutineActive == FALSE && - Pcr->PrcbData.DpcInterruptRequested == FALSE) - { - HalRequestSoftwareInterrupt(DISPATCH_LEVEL); - } - - /* FIXME: Do DPC rate adjustments */ - - /* - * If we're at end of quantum request software interrupt. The rest - * is handled in KiDispatchInterrupt. - */ - if ((CurrentThread->Quantum -= 3) <= 0) - { - Pcr->PrcbData.QuantumEnd = TRUE; - HalRequestSoftwareInterrupt(DISPATCH_LEVEL); - } -} - - -/* - * NOTE: On Windows this function takes exactly zero parameters and EBP is - * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL. - * The function is used only by HAL, so there's no point in keeping - * that prototype. - * - * @implemented + * Dispatcher Lock held! */ VOID STDCALL -KeUpdateSystemTime( - IN PKTRAP_FRAME TrapFrame, - IN KIRQL Irql - ) +KiHandleExpiredTimer(PKTIMER Timer) +{ + + LARGE_INTEGER DueTime; + DPRINT("HandleExpiredTime(Timer %x)\n", Timer); + + /* First of all, remove the Timer */ + Timer->Header.Inserted = FALSE; + RemoveEntryList(&Timer->TimerListEntry); + + /* Set it as Signaled */ + DPRINT("Setting Timer as Signaled\n"); + Timer->Header.SignalState = TRUE; + KiDispatcherObjectWake(&Timer->Header, 0); + + /* If the Timer is periodic, reinsert the timer with the new due time */ + if (Timer->Period) { + + /* Reinsert the Timer */ + DueTime.QuadPart = Timer->Period * -SYSTEM_TIME_UNITS_PER_MSEC; + if (!KiInsertTimer(Timer, DueTime)) { + + /* FIXME: I will think about how to handle this and fix it ASAP -- Alex */ + DPRINT1("CRITICAL UNHANDLED CASE: TIMER ALREADY EXPIRED!!!\n"); + }; + } + + /* Check if the Timer has a DPC */ + if (Timer->Dpc) { + + DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n", Timer->Dpc, Timer->Dpc->DeferredRoutine); + + /* Insert the DPC */ + KeInsertQueueDpc(Timer->Dpc, + NULL, + NULL); + + DPRINT("Finished dpc routine\n"); + } +} + /* - * FUNCTION: Handles a timer interrupt + * Note: This function is called with the Dispatcher Lock held. */ +BOOLEAN +STDCALL +KiInsertTimer(PKTIMER Timer, + LARGE_INTEGER DueTime) { - LARGE_INTEGER Time; - - ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL); - - KiRawTicks++; - - if (TimerInitDone == FALSE) - { - return; - } - /* - * Increment the number of timers ticks - */ - KeTickCount++; - SharedUserData->TickCountLowDeprecated++; - - Time.u.LowPart = SharedUserData->InterruptTime.LowPart; - Time.u.HighPart = SharedUserData->InterruptTime.High1Time; - Time.QuadPart += CLOCK_INCREMENT; - SharedUserData->InterruptTime.High2Time = Time.u.HighPart; - SharedUserData->InterruptTime.LowPart = Time.u.LowPart; - SharedUserData->InterruptTime.High1Time = Time.u.HighPart; - - Time.u.LowPart = SharedUserData->SystemTime.LowPart; - Time.u.HighPart = SharedUserData->SystemTime.High1Time; - Time.QuadPart += CLOCK_INCREMENT; - SharedUserData->SystemTime.High2Time = Time.u.HighPart; - SharedUserData->SystemTime.LowPart = Time.u.LowPart; - SharedUserData->SystemTime.High1Time = Time.u.HighPart; - - /* FIXME: Here we should check for remote debugger break-ins */ - - /* Update process and thread times */ - KeUpdateRunTime(TrapFrame, Irql); - - /* - * Queue a DPC that will expire timers - */ - KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)TrapFrame->Eip, 0); -} - - -VOID -KiSetSystemTime(PLARGE_INTEGER NewSystemTime) -{ - LARGE_INTEGER OldSystemTime; - LARGE_INTEGER DeltaTime; - KIRQL OldIrql; - PLIST_ENTRY current_entry = NULL; - PKTIMER current = NULL; - - ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - - OldIrql = KeAcquireDispatcherDatabaseLock(); - - do - { - OldSystemTime.u.HighPart = SharedUserData->SystemTime.High1Time; - OldSystemTime.u.LowPart = SharedUserData->SystemTime.LowPart; + LARGE_INTEGER SystemTime; + LARGE_INTEGER DifferenceTime; + ULONGLONG InterruptTime; + + DPRINT("KiInsertTimer(Timer %x DueTime %I64d)\n", Timer, DueTime.QuadPart); + + /* Set it as Inserted */ + Timer->Header.Inserted = TRUE; + + /* Convert to relative time if needed */ + if (DueTime.u.HighPart >= 0) { + + /* Get System Time */ + KeQuerySystemTime(&SystemTime); + + /* Do the conversion */ + DifferenceTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart; + DPRINT("Time Difference is: %I64d\n", DifferenceTime.QuadPart); + + /* Make sure it hasn't already expired */ + if (DifferenceTime.u.HighPart >= 0) { + + /* Cancel everything */ + DPRINT("Timer already expired: %d\n", DifferenceTime.u.HighPart); + Timer->Header.SignalState = TRUE; + Timer->Header.Inserted = FALSE; + return FALSE; + } + + /* Set the time as Absolute */ + Timer->Header.Absolute = TRUE; + DueTime = DifferenceTime; } - while (OldSystemTime.u.HighPart != SharedUserData->SystemTime.High2Time); - - /* Set the new system time */ - SharedUserData->SystemTime.LowPart = NewSystemTime->u.LowPart; - SharedUserData->SystemTime.High1Time = NewSystemTime->u.HighPart; - SharedUserData->SystemTime.High2Time = NewSystemTime->u.HighPart; - - /* Calculate the difference between the new and the old time */ - DeltaTime.QuadPart = NewSystemTime->QuadPart - OldSystemTime.QuadPart; - - /* Update system boot time */ - SystemBootTime.QuadPart += DeltaTime.QuadPart; - - /* Update all relative timers */ - current_entry = RelativeTimerListHead.Flink; - ASSERT(current_entry); - while (current_entry != &RelativeTimerListHead) - { - current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry); - ASSERT(current); - ASSERT(current_entry != &RelativeTimerListHead); - ASSERT(current_entry->Flink != current_entry); - - current->DueTime.QuadPart += DeltaTime.QuadPart; - - current_entry = current_entry->Flink; - } - - KeReleaseDispatcherDatabaseLock(OldIrql); - - /* - * NOTE: Expired timers will be processed at the next clock tick! - */ + + /* Get the Interrupt Time */ + InterruptTime = KeQueryInterruptTime(); + + /* Set the Final Due Time */ + Timer->DueTime.QuadPart = InterruptTime - DueTime.QuadPart; + DPRINT("Final Due Time is: %I64d\n", Timer->DueTime.QuadPart); + + /* Now insert it into the Timer List */ + DPRINT("Inserting Timer into list\n"); + InsertAscendingList(&KiTimerListHead, + KTIMER, + TimerListEntry, + Timer, + DueTime.QuadPart); + + return TRUE; } - /* EOF */ diff --git a/reactos/ntoskrnl/ke/wait.c b/reactos/ntoskrnl/ke/wait.c index 3cff12ee706..8e4da0f3ae2 100644 --- a/reactos/ntoskrnl/ke/wait.c +++ b/reactos/ntoskrnl/ke/wait.c @@ -397,6 +397,32 @@ BOOLEAN KiDispatcherObjectWake(DISPATCHER_HEADER* hdr, KPRIORITY increment) return(FALSE); } +/* + * @implemented + */ +NTSTATUS STDCALL +KeDelayExecutionThread (KPROCESSOR_MODE WaitMode, + BOOLEAN Alertable, + PLARGE_INTEGER Interval) +/* + * FUNCTION: Puts the current thread into an alertable or nonalertable + * wait state for a given internal + * ARGUMENTS: + * WaitMode = Processor mode in which the caller is waiting + * Altertable = Specifies if the wait is alertable + * Interval = Specifies the interval to wait + * RETURNS: Status + */ +{ + PKTHREAD Thread = KeGetCurrentThread(); + + KeSetTimer(&Thread->Timer, *Interval, NULL); + return (KeWaitForSingleObject(&Thread->Timer, + (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */ + WaitMode, /* TMN: Was UserMode */ + Alertable, + NULL)); +} /* * @implemented @@ -585,13 +611,13 @@ KeWaitForMultipleObjects(ULONG Count, { Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned; + KeReleaseDispatcherDatabaseLock(OldIrql); + if (Timeout != NULL && Timeout->QuadPart != 0) { KeCancelTimer(&CurrentThread->Timer); } - KeReleaseDispatcherDatabaseLock(OldIrql); - DPRINT("One object is (already) signaled!\n"); if (Abandoned == TRUE) { @@ -612,12 +638,13 @@ KeWaitForMultipleObjects(ULONG Count, Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned; } + KeReleaseDispatcherDatabaseLock(OldIrql); + if (Timeout != NULL && Timeout->QuadPart != 0) { KeCancelTimer(&CurrentThread->Timer); } - KeReleaseDispatcherDatabaseLock(OldIrql); DPRINT("All objects are (already) signaled!\n"); if (Abandoned == TRUE) @@ -641,8 +668,8 @@ KeWaitForMultipleObjects(ULONG Count, if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread)) { KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread); - KeCancelTimer(&CurrentThread->Timer); KeReleaseDispatcherDatabaseLock(OldIrql); + KeCancelTimer(&CurrentThread->Timer); return (STATUS_TIMEOUT); }