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