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