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