9226ea7505490c98532f65426b866c844f8e2923
[reactos.git] / reactos / ntoskrnl / ex / timer.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/timer.c
5 * PURPOSE: Executive Timer Implementation
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 /* Timer Object Type */
18 POBJECT_TYPE ExTimerType = NULL;
19
20 KSPIN_LOCK ExpWakeListLock;
21 LIST_ENTRY ExpWakeList;
22
23 /* Timer Mapping */
24 static GENERIC_MAPPING ExpTimerMapping =
25 {
26 STANDARD_RIGHTS_READ | TIMER_QUERY_STATE,
27 STANDARD_RIGHTS_WRITE | TIMER_MODIFY_STATE,
28 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
29 TIMER_ALL_ACCESS
30 };
31
32 /* Timer Information Classes */
33 static const INFORMATION_CLASS_INFO ExTimerInfoClass[] =
34 {
35 /* TimerBasicInformation */
36 ICI_SQ_SAME(sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY),
37 };
38
39 /* PRIVATE FUNCTIONS *********************************************************/
40
41 VOID
42 NTAPI
43 ExTimerRundown(VOID)
44 {
45 PETHREAD Thread = PsGetCurrentThread();
46 KIRQL OldIrql;
47 PLIST_ENTRY CurrentEntry;
48 PETIMER Timer;
49 ULONG DerefsToDo;
50
51 /* Lock the Thread's Active Timer List and loop it */
52 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql);
53 CurrentEntry = Thread->ActiveTimerListHead.Flink;
54 while (CurrentEntry != &Thread->ActiveTimerListHead)
55 {
56 /* Get the timer */
57 Timer = CONTAINING_RECORD(CurrentEntry, ETIMER, ActiveTimerListEntry);
58
59 /* Reference it */
60 ObReferenceObject(Timer);
61 DerefsToDo = 1;
62
63 /* Unlock the list */
64 KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql);
65
66 /* Lock the Timer */
67 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
68
69 /* Lock the list again */
70 KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
71
72 /* Make sure that the timer is valid */
73 if ((Timer->ApcAssociated) && (&Thread->Tcb == Timer->TimerApc.Thread))
74 {
75 /* Remove it from the list */
76 RemoveEntryList(&Timer->ActiveTimerListEntry);
77 Timer->ApcAssociated = FALSE;
78
79 /* Cancel the timer and remove its DPC and APC */
80 KeCancelTimer(&Timer->KeTimer);
81 KeRemoveQueueDpc(&Timer->TimerDpc);
82 if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
83
84 /* Add another dereference to do */
85 DerefsToDo++;
86 }
87
88 /* Unlock the list */
89 KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
90
91 /* Unlock the Timer */
92 KeReleaseSpinLock(&Timer->Lock, OldIrql);
93
94 /* Dereference it */
95 ObDereferenceObjectEx(Timer, DerefsToDo);
96
97 /* Loop again */
98 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql);
99 CurrentEntry = Thread->ActiveTimerListHead.Flink;
100 }
101
102 /* Release lock and return */
103 KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql);
104 }
105
106 VOID
107 NTAPI
108 ExpDeleteTimer(IN PVOID ObjectBody)
109 {
110 KIRQL OldIrql;
111 PETIMER Timer = ObjectBody;
112
113 /* Check if it has a Wait List */
114 if (Timer->WakeTimerListEntry.Flink)
115 {
116 /* Lock the Wake List */
117 KeAcquireSpinLock(&ExpWakeListLock, &OldIrql);
118
119 /* Check again, since it might've changed before we locked */
120 if (Timer->WakeTimerListEntry.Flink)
121 {
122 /* Remove it from the Wait List */
123 RemoveEntryList(&Timer->WakeTimerListEntry);
124 Timer->WakeTimerListEntry.Flink = NULL;
125 }
126
127 /* Release the Wake List */
128 KeReleaseSpinLock(&ExpWakeListLock, OldIrql);
129 }
130
131 /* Tell the Kernel to cancel the Timer and flush all queued DPCs */
132 KeCancelTimer(&Timer->KeTimer);
133 KeFlushQueuedDpcs();
134 }
135
136 VOID
137 NTAPI
138 ExpTimerDpcRoutine(IN PKDPC Dpc,
139 IN PVOID DeferredContext,
140 IN PVOID SystemArgument1,
141 IN PVOID SystemArgument2)
142 {
143 PETIMER Timer = DeferredContext;
144 BOOLEAN Inserted = FALSE;
145
146 /* Reference the timer */
147 if (!ObReferenceObjectSafe(Timer)) return;
148
149 /* Lock the Timer */
150 KeAcquireSpinLockAtDpcLevel(&Timer->Lock);
151
152 /* Check if the timer is associated */
153 if (Timer->ApcAssociated)
154 {
155 /* Queue the APC */
156 Inserted = KeInsertQueueApc(&Timer->TimerApc,
157 SystemArgument1,
158 SystemArgument2,
159 IO_NO_INCREMENT);
160 }
161
162 /* Release the Timer */
163 KeReleaseSpinLockFromDpcLevel(&Timer->Lock);
164
165 /* Dereference it if we couldn't queue the APC */
166 if (!Inserted) ObDereferenceObject(Timer);
167 }
168
169 VOID
170 NTAPI
171 ExpTimerApcKernelRoutine(IN PKAPC Apc,
172 IN OUT PKNORMAL_ROUTINE* NormalRoutine,
173 IN OUT PVOID* NormalContext,
174 IN OUT PVOID* SystemArgument1,
175 IN OUT PVOID* SystemArguemnt2)
176 {
177 PETIMER Timer;
178 KIRQL OldIrql;
179 ULONG DerefsToDo = 1;
180 PETHREAD Thread = PsGetCurrentThread();
181
182 /* We need to find out which Timer we are */
183 Timer = CONTAINING_RECORD(Apc, ETIMER, TimerApc);
184
185 /* Lock the Timer */
186 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
187
188 /* Lock the Thread's Active Timer List*/
189 KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
190
191 /* Make sure that the Timer is valid, and that it belongs to this thread */
192 if ((Timer->ApcAssociated) && (&Thread->Tcb == Timer->TimerApc.Thread))
193 {
194 /* Check if it's not periodic */
195 if (!Timer->Period)
196 {
197 /* Remove it from the Active Timers List */
198 RemoveEntryList(&Timer->ActiveTimerListEntry);
199
200 /* Disable it */
201 Timer->ApcAssociated = FALSE;
202 DerefsToDo++;
203 }
204 }
205 else
206 {
207 /* Clear the normal routine */
208 *NormalRoutine = NULL;
209 }
210
211 /* Release locks */
212 KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
213 KeReleaseSpinLock(&Timer->Lock, OldIrql);
214
215 /* Dereference as needed */
216 ObDereferenceObjectEx(Timer, DerefsToDo);
217 }
218
219 BOOLEAN
220 INIT_FUNCTION
221 NTAPI
222 ExpInitializeTimerImplementation(VOID)
223 {
224 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
225 UNICODE_STRING Name;
226 NTSTATUS Status;
227
228 /* Create the Timer Object Type */
229 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
230 RtlInitUnicodeString(&Name, L"Timer");
231 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
232 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
233 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER);
234 ObjectTypeInitializer.GenericMapping = ExpTimerMapping;
235 ObjectTypeInitializer.PoolType = NonPagedPool;
236 ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS;
237 ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer;
238 Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExTimerType);
239 if (!NT_SUCCESS(Status)) return FALSE;
240
241 /* Initialize the Wait List and Lock */
242 KeInitializeSpinLock(&ExpWakeListLock);
243 InitializeListHead(&ExpWakeList);
244 return TRUE;
245 }
246
247 /* PUBLIC FUNCTIONS **********************************************************/
248
249 NTSTATUS
250 NTAPI
251 NtCancelTimer(IN HANDLE TimerHandle,
252 OUT PBOOLEAN CurrentState OPTIONAL)
253 {
254 PETIMER Timer;
255 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
256 BOOLEAN State;
257 KIRQL OldIrql;
258 PETHREAD TimerThread;
259 ULONG DerefsToDo = 1;
260 NTSTATUS Status;
261 PAGED_CODE();
262
263 /* Check if we need to probe */
264 if ((CurrentState) && (PreviousMode != KernelMode))
265 {
266 _SEH2_TRY
267 {
268 /* Make sure the pointer is valid */
269 ProbeForWriteBoolean(CurrentState);
270 }
271 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
272 {
273 /* Return the exception code */
274 _SEH2_YIELD(return _SEH2_GetExceptionCode());
275 }
276 _SEH2_END;
277 }
278
279 /* Get the Timer Object */
280 Status = ObReferenceObjectByHandle(TimerHandle,
281 TIMER_MODIFY_STATE,
282 ExTimerType,
283 PreviousMode,
284 (PVOID*)&Timer,
285 NULL);
286 if (NT_SUCCESS(Status))
287 {
288 /* Lock the Timer */
289 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
290
291 /* Check if it's enabled */
292 if (Timer->ApcAssociated)
293 {
294 /* Get the Thread. */
295 TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread,
296 ETHREAD,
297 Tcb);
298
299 /* Lock its active list */
300 KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock);
301
302 /* Remove it */
303 RemoveEntryList(&TimerThread->ActiveTimerListHead);
304 Timer->ApcAssociated = FALSE;
305
306 /* Unlock the list */
307 KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock);
308
309 /* Cancel the Timer */
310 KeCancelTimer(&Timer->KeTimer);
311 KeRemoveQueueDpc(&Timer->TimerDpc);
312 if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
313 DerefsToDo++;
314 }
315 else
316 {
317 /* If timer was disabled, we still need to cancel it */
318 KeCancelTimer(&Timer->KeTimer);
319 }
320
321 /* Handle a Wake Timer */
322 if (Timer->WakeTimerListEntry.Flink)
323 {
324 /* Lock the Wake List */
325 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock);
326
327 /* Check again, since it might've changed before we locked */
328 if (Timer->WakeTimerListEntry.Flink)
329 {
330 /* Remove it from the Wait List */
331 RemoveEntryList(&Timer->WakeTimerListEntry);
332 Timer->WakeTimerListEntry.Flink = NULL;
333 }
334
335 /* Release the Wake List */
336 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock);
337 }
338
339 /* Unlock the Timer */
340 KeReleaseSpinLock(&Timer->Lock, OldIrql);
341
342 /* Read the old State */
343 State = KeReadStateTimer(&Timer->KeTimer);
344
345 /* Dereference the Object */
346 ObDereferenceObjectEx(Timer, DerefsToDo);
347
348 /* Check if caller wants the state */
349 if (CurrentState)
350 {
351 _SEH2_TRY
352 {
353 /* Return the Timer State */
354 *CurrentState = State;
355 }
356 _SEH2_EXCEPT(ExSystemExceptionFilter())
357 {
358
359 }
360 _SEH2_END;
361 }
362 }
363
364 /* Return to Caller */
365 return Status;
366 }
367
368 NTSTATUS
369 NTAPI
370 NtCreateTimer(OUT PHANDLE TimerHandle,
371 IN ACCESS_MASK DesiredAccess,
372 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
373 IN TIMER_TYPE TimerType)
374 {
375 PETIMER Timer;
376 HANDLE hTimer;
377 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
378 NTSTATUS Status;
379 PAGED_CODE();
380
381 /* Check for correct timer type */
382 if ((TimerType != NotificationTimer) &&
383 (TimerType != SynchronizationTimer))
384 {
385 /* Fail */
386 return STATUS_INVALID_PARAMETER_4;
387 }
388
389 /* Check if we need to probe */
390 if (PreviousMode != KernelMode)
391 {
392 _SEH2_TRY
393 {
394 /* Make sure the pointer is valid */
395 ProbeForWriteHandle(TimerHandle);
396 }
397 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
398 {
399 /* Return the exception code */
400 _SEH2_YIELD(return _SEH2_GetExceptionCode());
401 }
402 _SEH2_END;
403 }
404
405 /* Create the Object */
406 Status = ObCreateObject(PreviousMode,
407 ExTimerType,
408 ObjectAttributes,
409 PreviousMode,
410 NULL,
411 sizeof(ETIMER),
412 0,
413 0,
414 (PVOID*)&Timer);
415 if (NT_SUCCESS(Status))
416 {
417 /* Initialize the DPC */
418 KeInitializeDpc(&Timer->TimerDpc, ExpTimerDpcRoutine, Timer);
419
420 /* Initialize the Kernel Timer */
421 KeInitializeTimerEx(&Timer->KeTimer, TimerType);
422
423 /* Initialize the timer fields */
424 KeInitializeSpinLock(&Timer->Lock);
425 Timer->ApcAssociated = FALSE;
426 Timer->WakeTimer = FALSE;
427 Timer->WakeTimerListEntry.Flink = NULL;
428
429 /* Insert the Timer */
430 Status = ObInsertObject((PVOID)Timer,
431 NULL,
432 DesiredAccess,
433 0,
434 NULL,
435 &hTimer);
436
437 /* Check for success */
438 if (NT_SUCCESS(Status))
439 {
440 /* Enter SEH */
441 _SEH2_TRY
442 {
443 /* Return the Timer Handle */
444 *TimerHandle = hTimer;
445 }
446 _SEH2_EXCEPT(ExSystemExceptionFilter())
447 {
448
449 }
450 _SEH2_END;
451 }
452 }
453
454 /* Return to Caller */
455 return Status;
456 }
457
458 NTSTATUS
459 NTAPI
460 NtOpenTimer(OUT PHANDLE TimerHandle,
461 IN ACCESS_MASK DesiredAccess,
462 IN POBJECT_ATTRIBUTES ObjectAttributes)
463 {
464 HANDLE hTimer;
465 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
466 NTSTATUS Status;
467 PAGED_CODE();
468
469 /* Check Parameter Validity */
470 if (PreviousMode != KernelMode)
471 {
472 _SEH2_TRY
473 {
474 /* Make sure the pointer is valid */
475 ProbeForWriteHandle(TimerHandle);
476 }
477 _SEH2_EXCEPT(ExSystemExceptionFilter())
478 {
479 /* Return the exception code */
480 _SEH2_YIELD(return _SEH2_GetExceptionCode());
481 }
482 _SEH2_END;
483 }
484
485 /* Open the Timer */
486 Status = ObOpenObjectByName(ObjectAttributes,
487 ExTimerType,
488 PreviousMode,
489 NULL,
490 DesiredAccess,
491 NULL,
492 &hTimer);
493 if (NT_SUCCESS(Status))
494 {
495 /* Enter SEH */
496 _SEH2_TRY
497 {
498 /* Return the Timer Handle */
499 *TimerHandle = hTimer;
500 }
501 _SEH2_EXCEPT(ExSystemExceptionFilter())
502 {
503
504 }
505 _SEH2_END;
506 }
507
508 /* Return to Caller */
509 return Status;
510 }
511
512 NTSTATUS
513 NTAPI
514 NtQueryTimer(IN HANDLE TimerHandle,
515 IN TIMER_INFORMATION_CLASS TimerInformationClass,
516 OUT PVOID TimerInformation,
517 IN ULONG TimerInformationLength,
518 OUT PULONG ReturnLength OPTIONAL)
519 {
520 PETIMER Timer;
521 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
522 NTSTATUS Status;
523 PTIMER_BASIC_INFORMATION BasicInfo = TimerInformation;
524 PAGED_CODE();
525
526 /* Check Validity */
527 Status = DefaultQueryInfoBufferCheck(TimerInformationClass,
528 ExTimerInfoClass,
529 sizeof(ExTimerInfoClass) /
530 sizeof(ExTimerInfoClass[0]),
531 TimerInformation,
532 TimerInformationLength,
533 ReturnLength,
534 NULL,
535 PreviousMode);
536 if (!NT_SUCCESS(Status)) return Status;
537
538 /* Get the Timer Object */
539 Status = ObReferenceObjectByHandle(TimerHandle,
540 TIMER_QUERY_STATE,
541 ExTimerType,
542 PreviousMode,
543 (PVOID*)&Timer,
544 NULL);
545 if (NT_SUCCESS(Status))
546 {
547 /* Return the Basic Information */
548 _SEH2_TRY
549 {
550 /* Return the remaining time, corrected */
551 BasicInfo->TimeRemaining.QuadPart = Timer->
552 KeTimer.DueTime.QuadPart -
553 KeQueryInterruptTime();
554
555 /* Return the current state */
556 BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer);
557
558 /* Return the buffer length if requested */
559 if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
560 }
561 _SEH2_EXCEPT(ExSystemExceptionFilter())
562 {
563 /* Get the exception code */
564 Status = _SEH2_GetExceptionCode();
565 }
566 _SEH2_END;
567
568 /* Dereference Object */
569 ObDereferenceObject(Timer);
570 }
571
572 /* Return Status */
573 return Status;
574 }
575
576 NTSTATUS
577 NTAPI
578 NtSetTimer(IN HANDLE TimerHandle,
579 IN PLARGE_INTEGER DueTime,
580 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
581 IN PVOID TimerContext OPTIONAL,
582 IN BOOLEAN WakeTimer,
583 IN LONG Period OPTIONAL,
584 OUT PBOOLEAN PreviousState OPTIONAL)
585 {
586 PETIMER Timer;
587 KIRQL OldIrql;
588 BOOLEAN State;
589 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
590 PETHREAD Thread = PsGetCurrentThread();
591 LARGE_INTEGER TimerDueTime;
592 PETHREAD TimerThread;
593 ULONG DerefsToDo = 1;
594 NTSTATUS Status = STATUS_SUCCESS;
595 PAGED_CODE();
596
597 /* Check for a valid Period */
598 if (Period < 0) return STATUS_INVALID_PARAMETER_6;
599
600 /* Check if we need to probe */
601 if (PreviousMode != KernelMode)
602 {
603 _SEH2_TRY
604 {
605 /* Probe and capture the due time */
606 TimerDueTime = ProbeForReadLargeInteger(DueTime);
607
608 /* Probe the state pointer if one was passed */
609 if (PreviousState) ProbeForWriteBoolean(PreviousState);
610 }
611 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
612 {
613 /* Return the exception code */
614 _SEH2_YIELD(return _SEH2_GetExceptionCode());
615 }
616 _SEH2_END;
617 }
618 else
619 {
620 /* Capture the time directly */
621 TimerDueTime = *DueTime;
622 }
623
624 /* Get the Timer Object */
625 Status = ObReferenceObjectByHandle(TimerHandle,
626 TIMER_MODIFY_STATE,
627 ExTimerType,
628 PreviousMode,
629 (PVOID*)&Timer,
630 NULL);
631
632 /*
633 * Tell the user we don't support Wake Timers...
634 * when we have the ability to use/detect the Power Management
635 * functionality required to support them, make this check dependent
636 * on the actual PM capabilities
637 */
638 if (WakeTimer) Status = STATUS_TIMER_RESUME_IGNORED;
639
640 /* Check status */
641 if (NT_SUCCESS(Status))
642 {
643 /* Lock the Timer */
644 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
645
646 /* Cancel Running Timer */
647 if (Timer->ApcAssociated)
648 {
649 /* Get the Thread. */
650 TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread,
651 ETHREAD,
652 Tcb);
653
654 /* Lock its active list */
655 KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock);
656
657 /* Remove it */
658 RemoveEntryList(&TimerThread->ActiveTimerListHead);
659 Timer->ApcAssociated = FALSE;
660
661 /* Unlock the list */
662 KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock);
663
664 /* Cancel the Timer */
665 KeCancelTimer(&Timer->KeTimer);
666 KeRemoveQueueDpc(&Timer->TimerDpc);
667 if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
668 DerefsToDo++;
669 }
670 else
671 {
672 /* If timer was disabled, we still need to cancel it */
673 KeCancelTimer(&Timer->KeTimer);
674 }
675
676 /* Read the State */
677 State = KeReadStateTimer(&Timer->KeTimer);
678
679 /* Handle Wake Timers */
680 Timer->WakeTimer = WakeTimer;
681 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock);
682 if ((WakeTimer) && !(Timer->WakeTimerListEntry.Flink))
683 {
684 /* Insert it into the list */
685 InsertTailList(&ExpWakeList, &Timer->WakeTimerListEntry);
686 }
687 else if (!(WakeTimer) && (Timer->WakeTimerListEntry.Flink))
688 {
689 /* Remove it from the list */
690 RemoveEntryList(&Timer->WakeTimerListEntry);
691 Timer->WakeTimerListEntry.Flink = NULL;
692 }
693 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock);
694
695 /* Set up the APC Routine if specified */
696 Timer->Period = Period;
697 if (TimerApcRoutine)
698 {
699 /* Initialize the APC */
700 KeInitializeApc(&Timer->TimerApc,
701 &Thread->Tcb,
702 CurrentApcEnvironment,
703 ExpTimerApcKernelRoutine,
704 (PKRUNDOWN_ROUTINE)NULL,
705 (PKNORMAL_ROUTINE)TimerApcRoutine,
706 PreviousMode,
707 TimerContext);
708
709 /* Lock the Thread's Active List and Insert */
710 KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
711 InsertTailList(&Thread->ActiveTimerListHead,
712 &Timer->ActiveTimerListEntry);
713 Timer->ApcAssociated = TRUE;
714 KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
715
716 /* One less dereference to do */
717 DerefsToDo--;
718 }
719
720 /* Enable and Set the Timer */
721 KeSetTimerEx(&Timer->KeTimer,
722 TimerDueTime,
723 Period,
724 TimerApcRoutine ? &Timer->TimerDpc : NULL);
725
726 /* Unlock the Timer */
727 KeReleaseSpinLock(&Timer->Lock, OldIrql);
728
729 /* Dereference if it was previously enabled */
730 if (DerefsToDo) ObDereferenceObjectEx(Timer, DerefsToDo);
731
732 /* Check if we need to return the State */
733 if (PreviousState)
734 {
735 /* Enter SEH */
736 _SEH2_TRY
737 {
738 /* Return the Timer State */
739 *PreviousState = State;
740 }
741 _SEH2_EXCEPT(ExSystemExceptionFilter())
742 {
743
744 }
745 _SEH2_END;
746 }
747 }
748
749 /* Return to Caller */
750 return Status;
751 }