cd017145283d002e6e60347e735451b6a759fad5
[reactos.git] / reactos / ntoskrnl / ke / kthread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/kthread.c
5 * PURPOSE: Microkernel thread support
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 #define THREAD_ALERT_INCREMENT 2
18 /* FUNCTIONS *****************************************************************/
19
20 ULONG
21 STDCALL
22 KeAlertResumeThread(IN PKTHREAD Thread)
23 {
24 ULONG PreviousCount;
25 KIRQL OldIrql;
26
27 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
28
29 /* Lock the Dispatcher Database and the APC Queue */
30 OldIrql = KeAcquireDispatcherDatabaseLock();
31 KiAcquireSpinLock(&Thread->ApcQueueLock);
32
33 /* Return if Thread is already alerted. */
34 if (Thread->Alerted[KernelMode] == FALSE) {
35
36 /* If it's Blocked, unblock if it we should */
37 if (Thread->State == THREAD_STATE_BLOCKED && Thread->Alertable) {
38
39 DPRINT("Aborting Wait\n");
40 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
41
42 } else {
43
44 /* If not, simply Alert it */
45 Thread->Alerted[KernelMode] = TRUE;
46 }
47 }
48
49 /* Save the old Suspend Count */
50 PreviousCount = Thread->SuspendCount;
51
52 /* If the thread is suspended, decrease one of the suspend counts */
53 if (PreviousCount) {
54
55 /* Decrease count. If we are now zero, unwait it completely */
56 if (--Thread->SuspendCount) {
57
58 /* Signal and satisfy */
59 Thread->SuspendSemaphore.Header.SignalState++;
60 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
61 }
62 }
63
64 /* Release Locks and return the Old State */
65 KiReleaseSpinLock(&Thread->ApcQueueLock);
66 KeReleaseDispatcherDatabaseLock(OldIrql);
67 return PreviousCount;
68 }
69
70 BOOLEAN
71 STDCALL
72 KeAlertThread(PKTHREAD Thread,
73 KPROCESSOR_MODE AlertMode)
74 {
75 KIRQL OldIrql;
76 BOOLEAN PreviousState;
77
78 /* Acquire the Dispatcher Database Lock */
79 OldIrql = KeAcquireDispatcherDatabaseLock();
80
81 /* Save the Previous State */
82 PreviousState = Thread->Alerted[AlertMode];
83
84 /* Return if Thread is already alerted. */
85 if (PreviousState == FALSE) {
86
87 /* If it's Blocked, unblock if it we should */
88 if (Thread->State == THREAD_STATE_BLOCKED &&
89 (AlertMode == KernelMode || Thread->WaitMode == AlertMode) &&
90 Thread->Alertable) {
91
92 DPRINT("Aborting Wait\n");
93 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
94
95 } else {
96
97 /* If not, simply Alert it */
98 Thread->Alerted[AlertMode] = TRUE;
99 }
100 }
101
102 /* Release the Dispatcher Lock */
103 KeReleaseDispatcherDatabaseLock(OldIrql);
104
105 /* Return the old state */
106 return PreviousState;
107 }
108
109 /*
110 * @unimplemented
111 */
112 VOID
113 STDCALL
114 KeCapturePersistentThreadState(IN PVOID CurrentThread,
115 IN ULONG Setting1,
116 IN ULONG Setting2,
117 IN ULONG Setting3,
118 IN ULONG Setting4,
119 IN ULONG Setting5,
120 IN PVOID ThreadState)
121 {
122 UNIMPLEMENTED;
123 }
124
125 /*
126 * FUNCTION: Initialize the microkernel state of the thread
127 */
128 VOID
129 STDCALL
130 KeInitializeThread(PKPROCESS Process,
131 PKTHREAD Thread,
132 BOOLEAN First)
133 {
134 PVOID KernelStack;
135 NTSTATUS Status;
136 extern unsigned int init_stack_top;
137 extern unsigned int init_stack;
138 PMEMORY_AREA StackArea;
139 ULONG i;
140 PHYSICAL_ADDRESS BoundaryAddressMultiple;
141
142 /* Initialize the Boundary Address */
143 BoundaryAddressMultiple.QuadPart = 0;
144
145 /* Initalize the Dispatcher Header */
146 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
147 ThreadObject,
148 sizeof(KTHREAD),
149 FALSE);
150 InitializeListHead(&Thread->MutantListHead);
151
152 /* If this is isn't the first thread, allocate the Kernel Stack */
153 if (!First) {
154
155 PFN_TYPE Page[MM_STACK_SIZE / PAGE_SIZE];
156 KernelStack = NULL;
157
158 MmLockAddressSpace(MmGetKernelAddressSpace());
159 Status = MmCreateMemoryArea(NULL,
160 MmGetKernelAddressSpace(),
161 MEMORY_AREA_KERNEL_STACK,
162 &KernelStack,
163 MM_STACK_SIZE,
164 0,
165 &StackArea,
166 FALSE,
167 FALSE,
168 BoundaryAddressMultiple);
169 MmUnlockAddressSpace(MmGetKernelAddressSpace());
170
171 /* Check for Success */
172 if (!NT_SUCCESS(Status)) {
173
174 DPRINT1("Failed to create thread stack\n");
175 KEBUGCHECK(0);
176 }
177
178 /* Mark the Stack */
179 for (i = 0; i < (MM_STACK_SIZE / PAGE_SIZE); i++) {
180
181 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]);
182
183 /* Check for success */
184 if (!NT_SUCCESS(Status)) {
185
186 KEBUGCHECK(0);
187 }
188 }
189
190 /* Create a Virtual Mapping for it */
191 Status = MmCreateVirtualMapping(NULL,
192 KernelStack,
193 PAGE_READWRITE,
194 Page,
195 MM_STACK_SIZE / PAGE_SIZE);
196
197 /* Check for success */
198 if (!NT_SUCCESS(Status)) {
199
200 KEBUGCHECK(0);
201 }
202
203 /* Set the Kernel Stack */
204 Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE;
205 Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE;
206 Thread->StackLimit = (ULONG_PTR)KernelStack;
207 Thread->KernelStack = (PCHAR)KernelStack + MM_STACK_SIZE;
208
209 } else {
210
211 /* Use the Initial Stack */
212 Thread->InitialStack = (PCHAR)init_stack_top;
213 Thread->StackBase = (PCHAR)init_stack_top;
214 Thread->StackLimit = (ULONG_PTR)init_stack;
215 Thread->KernelStack = (PCHAR)init_stack_top;
216 }
217
218 /*
219 * Establish the pde's for the new stack and the thread structure within the
220 * address space of the new process. They are accessed while taskswitching or
221 * while handling page faults. At this point it isn't possible to call the
222 * page fault handler for the missing pde's.
223 */
224 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
225 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
226
227 /* Set the Thread to initalized */
228 Thread->State = THREAD_STATE_INITIALIZED;
229
230 /* The Native API function will initialize the TEB field later */
231 Thread->Teb = NULL;
232
233 /* Initialize stuff to zero */
234 Thread->TlsArray = NULL;
235 Thread->DebugActive = 0;
236 Thread->Alerted[0] = 0;
237 Thread->Alerted[1] = 0;
238 Thread->Iopl = 0;
239
240 /* Set up FPU/NPX Stuff */
241 Thread->NpxState = NPX_STATE_INVALID;
242 Thread->NpxIrql = 0;
243
244 /* Setup APC Fields */
245 InitializeListHead(&Thread->ApcState.ApcListHead[0]);
246 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
247 Thread->ApcState.Process = Process;
248 Thread->ApcState.KernelApcInProgress = 0;
249 Thread->ApcState.KernelApcPending = 0;
250 Thread->ApcState.UserApcPending = 0;
251 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
252 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
253 Thread->ApcStateIndex = OriginalApcEnvironment;
254 Thread->ApcQueueable = TRUE;
255 RtlZeroMemory(&Thread->SavedApcState, sizeof(KAPC_STATE));
256 KeInitializeSpinLock(&Thread->ApcQueueLock);
257
258 /* Setup Wait Fields */
259 Thread->WaitStatus = STATUS_SUCCESS;
260 Thread->WaitIrql = PASSIVE_LEVEL;
261 Thread->WaitMode = 0;
262 Thread->WaitNext = FALSE;
263 Thread->WaitListEntry.Flink = NULL;
264 Thread->WaitListEntry.Blink = NULL;
265 Thread->WaitTime = 0;
266 Thread->WaitBlockList = NULL;
267 RtlZeroMemory(Thread->WaitBlock, sizeof(KWAIT_BLOCK) * 4);
268 RtlZeroMemory(&Thread->Timer, sizeof(KTIMER));
269 KeInitializeTimer(&Thread->Timer);
270
271 /* Setup scheduler Fields */
272 Thread->BasePriority = Process->BasePriority;
273 Thread->DecrementCount = 0;
274 Thread->PriorityDecrement = 0;
275 Thread->Quantum = Process->ThreadQuantum;
276 Thread->Saturation = 0;
277 Thread->Priority = Process->BasePriority;
278 Thread->UserAffinity = Process->Affinity;
279 Thread->SystemAffinityActive = 0;
280 Thread->Affinity = Process->Affinity;
281 Thread->Preempted = 0;
282 Thread->ProcessReadyQueue = 0;
283 Thread->KernelStackResident = 1;
284 Thread->NextProcessor = 0;
285 Thread->ContextSwitches = 0;
286
287 /* Setup Queue Fields */
288 Thread->Queue = NULL;
289 Thread->QueueListEntry.Flink = NULL;
290 Thread->QueueListEntry.Blink = NULL;
291
292 /* Setup Misc Fields */
293 Thread->LegoData = 0;
294 Thread->PowerState = 0;
295 Thread->ServiceTable = KeServiceDescriptorTable;
296 Thread->CallbackStack = NULL;
297 Thread->Win32Thread = NULL;
298 Thread->TrapFrame = NULL;
299 Thread->EnableStackSwap = 0;
300 Thread->LargeStack = 0;
301 Thread->ResourceIndex = 0;
302 Thread->PreviousMode = KernelMode;
303 Thread->KernelTime = 0;
304 Thread->UserTime = 0;
305 Thread->AutoAlignment = Process->AutoAlignment;
306
307 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
308 #if 0
309 Thread->WaitBlock[3].Object = (PVOID)&Thread->Timer;
310 Thread->WaitBlock[3].Thread = Thread;
311 Thread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
312 Thread->WaitBlock[3].WaitType = WaitAny;
313 Thread->WaitBlock[3].NextWaitBlock = NULL;
314 InsertTailList(&Thread->Timer.Header.WaitListHead,
315 &Thread->WaitBlock[3].WaitListEntry);
316 #endif
317
318 /* Initialize the Suspend APC */
319 KeInitializeApc(&Thread->SuspendApc,
320 Thread,
321 OriginalApcEnvironment,
322 PiSuspendThreadKernelRoutine,
323 PiSuspendThreadRundownRoutine,
324 PiSuspendThreadNormalRoutine,
325 KernelMode,
326 NULL);
327
328 /* Initialize the Suspend Semaphore */
329 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128);
330
331 /* Insert the Thread into the Process's Thread List */
332 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
333
334 /* Set up the Suspend Counts */
335 Thread->FreezeCount = 0;
336 Thread->SuspendCount = 0;
337
338 /* Do x86 specific part */
339 }
340
341 /*
342 * @implemented
343 */
344 KPRIORITY
345 STDCALL
346 KeQueryPriorityThread (IN PKTHREAD Thread)
347 {
348 return Thread->Priority;
349 }
350
351 /*
352 * @implemented
353 */
354 ULONG
355 STDCALL
356 KeQueryRuntimeThread(IN PKTHREAD Thread,
357 OUT PULONG UserTime)
358 {
359 /* Return the User Time */
360 *UserTime = Thread->UserTime;
361
362 /* Return the Kernel Time */
363 return Thread->KernelTime;
364 }
365
366 VOID
367 KeFreeStackPage(PVOID Context,
368 MEMORY_AREA* MemoryArea,
369 PVOID Address,
370 PFN_TYPE Page,
371 SWAPENTRY SwapEntry,
372 BOOLEAN Dirty)
373 {
374 ASSERT(SwapEntry == 0);
375 if (Page) MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
376 }
377
378 NTSTATUS
379 KeReleaseThread(PKTHREAD Thread)
380 /*
381 * FUNCTION: Releases the resource allocated for a thread by
382 * KeInitializeThread
383 * NOTE: The thread had better not be running when this is called
384 */
385 {
386 extern unsigned int init_stack;
387
388 /* FIXME - lock the process */
389 RemoveEntryList(&Thread->ThreadListEntry);
390
391 if (Thread->StackLimit != (ULONG_PTR)init_stack)
392 {
393 MmLockAddressSpace(MmGetKernelAddressSpace());
394 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
395 (PVOID)Thread->StackLimit,
396 KeFreeStackPage,
397 NULL);
398 MmUnlockAddressSpace(MmGetKernelAddressSpace());
399 }
400 Thread->StackLimit = 0;
401 Thread->InitialStack = NULL;
402 Thread->StackBase = NULL;
403 Thread->KernelStack = NULL;
404 return(STATUS_SUCCESS);
405 }
406
407 /*
408 * @implemented
409 */
410 BOOLEAN
411 STDCALL
412 KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
413 {
414 PKTHREAD Thread = KeGetCurrentThread();
415 BOOLEAN PreviousState;
416 KIRQL OldIrql;
417
418 /* Lock the Dispatcher Database */
419 OldIrql = KeAcquireDispatcherDatabaseLock();
420
421 /* Save Old State */
422 PreviousState = Thread->EnableStackSwap;
423
424 /* Set New State */
425 Thread->EnableStackSwap = Enable;
426
427 /* No, Release Lock */
428 KeReleaseDispatcherDatabaseLock(OldIrql);
429
430 /* Return Old State */
431 return PreviousState;
432 }
433
434 /*
435 * @implemented
436 */
437 VOID
438 STDCALL
439 KeRevertToUserAffinityThread(VOID)
440 {
441 PKTHREAD CurrentThread = KeGetCurrentThread();
442 KIRQL OldIrql;
443
444 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
445
446 /* Lock the Dispatcher Database */
447 OldIrql = KeAcquireDispatcherDatabaseLock();
448
449 /* Return to User Affinity */
450 CurrentThread->Affinity = CurrentThread->UserAffinity;
451
452 /* Disable System Affinity */
453 CurrentThread->SystemAffinityActive = FALSE;
454
455 /* Check if we need to Dispatch a New thread */
456 if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) {
457
458 /* No, just release */
459 KeReleaseDispatcherDatabaseLock(OldIrql);
460
461 } else {
462
463 /* We need to dispatch a new thread */
464 CurrentThread->WaitIrql = OldIrql;
465 PsDispatchThreadNoLock(THREAD_STATE_READY);
466 KeLowerIrql(OldIrql);
467 }
468 }
469
470 /*
471 * @implemented
472 */
473 CCHAR
474 STDCALL
475 KeSetIdealProcessorThread(IN PKTHREAD Thread,
476 IN CCHAR Processor)
477 {
478 CCHAR PreviousIdealProcessor;
479 KIRQL OldIrql;
480
481 /* Lock the Dispatcher Database */
482 OldIrql = KeAcquireDispatcherDatabaseLock();
483
484 /* Save Old Ideal Processor */
485 PreviousIdealProcessor = Thread->IdealProcessor;
486
487 /* Set New Ideal Processor */
488 Thread->IdealProcessor = Processor;
489
490 /* Release Lock */
491 KeReleaseDispatcherDatabaseLock(OldIrql);
492
493 /* Return Old Ideal Processor */
494 return PreviousIdealProcessor;
495 }
496
497 /*
498 * @implemented
499 */
500 VOID
501 STDCALL
502 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
503 {
504 PKTHREAD CurrentThread = KeGetCurrentThread();
505 KIRQL OldIrql;
506
507 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
508
509 /* Lock the Dispatcher Database */
510 OldIrql = KeAcquireDispatcherDatabaseLock();
511
512 /* Set the System Affinity Specified */
513 CurrentThread->Affinity = Affinity;
514
515 /* Enable System Affinity */
516 CurrentThread->SystemAffinityActive = TRUE;
517
518 /* Check if we need to Dispatch a New thread */
519 if (Affinity & (1 << KeGetCurrentProcessorNumber())) {
520
521 /* No, just release */
522 KeReleaseDispatcherDatabaseLock(OldIrql);
523
524 } else {
525
526 /* We need to dispatch a new thread */
527 CurrentThread->WaitIrql = OldIrql;
528 PsDispatchThreadNoLock(THREAD_STATE_READY);
529 KeLowerIrql(OldIrql);
530 }
531 }
532
533 /*
534 * @implemented
535 */
536 /* The Increment Argument seems to be ignored by NT and always 0 when called */
537 VOID
538 STDCALL
539 KeTerminateThread(IN KPRIORITY Increment)
540 {
541 /* Call our own internal routine */
542 PsTerminateCurrentThread(0);
543 }
544
545 /*
546 * FUNCTION: Tests whether there are any pending APCs for the current thread
547 * and if so the APCs will be delivered on exit from kernel mode
548 */
549 BOOLEAN
550 STDCALL
551 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
552 {
553 KIRQL OldIrql;
554 PKTHREAD Thread = KeGetCurrentThread();
555 BOOLEAN OldState;
556
557 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
558
559 /* Lock the Dispatcher Database and the APC Queue */
560 OldIrql = KeAcquireDispatcherDatabaseLock();
561 KiAcquireSpinLock(&Thread->ApcQueueLock);
562
563 /* Save the old State */
564 OldState = Thread->Alerted[AlertMode];
565
566 /* If the Thread is Alerted, Clear it */
567 if (OldState) {
568
569 Thread->Alerted[AlertMode] = FALSE;
570
571 } else if ((AlertMode == UserMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) {
572
573 /* If the mode is User and the Queue isn't empty, set Pending */
574 Thread->ApcState.UserApcPending = TRUE;
575 }
576
577 /* Release Locks and return the Old State */
578 KiReleaseSpinLock(&Thread->ApcQueueLock);
579 KeReleaseDispatcherDatabaseLock(OldIrql);
580 return OldState;
581 }
582
583 VOID
584 KiServiceCheck (VOID)
585 {
586 PKTHREAD Thread = KeGetCurrentThread();
587
588 /* Check if we need to inialize Win32 for this Thread */
589 if (Thread->ServiceTable != KeServiceDescriptorTableShadow) {
590
591 /* We do. Initialize it and save the new table */
592 PsInitWin32Thread((PETHREAD)Thread);
593 Thread->ServiceTable = KeServiceDescriptorTableShadow;
594 }
595 }
596
597 /*
598 *
599 * NOT EXPORTED
600 */
601 NTSTATUS
602 STDCALL
603 NtAlertResumeThread(IN HANDLE ThreadHandle,
604 OUT PULONG SuspendCount)
605 {
606 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
607 PETHREAD Thread;
608 NTSTATUS Status;
609 ULONG PreviousState;
610
611 /* Check if parameters are valid */
612 if(PreviousMode != KernelMode) {
613
614 _SEH_TRY {
615
616 ProbeForWrite(SuspendCount,
617 sizeof(HANDLE),
618 sizeof(ULONG));
619
620 } _SEH_HANDLE {
621
622 Status = _SEH_GetExceptionCode();
623
624 } _SEH_END;
625 }
626
627 /* Reference the Object */
628 Status = ObReferenceObjectByHandle(ThreadHandle,
629 THREAD_SUSPEND_RESUME,
630 PsThreadType,
631 PreviousMode,
632 (PVOID*)&Thread,
633 NULL);
634
635 /* Check for Success */
636 if (NT_SUCCESS(Status)) {
637
638 /* Call the Kernel Function */
639 PreviousState = KeAlertResumeThread(&Thread->Tcb);
640
641 /* Dereference Object */
642 ObDereferenceObject(Thread);
643
644 if (SuspendCount) {
645
646 _SEH_TRY {
647
648 *SuspendCount = PreviousState;
649
650 } _SEH_HANDLE {
651
652 Status = _SEH_GetExceptionCode();
653
654 } _SEH_END;
655 }
656 }
657
658 /* Return status */
659 return Status;
660 }
661
662 /*
663 * @implemented
664 *
665 * EXPORTED
666 */
667 NTSTATUS
668 STDCALL
669 NtAlertThread (IN HANDLE ThreadHandle)
670 {
671 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
672 PETHREAD Thread;
673 NTSTATUS Status;
674
675 /* Reference the Object */
676 Status = ObReferenceObjectByHandle(ThreadHandle,
677 THREAD_SUSPEND_RESUME,
678 PsThreadType,
679 PreviousMode,
680 (PVOID*)&Thread,
681 NULL);
682
683 /* Check for Success */
684 if (NT_SUCCESS(Status)) {
685
686 /*
687 * Do an alert depending on the processor mode. If some kmode code wants to
688 * enforce a umode alert it should call KeAlertThread() directly. If kmode
689 * code wants to do a kmode alert it's sufficient to call it with Zw or just
690 * use KeAlertThread() directly
691 */
692 KeAlertThread(&Thread->Tcb, PreviousMode);
693
694 /* Dereference Object */
695 ObDereferenceObject(Thread);
696 }
697
698 /* Return status */
699 return Status;
700 }
701
702 NTSTATUS
703 STDCALL
704 NtDelayExecution(IN BOOLEAN Alertable,
705 IN PLARGE_INTEGER DelayInterval)
706 {
707 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
708 LARGE_INTEGER SafeInterval;
709 NTSTATUS Status;
710
711 /* Check if parameters are valid */
712 if(PreviousMode != KernelMode) {
713
714 _SEH_TRY {
715
716 ProbeForRead(DelayInterval,
717 sizeof(LARGE_INTEGER),
718 sizeof(ULONG));
719
720 /* make a copy on the kernel stack and let DelayInterval point to it so
721 we don't need to wrap KeDelayExecutionThread in SEH! */
722 SafeInterval = *DelayInterval;
723
724 } _SEH_HANDLE {
725
726 Status = _SEH_GetExceptionCode();
727 } _SEH_END;
728 }
729
730 /* Call the Kernel Function */
731 Status = KeDelayExecutionThread(PreviousMode,
732 Alertable,
733 &SafeInterval);
734
735 /* Return Status */
736 return Status;
737 }