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
= ExGetPreviousMode();
213 NTSTATUS Status
= STATUS_SUCCESS
;
216 BOOLEAN KillTimer
= FALSE
;
217 PETHREAD TimerThread
;
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
) {
301 *CurrentState
= State
;
303 Status
= _SEH_GetExceptionCode();
308 /* Return to Caller */
315 NtCreateTimer(OUT PHANDLE TimerHandle
,
316 IN ACCESS_MASK DesiredAccess
,
317 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
318 IN TIMER_TYPE TimerType
)
322 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
323 NTSTATUS Status
= STATUS_SUCCESS
;
325 DPRINT("NtCreateTimer(Handle: %x, Type: %d)\n", TimerHandle
, TimerType
);
327 /* Check Parameter Validity */
328 if (PreviousMode
!= KernelMode
) {
330 ProbeForWrite(TimerHandle
,
334 Status
= _SEH_GetExceptionCode();
337 if(!NT_SUCCESS(Status
)) {
342 /* Create the Object */
343 Status
= ObCreateObject(PreviousMode
,
353 /* Check for Success */
354 if(NT_SUCCESS(Status
)) {
356 /* Initialize the Kernel Timer */
357 DPRINT("Initializing Timer: %x\n", Timer
);
358 KeInitializeTimerEx(&Timer
->KeTimer
, TimerType
);
360 /* Initialize the Timer Lock */
361 KeInitializeSpinLock(&Timer
->Lock
);
363 /* Initialize the DPC */
364 KeInitializeDpc(&Timer
->TimerDpc
, ExpTimerDpcRoutine
, Timer
);
366 /* Set Initial State */
367 Timer
->ApcAssociated
= FALSE
;
368 InitializeListHead(&Timer
->WakeTimerListEntry
);
369 Timer
->WakeTimer
= FALSE
;
371 /* Insert the Timer */
372 Status
= ObInsertObject((PVOID
)Timer
,
378 DPRINT("Timer Inserted\n");
381 /* Make sure it's safe to write to the handle */
383 *TimerHandle
= hTimer
;
385 Status
= _SEH_GetExceptionCode();
389 /* Return to Caller */
396 NtOpenTimer(OUT PHANDLE TimerHandle
,
397 IN ACCESS_MASK DesiredAccess
,
398 IN POBJECT_ATTRIBUTES ObjectAttributes
)
401 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
402 NTSTATUS Status
= STATUS_SUCCESS
;
404 DPRINT("NtOpenTimer(TimerHandle: %x)\n", TimerHandle
);
406 /* Check Parameter Validity */
407 if (PreviousMode
!= KernelMode
) {
409 ProbeForWrite(TimerHandle
,
413 Status
= _SEH_GetExceptionCode();
416 if(!NT_SUCCESS(Status
)) {
422 Status
= ObOpenObjectByName(ObjectAttributes
,
430 /* Check for success */
431 if(NT_SUCCESS(Status
)) {
433 /* Make sure it's safe to write to the handle */
435 *TimerHandle
= hTimer
;
437 Status
= _SEH_GetExceptionCode();
441 /* Return to Caller */
448 NtQueryTimer(IN HANDLE TimerHandle
,
449 IN TIMER_INFORMATION_CLASS TimerInformationClass
,
450 OUT PVOID TimerInformation
,
451 IN ULONG TimerInformationLength
,
452 OUT PULONG ReturnLength OPTIONAL
)
455 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
456 NTSTATUS Status
= STATUS_SUCCESS
;
457 PTIMER_BASIC_INFORMATION BasicInfo
= (PTIMER_BASIC_INFORMATION
)TimerInformation
;
459 DPRINT("NtQueryTimer(TimerHandle: %x, Class: %d)\n", TimerHandle
, TimerInformationClass
);
462 DefaultQueryInfoBufferCheck(TimerInformationClass
,
465 TimerInformationLength
,
469 if(!NT_SUCCESS(Status
)) {
471 DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status
);
475 /* Get the Timer Object */
476 Status
= ObReferenceObjectByHandle(TimerHandle
,
483 /* Check for Success */
484 if(NT_SUCCESS(Status
)) {
486 /* Return the Basic Information */
489 /* FIXME: Interrupt correction based on Interrupt Time */
490 DPRINT("Returning Information for Timer: %x. Time Remaining: %d\n", Timer
, Timer
->KeTimer
.DueTime
.QuadPart
);
491 BasicInfo
->TimeRemaining
.QuadPart
= Timer
->KeTimer
.DueTime
.QuadPart
;
492 BasicInfo
->SignalState
= KeReadStateTimer(&Timer
->KeTimer
);
493 ObDereferenceObject(Timer
);
495 if(ReturnLength
!= NULL
) {
496 *ReturnLength
= sizeof(TIMER_BASIC_INFORMATION
);
500 Status
= _SEH_GetExceptionCode();
510 NtSetTimer(IN HANDLE TimerHandle
,
511 IN PLARGE_INTEGER DueTime
,
512 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL
,
513 IN PVOID TimerContext OPTIONAL
,
514 IN BOOLEAN WakeTimer
,
515 IN LONG Period OPTIONAL
,
516 OUT PBOOLEAN PreviousState OPTIONAL
)
522 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
523 PETHREAD CurrentThread
= PsGetCurrentThread();
524 NTSTATUS Status
= STATUS_SUCCESS
;
525 LARGE_INTEGER TimerDueTime
;
526 PETHREAD TimerThread
;
528 DPRINT("NtSetTimer(TimerHandle: %x, DueTime: %d, Apc: %x, Period: %d)\n", TimerHandle
, DueTime
->QuadPart
, TimerApcRoutine
, Period
);
530 /* Check Parameter Validity */
531 if (PreviousMode
!= KernelMode
) {
533 ProbeForRead(DueTime
,
534 sizeof(LARGE_INTEGER
),
536 TimerDueTime
= *DueTime
;
538 if(PreviousState
!= NULL
) {
539 ProbeForWrite(PreviousState
,
545 Status
= _SEH_GetExceptionCode();
548 if(!NT_SUCCESS(Status
)) {
553 /* Get the Timer Object */
554 Status
= ObReferenceObjectByHandle(TimerHandle
,
562 if (NT_SUCCESS(Status
)) {
565 DPRINT("Timer Referencced: %x\n", Timer
);
566 KeAcquireSpinLock(&Timer
->Lock
, &OldIrql
);
568 /* Cancel Running Timer */
569 if (Timer
->ApcAssociated
) {
572 * First, remove it from the Thread's Active List
575 TimerThread
= CONTAINING_RECORD(Timer
->TimerApc
.Thread
, ETHREAD
, Tcb
);
576 DPRINT("Thread already running. Removing from Thread: %x\n", TimerThread
);
578 /* Lock its active list */
579 KeAcquireSpinLockAtDpcLevel(&TimerThread
->ActiveTimerListLock
);
582 RemoveEntryList(&TimerThread
->ActiveTimerListHead
);
584 /* Unlock the list */
585 KeReleaseSpinLockFromDpcLevel(&TimerThread
->ActiveTimerListLock
);
587 /* Cancel the Timer */
588 KeCancelTimer(&Timer
->KeTimer
);
589 KeRemoveQueueDpc(&Timer
->TimerDpc
);
590 KeRemoveQueueApc(&Timer
->TimerApc
);
591 Timer
->ApcAssociated
= FALSE
;
596 /* If timer was disabled, we still need to cancel it */
597 DPRINT("No APCs. Simply cancelling\n");
598 KeCancelTimer(&Timer
->KeTimer
);
602 State
= KeReadStateTimer(&Timer
->KeTimer
);
604 /* Handle Wake Timers */
605 DPRINT("Doing Wake Semantics\n");
606 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock
);
609 /* Insert it into the list */
610 InsertTailList(&ExpWakeList
, &Timer
->WakeTimerListEntry
);
614 /* Remove it from the list */
615 RemoveEntryList(&Timer
->WakeTimerListEntry
);
616 Timer
->WakeTimerListEntry
.Flink
= NULL
;
618 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock
);
620 /* Set up the APC Routine if specified */
621 if (TimerApcRoutine
) {
623 /* Initialize the APC */
624 DPRINT("Initializing APC: %x\n", Timer
->TimerApc
);
625 KeInitializeApc(&Timer
->TimerApc
,
627 CurrentApcEnvironment
,
628 &ExpTimerApcKernelRoutine
,
629 (PKRUNDOWN_ROUTINE
)NULL
,
630 (PKNORMAL_ROUTINE
)TimerApcRoutine
,
634 /* Lock the Thread's Active List and Insert */
635 KeAcquireSpinLockAtDpcLevel(&CurrentThread
->ActiveTimerListLock
);
636 InsertTailList(&CurrentThread
->ActiveTimerListHead
,
637 &Timer
->ActiveTimerListEntry
);
638 KeReleaseSpinLockFromDpcLevel(&CurrentThread
->ActiveTimerListLock
);
642 /* Enable and Set the Timer */
643 DPRINT("Setting Kernel Timer\n");
644 KeSetTimerEx(&Timer
->KeTimer
,
647 TimerApcRoutine
? &Timer
->TimerDpc
: 0);
648 Timer
->ApcAssociated
= TimerApcRoutine
? TRUE
: FALSE
;
650 /* Unlock the Timer */
651 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
653 /* Dereference the Object */
654 ObDereferenceObject(Timer
);
656 /* Unlock the Timer */
657 KeReleaseSpinLock(&Timer
->Lock
, OldIrql
);
659 /* Dereference if it was previously enabled */
660 if (!TimerApcRoutine
) ObDereferenceObject(Timer
);
661 if (KillTimer
) ObDereferenceObject(Timer
);
662 DPRINT("Finished Setting the Timer\n");
664 /* Make sure it's safe to write to the handle */
665 if(PreviousState
!= NULL
) {
667 *PreviousState
= State
;
669 Status
= _SEH_GetExceptionCode();
674 /* Return to Caller */