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