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 *****************************************************************/
14 #include <internal/debug.h>
16 /* TYPES ********************************************************************/
18 /* Executive Timer Object */
19 typedef struct _ETIMER
{
23 LIST_ENTRY ActiveTimerListEntry
;
26 BOOLEAN ApcAssociated
;
28 LIST_ENTRY WakeTimerListEntry
;
31 /* GLOBALS ******************************************************************/
33 /* Timer Object Type */
34 POBJECT_TYPE ExTimerType
= NULL
;
36 KSPIN_LOCK ExpWakeListLock
;
37 LIST_ENTRY ExpWakeList
;
40 static GENERIC_MAPPING ExpTimerMapping
= {
41 STANDARD_RIGHTS_READ
| TIMER_QUERY_STATE
,
42 STANDARD_RIGHTS_WRITE
| TIMER_MODIFY_STATE
,
43 STANDARD_RIGHTS_EXECUTE
| SYNCHRONIZE
,
47 /* Timer Information Classes */
48 static const INFORMATION_CLASS_INFO ExTimerInfoClass
[] = {
50 /* TimerBasicInformation */
51 ICI_SQ_SAME( sizeof(TIMER_BASIC_INFORMATION
), sizeof(ULONG
), ICIF_QUERY
),
54 /* FUNCTIONS *****************************************************************/
58 ExpDeleteTimer(PVOID ObjectBody
)
61 PETIMER Timer
= ObjectBody
;
63 DPRINT("ExpDeleteTimer(Timer: %x)\n", Timer
);
65 /* Lock the Wake List */
66 KeAcquireSpinLock(&ExpWakeListLock
, &OldIrql
);
68 /* Check if it has a Wait List */
69 if (!IsListEmpty(&Timer
->WakeTimerListEntry
)) {
71 /* Remove it from the Wait List */
72 DPRINT("Removing wake list\n");
73 RemoveEntryList(&Timer
->WakeTimerListEntry
);
76 /* Release the Wake List */
77 KeReleaseSpinLock(&ExpWakeListLock
, OldIrql
);
79 /* Tell the Kernel to cancel the Timer */
80 DPRINT("Cancelling Timer\n");
81 KeCancelTimer(&Timer
->KeTimer
);
86 ExpTimerDpcRoutine(PKDPC Dpc
,
87 PVOID DeferredContext
,
88 PVOID SystemArgument1
,
89 PVOID SystemArgument2
)
94 DPRINT("ExpTimerDpcRoutine(Dpc: %x)\n", Dpc
);
96 /* Get the Timer Object */
97 Timer
= (PETIMER
)DeferredContext
;
100 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
103 if(Timer
->ApcAssociated
) {
105 DPRINT("Queuing APC\n");
106 KeInsertQueueApc(&Timer
->TimerApc
,
112 /* Release the Timer */
113 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
119 ExpTimerApcKernelRoutine(PKAPC Apc
,
120 PKNORMAL_ROUTINE
* NormalRoutine
,
121 PVOID
* NormalContext
,
122 PVOID
* SystemArgument1
,
123 PVOID
* SystemArguemnt2
)
127 PETHREAD CurrentThread
= PsGetCurrentThread();
129 /* We need to find out which Timer we are */
130 Timer
= CONTAINING_RECORD(Apc
, ETIMER
, TimerApc
);
131 DPRINT("ExpTimerApcKernelRoutine(Apc: %x. Timer: %x)\n", Apc
, Timer
);
134 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
136 /* Lock the Thread's Active Timer List*/
137 KeAcquireSpinLockAtDpcLevel(&CurrentThread
->ActiveTimerListLock
);
140 * Make sure that the Timer is still valid, and that it belongs to this thread
141 * Remove it if it's not periodic
143 if ((Timer
->ApcAssociated
) &&
144 (&CurrentThread
->Tcb
== Timer
->TimerApc
.Thread
) &&
147 /* Remove it from the Active Timers List */
148 DPRINT("Removing Timer\n");
149 RemoveEntryList(&Timer
->ActiveTimerListEntry
);
152 Timer
->ApcAssociated
= FALSE
;
154 /* Release spinlocks */
155 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
156 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
158 /* Dereference the Timer Object */
159 ObDereferenceObject(Timer
);
163 /* Release spinlocks */
164 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
165 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
170 ExpInitializeTimerImplementation(VOID
)
172 DPRINT("ExpInitializeTimerImplementation()\n");
174 /* Allocate Memory for the Timer */
175 ExTimerType
= ExAllocatePool(NonPagedPool
, sizeof(OBJECT_TYPE
));
177 /* Create the Executive Timer Object */
178 RtlInitUnicodeString(&ExTimerType
->TypeName
, L
"Timer");
179 ExTimerType
->Tag
= TAG('T', 'I', 'M', 'T');
180 ExTimerType
->PeakObjects
= 0;
181 ExTimerType
->PeakHandles
= 0;
182 ExTimerType
->TotalObjects
= 0;
183 ExTimerType
->TotalHandles
= 0;
184 ExTimerType
->PagedPoolCharge
= 0;
185 ExTimerType
->NonpagedPoolCharge
= sizeof(ETIMER
);
186 ExTimerType
->Mapping
= &ExpTimerMapping
;
187 ExTimerType
->Dump
= NULL
;
188 ExTimerType
->Open
= NULL
;
189 ExTimerType
->Close
= NULL
;
190 ExTimerType
->Delete
= ExpDeleteTimer
;
191 ExTimerType
->Parse
= NULL
;
192 ExTimerType
->Security
= NULL
;
193 ExTimerType
->QueryName
= NULL
;
194 ExTimerType
->OkayToClose
= NULL
;
195 ExTimerType
->Create
= NULL
;
196 ExTimerType
->DuplicationNotify
= NULL
;
197 ObpCreateTypeObject(ExTimerType
);
199 /* Initialize the Wait List and Lock */
200 KeInitializeSpinLock(&ExpWakeListLock
);
201 InitializeListHead(&ExpWakeList
);
207 NtCancelTimer(IN HANDLE TimerHandle
,
208 OUT PBOOLEAN CurrentState OPTIONAL
)
211 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
214 PETHREAD TimerThread
;
215 BOOLEAN KillTimer
= FALSE
;
216 NTSTATUS Status
= STATUS_SUCCESS
;
219 DPRINT("NtCancelTimer(0x%x, 0x%x)\n", TimerHandle
, CurrentState
);
221 /* Check Parameter Validity */
222 if(CurrentState
!= NULL
&& PreviousMode
!= KernelMode
) {
224 ProbeForWrite(CurrentState
,
228 Status
= _SEH_GetExceptionCode();
231 if(!NT_SUCCESS(Status
)) {
236 /* Get the Timer Object */
237 Status
= ObReferenceObjectByHandle(TimerHandle
,
244 /* Check for success */
245 if(NT_SUCCESS(Status
)) {
247 DPRINT("Timer Referencced: %x\n", Timer
);
250 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
252 /* Check if it's enabled */
253 if (Timer
->ApcAssociated
) {
256 * First, remove it from the Thread's Active List
259 TimerThread
= CONTAINING_RECORD(Timer
->TimerApc
.Thread
, ETHREAD
, Tcb
);
260 DPRINT("Removing from Thread: %x\n", TimerThread
);
262 /* Lock its active list */
263 KeAcquireSpinLockAtDpcLevel(&TimerThread
->ActiveTimerListLock
);
266 RemoveEntryList(&TimerThread
->ActiveTimerListHead
);
268 /* Unlock the list */
269 KeReleaseSpinLockFromDpcLevel(&TimerThread
->ActiveTimerListLock
);
271 /* Cancel the Timer */
272 KeCancelTimer(&Timer
->KeTimer
);
273 KeRemoveQueueDpc(&Timer
->TimerDpc
);
274 KeRemoveQueueApc(&Timer
->TimerApc
);
275 Timer
->ApcAssociated
= FALSE
;
280 /* If timer was disabled, we still need to cancel it */
281 DPRINT("APC was not Associated. Cancelling Timer\n");
282 KeCancelTimer(&Timer
->KeTimer
);
285 /* Read the old State */
286 State
= KeReadStateTimer(&Timer
->KeTimer
);
288 /* Dereference the Object */
289 ObDereferenceObject(Timer
);
291 /* Unlock the Timer */
292 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
294 /* Dereference if it was previously enabled */
295 if (KillTimer
) ObDereferenceObject(Timer
);
296 DPRINT1("Timer disabled\n");
298 /* Make sure it's safe to write to the handle */
299 if(CurrentState
!= NULL
) {
303 *CurrentState
= State
;
307 Status
= _SEH_GetExceptionCode();
313 /* Return to Caller */
320 NtCreateTimer(OUT PHANDLE TimerHandle
,
321 IN ACCESS_MASK DesiredAccess
,
322 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
323 IN TIMER_TYPE TimerType
)
327 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
328 NTSTATUS Status
= STATUS_SUCCESS
;
331 DPRINT("NtCreateTimer(Handle: %x, Type: %d)\n", TimerHandle
, TimerType
);
333 /* Check Parameter Validity */
334 if (PreviousMode
!= KernelMode
) {
338 ProbeForWrite(TimerHandle
,
343 Status
= _SEH_GetExceptionCode();
347 if(!NT_SUCCESS(Status
)) return Status
;
350 /* Create the Object */
351 Status
= ObCreateObject(PreviousMode
,
361 /* Check for Success */
362 if(NT_SUCCESS(Status
)) {
364 /* Initialize the Kernel Timer */
365 DPRINT("Initializing Timer: %x\n", Timer
);
366 KeInitializeTimerEx(&Timer
->KeTimer
, TimerType
);
368 /* Initialize the Timer Lock */
369 KeInitializeSpinLock(&Timer
->Lock
);
371 /* Initialize the DPC */
372 KeInitializeDpc(&Timer
->TimerDpc
, ExpTimerDpcRoutine
, Timer
);
374 /* Set Initial State */
375 Timer
->ApcAssociated
= FALSE
;
376 InitializeListHead(&Timer
->WakeTimerListEntry
);
377 Timer
->WakeTimer
= FALSE
;
379 /* Insert the Timer */
380 Status
= ObInsertObject((PVOID
)Timer
,
386 DPRINT("Timer Inserted\n");
389 /* Make sure it's safe to write to the handle */
392 *TimerHandle
= hTimer
;
396 Status
= _SEH_GetExceptionCode();
401 /* Return to Caller */
408 NtOpenTimer(OUT PHANDLE TimerHandle
,
409 IN ACCESS_MASK DesiredAccess
,
410 IN POBJECT_ATTRIBUTES ObjectAttributes
)
413 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
414 NTSTATUS Status
= STATUS_SUCCESS
;
417 DPRINT("NtOpenTimer(TimerHandle: %x)\n", TimerHandle
);
419 /* Check Parameter Validity */
420 if (PreviousMode
!= KernelMode
) {
424 ProbeForWrite(TimerHandle
,
430 Status
= _SEH_GetExceptionCode();
434 if(!NT_SUCCESS(Status
)) return Status
;
438 Status
= ObOpenObjectByName(ObjectAttributes
,
446 /* Check for success */
447 if(NT_SUCCESS(Status
)) {
449 /* Make sure it's safe to write to the handle */
452 *TimerHandle
= hTimer
;
456 Status
= _SEH_GetExceptionCode();
461 /* Return to Caller */
468 NtQueryTimer(IN HANDLE TimerHandle
,
469 IN TIMER_INFORMATION_CLASS TimerInformationClass
,
470 OUT PVOID TimerInformation
,
471 IN ULONG TimerInformationLength
,
472 OUT PULONG ReturnLength OPTIONAL
)
475 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
476 NTSTATUS Status
= STATUS_SUCCESS
;
477 PTIMER_BASIC_INFORMATION BasicInfo
= (PTIMER_BASIC_INFORMATION
)TimerInformation
;
480 DPRINT("NtQueryTimer(TimerHandle: %x, Class: %d)\n", TimerHandle
, TimerInformationClass
);
483 DefaultQueryInfoBufferCheck(TimerInformationClass
,
486 TimerInformationLength
,
490 if(!NT_SUCCESS(Status
)) {
492 DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status
);
496 /* Get the Timer Object */
497 Status
= ObReferenceObjectByHandle(TimerHandle
,
504 /* Check for Success */
505 if(NT_SUCCESS(Status
)) {
507 /* Return the Basic Information */
510 /* FIXME: Interrupt correction based on Interrupt Time */
511 DPRINT("Returning Information for Timer: %x. Time Remaining: %d\n", Timer
, Timer
->KeTimer
.DueTime
.QuadPart
);
512 BasicInfo
->TimeRemaining
.QuadPart
= Timer
->KeTimer
.DueTime
.QuadPart
;
513 BasicInfo
->SignalState
= KeReadStateTimer(&Timer
->KeTimer
);
515 if(ReturnLength
!= NULL
) *ReturnLength
= sizeof(TIMER_BASIC_INFORMATION
);
519 Status
= _SEH_GetExceptionCode();
523 /* Dereference Object */
524 ObDereferenceObject(Timer
);
533 NtSetTimer(IN HANDLE TimerHandle
,
534 IN PLARGE_INTEGER DueTime
,
535 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL
,
536 IN PVOID TimerContext OPTIONAL
,
537 IN BOOLEAN WakeTimer
,
538 IN LONG Period OPTIONAL
,
539 OUT PBOOLEAN PreviousState OPTIONAL
)
544 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
545 PETHREAD CurrentThread
= PsGetCurrentThread();
546 LARGE_INTEGER TimerDueTime
;
547 PETHREAD TimerThread
;
548 BOOLEAN KillTimer
= FALSE
;
549 NTSTATUS Status
= STATUS_SUCCESS
;
552 DPRINT("NtSetTimer(TimerHandle: %x, DueTime: %d, Apc: %x, Period: %d)\n", TimerHandle
, DueTime
->QuadPart
, TimerApcRoutine
, Period
);
554 /* Check Parameter Validity */
555 if (PreviousMode
!= KernelMode
) {
559 ProbeForRead(DueTime
,
560 sizeof(LARGE_INTEGER
),
562 TimerDueTime
= *DueTime
;
564 if(PreviousState
!= NULL
) {
566 ProbeForWrite(PreviousState
,
573 Status
= _SEH_GetExceptionCode();
577 if(!NT_SUCCESS(Status
)) return Status
;
580 /* Get the Timer Object */
581 Status
= ObReferenceObjectByHandle(TimerHandle
,
589 if (NT_SUCCESS(Status
)) {
592 DPRINT("Timer Referencced: %x\n", Timer
);
593 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
595 /* Cancel Running Timer */
596 if (Timer
->ApcAssociated
) {
599 * First, remove it from the Thread's Active List
602 TimerThread
= CONTAINING_RECORD(Timer
->TimerApc
.Thread
, ETHREAD
, Tcb
);
603 DPRINT("Thread already running. Removing from Thread: %x\n", TimerThread
);
605 /* Lock its active list */
606 KeAcquireSpinLockAtDpcLevel(&TimerThread
->ActiveTimerListLock
);
609 RemoveEntryList(&TimerThread
->ActiveTimerListHead
);
611 /* Unlock the list */
612 KeReleaseSpinLockFromDpcLevel(&TimerThread
->ActiveTimerListLock
);
614 /* Cancel the Timer */
615 KeCancelTimer(&Timer
->KeTimer
);
616 KeRemoveQueueDpc(&Timer
->TimerDpc
);
617 KeRemoveQueueApc(&Timer
->TimerApc
);
618 Timer
->ApcAssociated
= FALSE
;
623 /* If timer was disabled, we still need to cancel it */
624 DPRINT("No APCs. Simply cancelling\n");
625 KeCancelTimer(&Timer
->KeTimer
);
629 State
= KeReadStateTimer(&Timer
->KeTimer
);
631 /* Handle Wake Timers */
632 DPRINT("Doing Wake Semantics\n");
633 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock
);
636 /* Insert it into the list */
637 InsertTailList(&ExpWakeList
, &Timer
->WakeTimerListEntry
);
641 /* Remove it from the list */
642 RemoveEntryList(&Timer
->WakeTimerListEntry
);
643 Timer
->WakeTimerListEntry
.Flink
= NULL
;
645 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock
);
647 /* Set up the APC Routine if specified */
648 if (TimerApcRoutine
) {
650 /* Initialize the APC */
651 DPRINT("Initializing APC: %x\n", Timer
->TimerApc
);
652 KeInitializeApc(&Timer
->TimerApc
,
654 CurrentApcEnvironment
,
655 &ExpTimerApcKernelRoutine
,
656 (PKRUNDOWN_ROUTINE
)NULL
,
657 (PKNORMAL_ROUTINE
)TimerApcRoutine
,
661 /* Lock the Thread's Active List and Insert */
662 KeAcquireSpinLockAtDpcLevel(&CurrentThread
->ActiveTimerListLock
);
663 InsertTailList(&CurrentThread
->ActiveTimerListHead
,
664 &Timer
->ActiveTimerListEntry
);
665 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
669 /* Enable and Set the Timer */
670 DPRINT("Setting Kernel Timer\n");
671 KeSetTimerEx(&Timer
->KeTimer
,
674 TimerApcRoutine
? &Timer
->TimerDpc
: 0);
675 Timer
->ApcAssociated
= TimerApcRoutine
? TRUE
: FALSE
;
677 /* Unlock the Timer */
678 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
680 /* Dereference the Object */
681 ObDereferenceObject(Timer
);
683 /* Unlock the Timer */
684 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
686 /* Dereference if it was previously enabled */
687 if (!TimerApcRoutine
) ObDereferenceObject(Timer
);
688 if (KillTimer
) ObDereferenceObject(Timer
);
689 DPRINT("Finished Setting the Timer\n");
691 /* Make sure it's safe to write to the handle */
692 if(PreviousState
!= NULL
) {
696 *PreviousState
= State
;
700 Status
= _SEH_GetExceptionCode();
706 /* Return to Caller */