2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ex/timer.c
5 * PURPOSE: User-mode timers
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Reimplemented
8 * David Welch (welch@mcmail.com)
11 /* INCLUDES *****************************************************************/
16 #include <internal/debug.h>
18 /* TYPES ********************************************************************/
20 /* Executive Timer Object */
21 typedef struct _ETIMER
{
25 LIST_ENTRY ActiveTimerListEntry
;
27 BOOLEAN ApcAssociated
;
29 LIST_ENTRY WakeTimerListEntry
;
32 /* GLOBALS ******************************************************************/
34 /* Timer Object Type */
35 POBJECT_TYPE ExTimerType
= NULL
;
37 KSPIN_LOCK ExpWakeListLock
;
38 LIST_ENTRY ExpWakeList
;
41 static GENERIC_MAPPING ExpTimerMapping
= {
42 STANDARD_RIGHTS_READ
| TIMER_QUERY_STATE
,
43 STANDARD_RIGHTS_WRITE
| TIMER_MODIFY_STATE
,
44 STANDARD_RIGHTS_EXECUTE
| SYNCHRONIZE
,
48 /* Timer Information Classes */
49 static const INFORMATION_CLASS_INFO ExTimerInfoClass
[] = {
51 /* TimerBasicInformation */
52 ICI_SQ_SAME( sizeof(TIMER_BASIC_INFORMATION
), sizeof(ULONG
), ICIF_QUERY
),
55 /* FUNCTIONS *****************************************************************/
61 PETHREAD Thread
= PsGetCurrentThread();
63 PLIST_ENTRY CurrentEntry
;
66 /* Lock the Thread's Active Timer List*/
67 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &OldIrql
);
69 while (!IsListEmpty(&Thread
->ActiveTimerListHead
))
73 CurrentEntry
= RemoveTailList(&Thread
->ActiveTimerListHead
);
76 Timer
= CONTAINING_RECORD(CurrentEntry
, ETIMER
, ActiveTimerListEntry
);
78 ASSERT (Timer
->ApcAssociated
);
79 Timer
->ApcAssociated
= FALSE
;
81 DPRINT("Timer, ThreadList: %x, %x\n", Timer
, Thread
);
84 KeReleaseSpinLockFromDpcLevel(&Thread
->ActiveTimerListLock
);
87 KeAcquireSpinLockAtDpcLevel(&Timer
->Lock
);
89 ASSERT (&Thread
->Tcb
== Timer
->TimerApc
.Thread
);
91 KeCancelTimer(&Timer
->KeTimer
);
92 KeRemoveQueueDpc(&Timer
->TimerDpc
);
93 KeRemoveQueueApc(&Timer
->TimerApc
);
96 /* Unlock the Timer */
97 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
99 /* Dereference it, if needed */
100 ObDereferenceObject(Timer
);
103 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &OldIrql
);
106 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, OldIrql
);
111 ExpDeleteTimer(PVOID ObjectBody
)
114 PETIMER Timer
= ObjectBody
;
116 DPRINT("ExpDeleteTimer(Timer: %x)\n", Timer
);
118 /* Lock the Wake List */
119 KeAcquireSpinLock(&ExpWakeListLock
, &OldIrql
);
121 /* Check if it has a Wait List */
122 if (Timer
->WakeTimer
) {
124 /* Remove it from the Wait List */
125 DPRINT("Removing wake list\n");
126 RemoveEntryList(&Timer
->WakeTimerListEntry
);
127 Timer
->WakeTimer
= FALSE
;
130 /* Release the Wake List */
131 KeReleaseSpinLock(&ExpWakeListLock
, OldIrql
);
133 /* Tell the Kernel to cancel the Timer */
134 DPRINT("Cancelling Timer\n");
135 KeCancelTimer(&Timer
->KeTimer
);
140 ExpTimerDpcRoutine(PKDPC Dpc
,
141 PVOID DeferredContext
,
142 PVOID SystemArgument1
,
143 PVOID SystemArgument2
)
148 DPRINT("ExpTimerDpcRoutine(Dpc: %x)\n", Dpc
);
150 /* Get the Timer Object */
151 Timer
= (PETIMER
)DeferredContext
;
154 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
157 if(Timer
->ApcAssociated
) {
159 DPRINT("Queuing APC\n");
160 KeInsertQueueApc(&Timer
->TimerApc
,
166 /* Release the Timer */
167 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
173 ExpTimerApcKernelRoutine(PKAPC Apc
,
174 PKNORMAL_ROUTINE
* NormalRoutine
,
175 PVOID
* NormalContext
,
176 PVOID
* SystemArgument1
,
177 PVOID
* SystemArguemnt2
)
181 PETHREAD CurrentThread
= PsGetCurrentThread();
183 /* We need to find out which Timer we are */
184 Timer
= CONTAINING_RECORD(Apc
, ETIMER
, TimerApc
);
185 DPRINT("ExpTimerApcKernelRoutine(Apc: %x. Timer: %x)\n", Apc
, Timer
);
188 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
190 /* Lock the Thread's Active Timer List*/
191 KeAcquireSpinLockAtDpcLevel(&CurrentThread
->ActiveTimerListLock
);
194 * Make sure that the Timer is still valid, and that it belongs to this thread
195 * Remove it if it's not periodic
197 if ((Timer
->ApcAssociated
) &&
198 (&CurrentThread
->Tcb
== Timer
->TimerApc
.Thread
) &&
199 (!Timer
->KeTimer
.Period
)) {
201 /* Remove it from the Active Timers List */
202 DPRINT("Removing Timer\n");
203 RemoveEntryList(&Timer
->ActiveTimerListEntry
);
206 Timer
->ApcAssociated
= FALSE
;
208 /* Release spinlocks */
209 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
210 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
212 /* Dereference the Timer Object */
213 ObDereferenceObject(Timer
);
217 /* Release spinlocks */
218 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
219 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
224 ExpInitializeTimerImplementation(VOID
)
226 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
229 DPRINT("Creating Timer Object Type\n");
231 /* Create the Event Pair Object Type */
232 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
233 RtlInitUnicodeString(&Name
, L
"Timer");
234 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
235 ObjectTypeInitializer
.DefaultNonPagedPoolCharge
= sizeof(KTIMER
);
236 ObjectTypeInitializer
.GenericMapping
= ExpTimerMapping
;
237 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
238 ObjectTypeInitializer
.ValidAccessMask
= TIMER_ALL_ACCESS
;
239 ObjectTypeInitializer
.UseDefaultObject
= TRUE
;
240 ObpCreateTypeObject(&ObjectTypeInitializer
, &Name
, &ExTimerType
);
242 /* Initialize the Wait List and Lock */
243 KeInitializeSpinLock(&ExpWakeListLock
);
244 InitializeListHead(&ExpWakeList
);
250 NtCancelTimer(IN HANDLE TimerHandle
,
251 OUT PBOOLEAN CurrentState OPTIONAL
)
254 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
257 PETHREAD TimerThread
;
258 BOOLEAN KillTimer
= FALSE
;
259 NTSTATUS Status
= STATUS_SUCCESS
;
262 DPRINT("NtCancelTimer(0x%x, 0x%x)\n", TimerHandle
, CurrentState
);
264 /* Check Parameter Validity */
265 if(CurrentState
!= NULL
&& PreviousMode
!= KernelMode
) {
267 ProbeForWrite(CurrentState
,
270 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
271 Status
= _SEH_GetExceptionCode();
274 if(!NT_SUCCESS(Status
)) {
279 /* Get the Timer Object */
280 Status
= ObReferenceObjectByHandle(TimerHandle
,
287 /* Check for success */
288 if(NT_SUCCESS(Status
)) {
290 DPRINT("Timer Referencced: %x\n", Timer
);
293 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
295 /* Check if it's enabled */
296 if (Timer
->ApcAssociated
) {
299 * First, remove it from the Thread's Active List
302 TimerThread
= CONTAINING_RECORD(Timer
->TimerApc
.Thread
, ETHREAD
, Tcb
);
303 DPRINT("Removing from Thread: %x\n", TimerThread
);
305 /* Lock its active list */
306 KeAcquireSpinLockAtDpcLevel(&TimerThread
->ActiveTimerListLock
);
309 RemoveEntryList(&TimerThread
->ActiveTimerListHead
);
311 /* Unlock the list */
312 KeReleaseSpinLockFromDpcLevel(&TimerThread
->ActiveTimerListLock
);
314 /* Cancel the Timer */
315 KeCancelTimer(&Timer
->KeTimer
);
316 KeRemoveQueueDpc(&Timer
->TimerDpc
);
317 KeRemoveQueueApc(&Timer
->TimerApc
);
318 Timer
->ApcAssociated
= FALSE
;
323 /* If timer was disabled, we still need to cancel it */
324 DPRINT("APC was not Associated. Cancelling Timer\n");
325 KeCancelTimer(&Timer
->KeTimer
);
328 /* Read the old State */
329 State
= KeReadStateTimer(&Timer
->KeTimer
);
331 /* Dereference the Object */
332 ObDereferenceObject(Timer
);
334 /* Unlock the Timer */
335 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
337 /* Dereference if it was previously enabled */
338 if (KillTimer
) ObDereferenceObject(Timer
);
339 DPRINT1("Timer disabled\n");
341 /* Make sure it's safe to write to the handle */
342 if(CurrentState
!= NULL
) {
346 *CurrentState
= State
;
348 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
350 Status
= _SEH_GetExceptionCode();
356 /* Return to Caller */
363 NtCreateTimer(OUT PHANDLE TimerHandle
,
364 IN ACCESS_MASK DesiredAccess
,
365 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
366 IN TIMER_TYPE TimerType
)
370 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
371 NTSTATUS Status
= STATUS_SUCCESS
;
374 DPRINT("NtCreateTimer(Handle: %x, Type: %d)\n", TimerHandle
, TimerType
);
376 /* Check Parameter Validity */
377 if (PreviousMode
!= KernelMode
) {
381 ProbeForWrite(TimerHandle
,
384 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
386 Status
= _SEH_GetExceptionCode();
390 if(!NT_SUCCESS(Status
)) return Status
;
393 /* Create the Object */
394 Status
= ObCreateObject(PreviousMode
,
404 /* Check for Success */
405 if(NT_SUCCESS(Status
)) {
407 /* Initialize the Kernel Timer */
408 DPRINT("Initializing Timer: %x\n", Timer
);
409 KeInitializeTimerEx(&Timer
->KeTimer
, TimerType
);
411 /* Initialize the Timer Lock */
412 KeInitializeSpinLock(&Timer
->Lock
);
414 /* Initialize the DPC */
415 KeInitializeDpc(&Timer
->TimerDpc
, ExpTimerDpcRoutine
, Timer
);
417 /* Set Initial State */
418 Timer
->ApcAssociated
= FALSE
;
419 Timer
->WakeTimer
= FALSE
;
421 /* Insert the Timer */
422 Status
= ObInsertObject((PVOID
)Timer
,
428 DPRINT("Timer Inserted\n");
431 /* Make sure it's safe to write to the handle */
434 *TimerHandle
= hTimer
;
436 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
438 Status
= _SEH_GetExceptionCode();
443 /* Return to Caller */
450 NtOpenTimer(OUT PHANDLE TimerHandle
,
451 IN ACCESS_MASK DesiredAccess
,
452 IN POBJECT_ATTRIBUTES ObjectAttributes
)
455 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
456 NTSTATUS Status
= STATUS_SUCCESS
;
459 DPRINT("NtOpenTimer(TimerHandle: %x)\n", TimerHandle
);
461 /* Check Parameter Validity */
462 if (PreviousMode
!= KernelMode
) {
466 ProbeForWrite(TimerHandle
,
470 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
472 Status
= _SEH_GetExceptionCode();
476 if(!NT_SUCCESS(Status
)) return Status
;
480 Status
= ObOpenObjectByName(ObjectAttributes
,
488 /* Check for success */
489 if(NT_SUCCESS(Status
)) {
491 /* Make sure it's safe to write to the handle */
494 *TimerHandle
= hTimer
;
496 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
498 Status
= _SEH_GetExceptionCode();
503 /* Return to Caller */
510 NtQueryTimer(IN HANDLE TimerHandle
,
511 IN TIMER_INFORMATION_CLASS TimerInformationClass
,
512 OUT PVOID TimerInformation
,
513 IN ULONG TimerInformationLength
,
514 OUT PULONG ReturnLength OPTIONAL
)
517 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
518 NTSTATUS Status
= STATUS_SUCCESS
;
519 PTIMER_BASIC_INFORMATION BasicInfo
= (PTIMER_BASIC_INFORMATION
)TimerInformation
;
522 DPRINT("NtQueryTimer(TimerHandle: %x, Class: %d)\n", TimerHandle
, TimerInformationClass
);
525 DefaultQueryInfoBufferCheck(TimerInformationClass
,
528 TimerInformationLength
,
532 if(!NT_SUCCESS(Status
)) {
534 DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status
);
538 /* Get the Timer Object */
539 Status
= ObReferenceObjectByHandle(TimerHandle
,
546 /* Check for Success */
547 if(NT_SUCCESS(Status
)) {
549 /* Return the Basic Information */
552 /* FIXME: Interrupt correction based on Interrupt Time */
553 DPRINT("Returning Information for Timer: %x. Time Remaining: %d\n", Timer
, Timer
->KeTimer
.DueTime
.QuadPart
);
554 BasicInfo
->TimeRemaining
.QuadPart
= Timer
->KeTimer
.DueTime
.QuadPart
;
555 BasicInfo
->SignalState
= KeReadStateTimer(&Timer
->KeTimer
);
557 if(ReturnLength
!= NULL
) *ReturnLength
= sizeof(TIMER_BASIC_INFORMATION
);
559 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
561 Status
= _SEH_GetExceptionCode();
565 /* Dereference Object */
566 ObDereferenceObject(Timer
);
575 NtSetTimer(IN HANDLE TimerHandle
,
576 IN PLARGE_INTEGER DueTime
,
577 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL
,
578 IN PVOID TimerContext OPTIONAL
,
579 IN BOOLEAN WakeTimer
,
580 IN LONG Period OPTIONAL
,
581 OUT PBOOLEAN PreviousState OPTIONAL
)
586 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
587 PETHREAD CurrentThread
= PsGetCurrentThread();
588 LARGE_INTEGER TimerDueTime
;
589 PETHREAD TimerThread
;
590 BOOLEAN KillTimer
= FALSE
;
591 NTSTATUS Status
= STATUS_SUCCESS
;
594 DPRINT("NtSetTimer(TimerHandle: %x, DueTime: %d, Apc: %x, Period: %d)\n", TimerHandle
, DueTime
->QuadPart
, TimerApcRoutine
, Period
);
596 /* Check Parameter Validity */
597 if (PreviousMode
!= KernelMode
) {
601 ProbeForRead(DueTime
,
602 sizeof(LARGE_INTEGER
),
604 TimerDueTime
= *DueTime
;
606 if(PreviousState
!= NULL
) {
608 ProbeForWrite(PreviousState
,
613 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
615 Status
= _SEH_GetExceptionCode();
619 if(!NT_SUCCESS(Status
)) return Status
;
622 /* Get the Timer Object */
623 Status
= ObReferenceObjectByHandle(TimerHandle
,
631 if (NT_SUCCESS(Status
)) {
634 DPRINT("Timer Referencced: %x\n", Timer
);
635 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
637 /* Cancel Running Timer */
638 if (Timer
->ApcAssociated
) {
641 * First, remove it from the Thread's Active List
644 TimerThread
= CONTAINING_RECORD(Timer
->TimerApc
.Thread
, ETHREAD
, Tcb
);
645 DPRINT("Thread already running. Removing from Thread: %x\n", TimerThread
);
647 /* Lock its active list */
648 KeAcquireSpinLockAtDpcLevel(&TimerThread
->ActiveTimerListLock
);
651 RemoveEntryList(&TimerThread
->ActiveTimerListHead
);
653 /* Unlock the list */
654 KeReleaseSpinLockFromDpcLevel(&TimerThread
->ActiveTimerListLock
);
656 /* Cancel the Timer */
657 KeCancelTimer(&Timer
->KeTimer
);
658 KeRemoveQueueDpc(&Timer
->TimerDpc
);
659 KeRemoveQueueApc(&Timer
->TimerApc
);
660 Timer
->ApcAssociated
= FALSE
;
665 /* If timer was disabled, we still need to cancel it */
666 DPRINT("No APCs. Simply cancelling\n");
667 KeCancelTimer(&Timer
->KeTimer
);
671 State
= KeReadStateTimer(&Timer
->KeTimer
);
673 /* Handle Wake Timers */
674 DPRINT("Doing Wake Semantics\n");
675 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock
);
676 if (WakeTimer
&& !Timer
->WakeTimer
) {
678 /* Insert it into the list */
679 Timer
->WakeTimer
= TRUE
;
680 InsertTailList(&ExpWakeList
, &Timer
->WakeTimerListEntry
);
681 } else if (!WakeTimer
&& Timer
->WakeTimer
) {
683 /* Remove it from the list */
684 RemoveEntryList(&Timer
->WakeTimerListEntry
);
685 Timer
->WakeTimer
= FALSE
;
687 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock
);
689 /* Set up the APC Routine if specified */
690 if (TimerApcRoutine
) {
692 /* Initialize the APC */
693 DPRINT("Initializing APC: %x\n", Timer
->TimerApc
);
694 KeInitializeApc(&Timer
->TimerApc
,
696 CurrentApcEnvironment
,
697 &ExpTimerApcKernelRoutine
,
698 (PKRUNDOWN_ROUTINE
)NULL
,
699 (PKNORMAL_ROUTINE
)TimerApcRoutine
,
703 /* Lock the Thread's Active List and Insert */
704 KeAcquireSpinLockAtDpcLevel(&CurrentThread
->ActiveTimerListLock
);
705 InsertTailList(&CurrentThread
->ActiveTimerListHead
,
706 &Timer
->ActiveTimerListEntry
);
707 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
711 /* Enable and Set the Timer */
712 DPRINT("Setting Kernel Timer\n");
713 KeSetTimerEx(&Timer
->KeTimer
,
716 TimerApcRoutine
? &Timer
->TimerDpc
: 0);
717 Timer
->ApcAssociated
= TimerApcRoutine
? TRUE
: FALSE
;
719 /* Unlock the Timer */
720 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
722 /* Dereference the Object */
723 ObDereferenceObject(Timer
);
725 /* Dereference if it was previously enabled */
726 if (!TimerApcRoutine
) ObDereferenceObject(Timer
);
727 if (KillTimer
) ObDereferenceObject(Timer
);
728 DPRINT("Finished Setting the Timer\n");
730 /* Make sure it's safe to write to the handle */
731 if(PreviousState
!= NULL
) {
735 *PreviousState
= State
;
737 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
) {
739 Status
= _SEH_GetExceptionCode();
745 /* Return to Caller */