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