1 /* $Id: nttimer.c 12779 2005-01-04 04:45:00Z gdalsnes $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ex/timer.c
6 * PURPOSE: User-mode timers
8 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Reimplemented
9 * David Welch (welch@mcmail.com)
12 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 /* TYPES ********************************************************************/
19 /* Executive Timer Object */
20 typedef struct _ETIMER
{
24 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 *****************************************************************/
59 ExpDeleteTimer(PVOID ObjectBody
)
62 PETIMER Timer
= ObjectBody
;
64 DPRINT("ExpDeleteTimer(Timer: %x)\n", Timer
);
66 /* Lock the Wake List */
67 KeAcquireSpinLock(&ExpWakeListLock
, &OldIrql
);
69 /* Check if it has a Wait List */
70 if (!IsListEmpty(&Timer
->WakeTimerListEntry
)) {
72 /* Remove it from the Wait List */
73 DPRINT("Removing wake list\n");
74 RemoveEntryList(&Timer
->WakeTimerListEntry
);
77 /* Release the Wake List */
78 KeReleaseSpinLock(&ExpWakeListLock
, OldIrql
);
80 /* Tell the Kernel to cancel the Timer */
81 DPRINT("Cancelling Timer\n");
82 KeCancelTimer(&Timer
->KeTimer
);
87 ExpTimerDpcRoutine(PKDPC Dpc
,
88 PVOID DeferredContext
,
89 PVOID SystemArgument1
,
90 PVOID SystemArgument2
)
95 DPRINT("ExpTimerDpcRoutine(Dpc: %x)\n", Dpc
);
97 /* Get the Timer Object */
98 Timer
= (PETIMER
)DeferredContext
;
101 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
104 if(Timer
->ApcAssociated
) {
106 DPRINT("Queuing APC\n");
107 KeInsertQueueApc(&Timer
->TimerApc
,
113 /* Release the Timer */
114 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
120 ExpTimerApcKernelRoutine(PKAPC Apc
,
121 PKNORMAL_ROUTINE
* NormalRoutine
,
122 PVOID
* NormalContext
,
123 PVOID
* SystemArgument1
,
124 PVOID
* SystemArguemnt2
)
127 PETHREAD CurrentThread
= PsGetCurrentThread();
130 /* We need to find out which Timer we are */
131 Timer
= CONTAINING_RECORD(Apc
, ETIMER
, TimerApc
);
132 DPRINT("ExpTimerApcKernelRoutine(Apc: %x. Timer: %x)\n", Apc
, Timer
);
135 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
137 /* Lock the Thread's Active Timer List*/
138 KeAcquireSpinLockAtDpcLevel(&CurrentThread
->ActiveTimerListLock
);
141 * Make sure that the Timer is still valid, and that it belongs to this thread
142 * Remove it if it's not periodic
144 if ((Timer
->ApcAssociated
) &&
145 (&CurrentThread
->Tcb
== Timer
->TimerApc
.Thread
) &&
148 /* Remove it from the Active Timers List */
149 DPRINT("Removing Timer\n");
150 RemoveEntryList(&Timer
->ActiveTimerListEntry
);
153 Timer
->ApcAssociated
= FALSE
;
155 /* Release spinlocks */
156 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
157 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
159 /* Dereference the Timer Object */
160 ObDereferenceObject(Timer
);
164 /* Release spinlocks */
165 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
166 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
171 ExpInitializeTimerImplementation(VOID
)
173 DPRINT("ExpInitializeTimerImplementation()\n");
175 /* Allocate Memory for the Timer */
176 ExTimerType
= ExAllocatePool(NonPagedPool
, sizeof(OBJECT_TYPE
));
178 /* Create the Executive Timer Object */
179 RtlpCreateUnicodeString(&ExTimerType
->TypeName
, L
"Timer", NonPagedPool
);
180 ExTimerType
->Tag
= TAG('T', 'I', 'M', 'T');
181 ExTimerType
->PeakObjects
= 0;
182 ExTimerType
->PeakHandles
= 0;
183 ExTimerType
->TotalObjects
= 0;
184 ExTimerType
->TotalHandles
= 0;
185 ExTimerType
->PagedPoolCharge
= 0;
186 ExTimerType
->NonpagedPoolCharge
= sizeof(ETIMER
);
187 ExTimerType
->Mapping
= &ExpTimerMapping
;
188 ExTimerType
->Dump
= NULL
;
189 ExTimerType
->Open
= NULL
;
190 ExTimerType
->Close
= NULL
;
191 ExTimerType
->Delete
= ExpDeleteTimer
;
192 ExTimerType
->Parse
= NULL
;
193 ExTimerType
->Security
= NULL
;
194 ExTimerType
->QueryName
= NULL
;
195 ExTimerType
->OkayToClose
= NULL
;
196 ExTimerType
->Create
= NULL
;
197 ExTimerType
->DuplicationNotify
= NULL
;
198 ObpCreateTypeObject(ExTimerType
);
200 /* Initialize the Wait List and Lock */
201 KeInitializeSpinLock(&ExpWakeListLock
);
202 InitializeListHead(&ExpWakeList
);
208 NtCancelTimer(IN HANDLE TimerHandle
,
209 OUT PBOOLEAN CurrentState OPTIONAL
)
212 KPROCESSOR_MODE PreviousMode
;
215 PETHREAD TimerThread
;
216 BOOLEAN KillTimer
= FALSE
;
217 NTSTATUS Status
= STATUS_SUCCESS
;
221 PreviousMode
= ExGetPreviousMode();
223 DPRINT("NtCancelTimer(0x%x, 0x%x)\n", TimerHandle
, CurrentState
);
225 /* Check Parameter Validity */
226 if(CurrentState
!= NULL
&& PreviousMode
!= KernelMode
) {
228 ProbeForWrite(CurrentState
,
232 Status
= _SEH_GetExceptionCode();
235 if(!NT_SUCCESS(Status
)) {
240 /* Get the Timer Object */
241 Status
= ObReferenceObjectByHandle(TimerHandle
,
248 /* Check for success */
249 if(NT_SUCCESS(Status
)) {
251 DPRINT("Timer Referencced: %x\n", Timer
);
254 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
256 /* Check if it's enabled */
257 if (Timer
->ApcAssociated
) {
260 * First, remove it from the Thread's Active List
263 TimerThread
= CONTAINING_RECORD(Timer
->TimerApc
.Thread
, ETHREAD
, Tcb
);
264 DPRINT("Removing from Thread: %x\n", TimerThread
);
266 /* Lock its active list */
267 KeAcquireSpinLockAtDpcLevel(&TimerThread
->ActiveTimerListLock
);
270 RemoveEntryList(&TimerThread
->ActiveTimerListHead
);
272 /* Unlock the list */
273 KeReleaseSpinLockFromDpcLevel(&TimerThread
->ActiveTimerListLock
);
275 /* Cancel the Timer */
276 KeCancelTimer(&Timer
->KeTimer
);
277 KeRemoveQueueDpc(&Timer
->TimerDpc
);
278 KeRemoveQueueApc(&Timer
->TimerApc
);
279 Timer
->ApcAssociated
= FALSE
;
284 /* If timer was disabled, we still need to cancel it */
285 DPRINT("APC was not Associated. Cancelling Timer\n");
286 KeCancelTimer(&Timer
->KeTimer
);
289 /* Read the old State */
290 State
= KeReadStateTimer(&Timer
->KeTimer
);
292 /* Dereference the Object */
293 ObDereferenceObject(Timer
);
295 /* Unlock the Timer */
296 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
298 /* Dereference if it was previously enabled */
299 if (KillTimer
) ObDereferenceObject(Timer
);
300 DPRINT1("Timer disabled\n");
302 /* Make sure it's safe to write to the handle */
303 if(CurrentState
!= NULL
) {
305 *CurrentState
= State
;
307 Status
= _SEH_GetExceptionCode();
312 /* Return to Caller */
319 NtCreateTimer(OUT PHANDLE TimerHandle
,
320 IN ACCESS_MASK DesiredAccess
,
321 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
322 IN TIMER_TYPE TimerType
)
326 KPROCESSOR_MODE PreviousMode
;
327 NTSTATUS Status
= STATUS_SUCCESS
;
331 PreviousMode
= ExGetPreviousMode();
333 DPRINT("NtCreateTimer(Handle: %x, Type: %d)\n", TimerHandle
, TimerType
);
335 /* Check Parameter Validity */
336 if (PreviousMode
!= KernelMode
) {
338 ProbeForWrite(TimerHandle
,
342 Status
= _SEH_GetExceptionCode();
345 if(!NT_SUCCESS(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 */
391 *TimerHandle
= hTimer
;
393 Status
= _SEH_GetExceptionCode();
397 /* Return to Caller */
404 NtOpenTimer(OUT PHANDLE TimerHandle
,
405 IN ACCESS_MASK DesiredAccess
,
406 IN POBJECT_ATTRIBUTES ObjectAttributes
)
409 KPROCESSOR_MODE PreviousMode
;
410 NTSTATUS Status
= STATUS_SUCCESS
;
414 PreviousMode
= ExGetPreviousMode();
416 DPRINT("NtOpenTimer(TimerHandle: %x)\n", TimerHandle
);
418 /* Check Parameter Validity */
419 if (PreviousMode
!= KernelMode
) {
421 ProbeForWrite(TimerHandle
,
425 Status
= _SEH_GetExceptionCode();
428 if(!NT_SUCCESS(Status
)) {
434 Status
= ObOpenObjectByName(ObjectAttributes
,
442 /* Check for success */
443 if(NT_SUCCESS(Status
)) {
445 /* Make sure it's safe to write to the handle */
447 *TimerHandle
= hTimer
;
449 Status
= _SEH_GetExceptionCode();
453 /* Return to Caller */
460 NtQueryTimer(IN HANDLE TimerHandle
,
461 IN TIMER_INFORMATION_CLASS TimerInformationClass
,
462 OUT PVOID TimerInformation
,
463 IN ULONG TimerInformationLength
,
464 OUT PULONG ReturnLength OPTIONAL
)
467 KPROCESSOR_MODE PreviousMode
;
468 NTSTATUS Status
= STATUS_SUCCESS
;
469 PTIMER_BASIC_INFORMATION BasicInfo
= (PTIMER_BASIC_INFORMATION
)TimerInformation
;
473 PreviousMode
= ExGetPreviousMode();
475 DPRINT("NtQueryTimer(TimerHandle: %x, Class: %d)\n", TimerHandle
, TimerInformationClass
);
478 DefaultQueryInfoBufferCheck(TimerInformationClass
,
481 TimerInformationLength
,
485 if(!NT_SUCCESS(Status
)) {
487 DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status
);
491 /* Get the Timer Object */
492 Status
= ObReferenceObjectByHandle(TimerHandle
,
499 /* Check for Success */
500 if(NT_SUCCESS(Status
)) {
502 /* Return the Basic Information */
505 /* FIXME: Interrupt correction based on Interrupt Time */
506 DPRINT("Returning Information for Timer: %x. Time Remaining: %d\n", Timer
, Timer
->KeTimer
.DueTime
.QuadPart
);
507 BasicInfo
->TimeRemaining
.QuadPart
= Timer
->KeTimer
.DueTime
.QuadPart
;
508 BasicInfo
->SignalState
= KeReadStateTimer(&Timer
->KeTimer
);
510 if(ReturnLength
!= NULL
) *ReturnLength
= sizeof(TIMER_BASIC_INFORMATION
);
514 Status
= _SEH_GetExceptionCode();
517 /* Dereference Object */
518 ObDereferenceObject(Timer
);
527 NtSetTimer(IN HANDLE TimerHandle
,
528 IN PLARGE_INTEGER DueTime
,
529 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL
,
530 IN PVOID TimerContext OPTIONAL
,
531 IN BOOLEAN WakeTimer
,
532 IN LONG Period OPTIONAL
,
533 OUT PBOOLEAN PreviousState OPTIONAL
)
538 KPROCESSOR_MODE PreviousMode
;
539 PETHREAD CurrentThread
;
540 LARGE_INTEGER TimerDueTime
;
541 PETHREAD TimerThread
;
542 BOOLEAN KillTimer
= FALSE
;
543 NTSTATUS Status
= STATUS_SUCCESS
;
547 PreviousMode
= ExGetPreviousMode();
548 CurrentThread
= PsGetCurrentThread();
550 DPRINT("NtSetTimer(TimerHandle: %x, DueTime: %d, Apc: %x, Period: %d)\n", TimerHandle
, DueTime
->QuadPart
, TimerApcRoutine
, Period
);
552 /* Check Parameter Validity */
553 if (PreviousMode
!= KernelMode
) {
555 ProbeForRead(DueTime
,
556 sizeof(LARGE_INTEGER
),
558 TimerDueTime
= *DueTime
;
560 if(PreviousState
!= NULL
) {
561 ProbeForWrite(PreviousState
,
567 Status
= _SEH_GetExceptionCode();
570 if(!NT_SUCCESS(Status
)) {
575 /* Get the Timer Object */
576 Status
= ObReferenceObjectByHandle(TimerHandle
,
584 if (NT_SUCCESS(Status
)) {
587 DPRINT("Timer Referencced: %x\n", Timer
);
588 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
590 /* Cancel Running Timer */
591 if (Timer
->ApcAssociated
) {
594 * First, remove it from the Thread's Active List
597 TimerThread
= CONTAINING_RECORD(Timer
->TimerApc
.Thread
, ETHREAD
, Tcb
);
598 DPRINT("Thread already running. Removing from Thread: %x\n", TimerThread
);
600 /* Lock its active list */
601 KeAcquireSpinLockAtDpcLevel(&TimerThread
->ActiveTimerListLock
);
604 RemoveEntryList(&TimerThread
->ActiveTimerListHead
);
606 /* Unlock the list */
607 KeReleaseSpinLockFromDpcLevel(&TimerThread
->ActiveTimerListLock
);
609 /* Cancel the Timer */
610 KeCancelTimer(&Timer
->KeTimer
);
611 KeRemoveQueueDpc(&Timer
->TimerDpc
);
612 KeRemoveQueueApc(&Timer
->TimerApc
);
613 Timer
->ApcAssociated
= FALSE
;
618 /* If timer was disabled, we still need to cancel it */
619 DPRINT("No APCs. Simply cancelling\n");
620 KeCancelTimer(&Timer
->KeTimer
);
624 State
= KeReadStateTimer(&Timer
->KeTimer
);
626 /* Handle Wake Timers */
627 DPRINT("Doing Wake Semantics\n");
628 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock
);
631 /* Insert it into the list */
632 InsertTailList(&ExpWakeList
, &Timer
->WakeTimerListEntry
);
636 /* Remove it from the list */
637 RemoveEntryList(&Timer
->WakeTimerListEntry
);
638 Timer
->WakeTimerListEntry
.Flink
= NULL
;
640 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock
);
642 /* Set up the APC Routine if specified */
643 if (TimerApcRoutine
) {
645 /* Initialize the APC */
646 DPRINT("Initializing APC: %x\n", Timer
->TimerApc
);
647 KeInitializeApc(&Timer
->TimerApc
,
649 CurrentApcEnvironment
,
650 &ExpTimerApcKernelRoutine
,
651 (PKRUNDOWN_ROUTINE
)NULL
,
652 (PKNORMAL_ROUTINE
)TimerApcRoutine
,
656 /* Lock the Thread's Active List and Insert */
657 KeAcquireSpinLockAtDpcLevel(&CurrentThread
->ActiveTimerListLock
);
658 InsertTailList(&CurrentThread
->ActiveTimerListHead
,
659 &Timer
->ActiveTimerListEntry
);
660 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
664 /* Enable and Set the Timer */
665 DPRINT("Setting Kernel Timer\n");
666 KeSetTimerEx(&Timer
->KeTimer
,
669 TimerApcRoutine
? &Timer
->TimerDpc
: 0);
670 Timer
->ApcAssociated
= TimerApcRoutine
? TRUE
: FALSE
;
672 /* Unlock the Timer */
673 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
675 /* Dereference the Object */
676 ObDereferenceObject(Timer
);
678 /* Unlock the Timer */
679 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
681 /* Dereference if it was previously enabled */
682 if (!TimerApcRoutine
) ObDereferenceObject(Timer
);
683 if (KillTimer
) ObDereferenceObject(Timer
);
684 DPRINT("Finished Setting the Timer\n");
686 /* Make sure it's safe to write to the handle */
687 if(PreviousState
!= NULL
) {
689 *PreviousState
= State
;
691 Status
= _SEH_GetExceptionCode();
696 /* Return to Caller */