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