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 IQS_SAME(TIMER_BASIC_INFORMATION, ULONG, ICIF_QUERY),
37 };
38
39 /* PRIVATE FUNCTIONS *********************************************************/
40
41 VOID
42 NTAPI
ExTimerRundown(VOID)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
ExpDeleteTimer(IN PVOID ObjectBody)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
_Function_class_(KDEFERRED_ROUTINE)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
ExpTimerApcKernelRoutine(IN PKAPC Apc,IN OUT PKNORMAL_ROUTINE * NormalRoutine,IN OUT PVOID * NormalContext,IN OUT PVOID * SystemArgument1,IN OUT PVOID * SystemArguemnt2)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 CODE_SEG("INIT")
221 BOOLEAN
222 NTAPI
ExpInitializeTimerImplementation(VOID)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
NtCancelTimer(IN HANDLE TimerHandle,OUT PBOOLEAN CurrentState OPTIONAL)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
NtCreateTimer(OUT PHANDLE TimerHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,IN TIMER_TYPE TimerType)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
NtOpenTimer(OUT PHANDLE TimerHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes)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
NtQueryTimer(IN HANDLE TimerHandle,IN TIMER_INFORMATION_CLASS TimerInformationClass,OUT PVOID TimerInformation,IN ULONG TimerInformationLength,OUT PULONG ReturnLength OPTIONAL)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 ICIF_PROBE_READ_WRITE,
536 TimerInformation,
537 TimerInformationLength,
538 ReturnLength,
539 NULL,
540 PreviousMode);
541 if (!NT_SUCCESS(Status)) return Status;
542
543 /* Get the Timer Object */
544 Status = ObReferenceObjectByHandle(TimerHandle,
545 TIMER_QUERY_STATE,
546 ExTimerType,
547 PreviousMode,
548 (PVOID*)&Timer,
549 NULL);
550 if (NT_SUCCESS(Status))
551 {
552 /* Return the Basic Information */
553 _SEH2_TRY
554 {
555 /* Return the remaining time, corrected */
556 BasicInfo->TimeRemaining.QuadPart = Timer->
557 KeTimer.DueTime.QuadPart -
558 KeQueryInterruptTime();
559
560 /* Return the current state */
561 BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer);
562
563 /* Return the buffer length if requested */
564 if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
565 }
566 _SEH2_EXCEPT(ExSystemExceptionFilter())
567 {
568 /* Get the exception code */
569 Status = _SEH2_GetExceptionCode();
570 }
571 _SEH2_END;
572
573 /* Dereference Object */
574 ObDereferenceObject(Timer);
575 }
576
577 /* Return Status */
578 return Status;
579 }
580
581 NTSTATUS
582 NTAPI
NtSetTimer(IN HANDLE TimerHandle,IN PLARGE_INTEGER DueTime,IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,IN PVOID TimerContext OPTIONAL,IN BOOLEAN WakeTimer,IN LONG Period OPTIONAL,OUT PBOOLEAN PreviousState OPTIONAL)583 NtSetTimer(IN HANDLE TimerHandle,
584 IN PLARGE_INTEGER DueTime,
585 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
586 IN PVOID TimerContext OPTIONAL,
587 IN BOOLEAN WakeTimer,
588 IN LONG Period OPTIONAL,
589 OUT PBOOLEAN PreviousState OPTIONAL)
590 {
591 PETIMER Timer;
592 KIRQL OldIrql;
593 BOOLEAN State;
594 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
595 PETHREAD Thread = PsGetCurrentThread();
596 LARGE_INTEGER TimerDueTime;
597 PETHREAD TimerThread;
598 ULONG DerefsToDo = 1;
599 NTSTATUS Status = STATUS_SUCCESS;
600 PAGED_CODE();
601
602 /* Check for a valid Period */
603 if (Period < 0) return STATUS_INVALID_PARAMETER_6;
604
605 /* Check if we need to probe */
606 if (PreviousMode != KernelMode)
607 {
608 _SEH2_TRY
609 {
610 /* Probe and capture the due time */
611 TimerDueTime = ProbeForReadLargeInteger(DueTime);
612
613 /* Probe the state pointer if one was passed */
614 if (PreviousState) ProbeForWriteBoolean(PreviousState);
615 }
616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
617 {
618 /* Return the exception code */
619 _SEH2_YIELD(return _SEH2_GetExceptionCode());
620 }
621 _SEH2_END;
622 }
623 else
624 {
625 /* Capture the time directly */
626 TimerDueTime = *DueTime;
627 }
628
629 /* Get the Timer Object */
630 Status = ObReferenceObjectByHandle(TimerHandle,
631 TIMER_MODIFY_STATE,
632 ExTimerType,
633 PreviousMode,
634 (PVOID*)&Timer,
635 NULL);
636
637 /*
638 * Tell the user we don't support Wake Timers...
639 * when we have the ability to use/detect the Power Management
640 * functionality required to support them, make this check dependent
641 * on the actual PM capabilities
642 */
643 if (NT_SUCCESS(Status) && WakeTimer)
644 {
645 Status = STATUS_TIMER_RESUME_IGNORED;
646 }
647
648 /* Check status */
649 if (NT_SUCCESS(Status))
650 {
651 /* Lock the Timer */
652 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
653
654 /* Cancel Running Timer */
655 if (Timer->ApcAssociated)
656 {
657 /* Get the Thread. */
658 TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread,
659 ETHREAD,
660 Tcb);
661
662 /* Lock its active list */
663 KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock);
664
665 /* Remove it */
666 RemoveEntryList(&Timer->ActiveTimerListEntry);
667 Timer->ApcAssociated = FALSE;
668
669 /* Unlock the list */
670 KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock);
671
672 /* Cancel the Timer */
673 KeCancelTimer(&Timer->KeTimer);
674 KeRemoveQueueDpc(&Timer->TimerDpc);
675 if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
676 DerefsToDo++;
677 }
678 else
679 {
680 /* If timer was disabled, we still need to cancel it */
681 KeCancelTimer(&Timer->KeTimer);
682 }
683
684 /* Read the State */
685 State = KeReadStateTimer(&Timer->KeTimer);
686
687 /* Handle Wake Timers */
688 Timer->WakeTimer = WakeTimer;
689 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock);
690 if ((WakeTimer) && !(Timer->WakeTimerListEntry.Flink))
691 {
692 /* Insert it into the list */
693 InsertTailList(&ExpWakeList, &Timer->WakeTimerListEntry);
694 }
695 else if (!(WakeTimer) && (Timer->WakeTimerListEntry.Flink))
696 {
697 /* Remove it from the list */
698 RemoveEntryList(&Timer->WakeTimerListEntry);
699 Timer->WakeTimerListEntry.Flink = NULL;
700 }
701 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock);
702
703 /* Set up the APC Routine if specified */
704 Timer->Period = Period;
705 if (TimerApcRoutine)
706 {
707 /* Initialize the APC */
708 KeInitializeApc(&Timer->TimerApc,
709 &Thread->Tcb,
710 CurrentApcEnvironment,
711 ExpTimerApcKernelRoutine,
712 (PKRUNDOWN_ROUTINE)NULL,
713 (PKNORMAL_ROUTINE)TimerApcRoutine,
714 PreviousMode,
715 TimerContext);
716
717 /* Lock the Thread's Active List and Insert */
718 KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
719 InsertTailList(&Thread->ActiveTimerListHead,
720 &Timer->ActiveTimerListEntry);
721 Timer->ApcAssociated = TRUE;
722 KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
723
724 /* One less dereference to do */
725 DerefsToDo--;
726 }
727
728 /* Enable and Set the Timer */
729 KeSetTimerEx(&Timer->KeTimer,
730 TimerDueTime,
731 Period,
732 TimerApcRoutine ? &Timer->TimerDpc : NULL);
733
734 /* Unlock the Timer */
735 KeReleaseSpinLock(&Timer->Lock, OldIrql);
736
737 /* Dereference if it was previously enabled */
738 if (DerefsToDo) ObDereferenceObjectEx(Timer, DerefsToDo);
739
740 /* Check if we need to return the State */
741 if (PreviousState)
742 {
743 /* Enter SEH */
744 _SEH2_TRY
745 {
746 /* Return the Timer State */
747 *PreviousState = State;
748 }
749 _SEH2_EXCEPT(ExSystemExceptionFilter())
750 {
751 /* Do nothing */
752 (void)0;
753 }
754 _SEH2_END;
755 }
756 }
757
758 /* Return to Caller */
759 return Status;
760 }
761