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