[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ke / thrdschd.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/thrdschd.c
5 * PURPOSE: Kernel Thread Scheduler (Affinity, Priority, Scheduling)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 ULONG KiIdleSummary;
18 ULONG KiIdleSMTSummary;
19
20 /* FUNCTIONS *****************************************************************/
21
22 PKTHREAD
23 FASTCALL
24 KiIdleSchedule(IN PKPRCB Prcb)
25 {
26 /* FIXME: TODO */
27 ASSERTMSG("SMP: Not yet implemented\n", FALSE);
28 return NULL;
29 }
30
31 VOID
32 FASTCALL
33 KiProcessDeferredReadyList(IN PKPRCB Prcb)
34 {
35 PSINGLE_LIST_ENTRY ListEntry;
36 PKTHREAD Thread;
37
38 /* Make sure there is something on the ready list */
39 ASSERT(Prcb->DeferredReadyListHead.Next != NULL);
40
41 /* Get the first entry and clear the list */
42 ListEntry = Prcb->DeferredReadyListHead.Next;
43 Prcb->DeferredReadyListHead.Next = NULL;
44
45 /* Start processing loop */
46 do
47 {
48 /* Get the thread and advance to the next entry */
49 Thread = CONTAINING_RECORD(ListEntry, KTHREAD, SwapListEntry);
50 ListEntry = ListEntry->Next;
51
52 /* Make the thread ready */
53 KiDeferredReadyThread(Thread);
54 } while (ListEntry != NULL);
55
56 /* Make sure the ready list is still empty */
57 ASSERT(Prcb->DeferredReadyListHead.Next == NULL);
58 }
59
60 VOID
61 FASTCALL
62 KiQueueReadyThread(IN PKTHREAD Thread,
63 IN PKPRCB Prcb)
64 {
65 /* Call the macro. We keep the API for compatibility with ASM code */
66 KxQueueReadyThread(Thread, Prcb);
67 }
68
69 VOID
70 FASTCALL
71 KiDeferredReadyThread(IN PKTHREAD Thread)
72 {
73 PKPRCB Prcb;
74 BOOLEAN Preempted;
75 ULONG Processor = 0;
76 KPRIORITY OldPriority;
77 PKTHREAD NextThread;
78
79 /* Sanity checks */
80 ASSERT(Thread->State == DeferredReady);
81 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
82
83 /* Check if we have any adjusts to do */
84 if (Thread->AdjustReason == AdjustBoost)
85 {
86 /* Lock the thread */
87 KiAcquireThreadLock(Thread);
88
89 /* Check if the priority is low enough to qualify for boosting */
90 if ((Thread->Priority <= Thread->AdjustIncrement) &&
91 (Thread->Priority < (LOW_REALTIME_PRIORITY - 3)) &&
92 !(Thread->DisableBoost))
93 {
94 /* Calculate the new priority based on the adjust increment */
95 OldPriority = min(Thread->AdjustIncrement + 1,
96 LOW_REALTIME_PRIORITY - 3);
97
98 /* Make sure we're not decreasing outside of the priority range */
99 ASSERT((Thread->PriorityDecrement >= 0) &&
100 (Thread->PriorityDecrement <= Thread->Priority));
101
102 /* Calculate the new priority decrement based on the boost */
103 Thread->PriorityDecrement += ((SCHAR)OldPriority - Thread->Priority);
104
105 /* Again verify that this decrement is valid */
106 ASSERT((Thread->PriorityDecrement >= 0) &&
107 (Thread->PriorityDecrement <= OldPriority));
108
109 /* Set the new priority */
110 Thread->Priority = (SCHAR)OldPriority;
111 }
112
113 /* We need 4 quanta, make sure we have them, then decrease by one */
114 if (Thread->Quantum < 4) Thread->Quantum = 4;
115 Thread->Quantum--;
116
117 /* Make sure the priority is still valid */
118 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
119
120 /* Release the lock and clear the adjust reason */
121 KiReleaseThreadLock(Thread);
122 Thread->AdjustReason = AdjustNone;
123 }
124 else if (Thread->AdjustReason == AdjustUnwait)
125 {
126 /* Acquire the thread lock and check if this is a real-time thread */
127 KiAcquireThreadLock(Thread);
128 if (Thread->Priority < LOW_REALTIME_PRIORITY)
129 {
130 /* It's not real time, but is it time critical? */
131 if (Thread->BasePriority >= (LOW_REALTIME_PRIORITY - 2))
132 {
133 /* It is, so simply reset its quantum */
134 Thread->Quantum = Thread->QuantumReset;
135 }
136 else
137 {
138 /* Has the priority been adjusted previously? */
139 if (!(Thread->PriorityDecrement) && (Thread->AdjustIncrement))
140 {
141 /* Yes, reset its quantum */
142 Thread->Quantum = Thread->QuantumReset;
143 }
144
145 /* Wait code already handles quantum adjustment during APCs */
146 if (Thread->WaitStatus != STATUS_KERNEL_APC)
147 {
148 /* Decrease the quantum by one and check if we're out */
149 if (--Thread->Quantum <= 0)
150 {
151 /* We are, reset the quantum and get a new priority */
152 Thread->Quantum = Thread->QuantumReset;
153 Thread->Priority = KiComputeNewPriority(Thread, 1);
154 }
155 }
156 }
157
158 /* Now check if we have no decrement and boosts are enabled */
159 if (!(Thread->PriorityDecrement) && !(Thread->DisableBoost))
160 {
161 /* Make sure we have an increment */
162 ASSERT(Thread->AdjustIncrement >= 0);
163
164 /* Calculate the new priority after the increment */
165 OldPriority = Thread->BasePriority + Thread->AdjustIncrement;
166
167 /* Check if this new priority is higher */
168 if (OldPriority > Thread->Priority)
169 {
170 /* Make sure we don't go into the real time range */
171 if (OldPriority >= LOW_REALTIME_PRIORITY)
172 {
173 /* Normalize it back down one notch */
174 OldPriority = LOW_REALTIME_PRIORITY - 1;
175 }
176
177 /* Check if the priority is higher then the boosted base */
178 if (OldPriority > (Thread->BasePriority +
179 Thread->AdjustIncrement))
180 {
181 /* Setup a priority decrement to nullify the boost */
182 Thread->PriorityDecrement = ((SCHAR)OldPriority -
183 Thread->BasePriority -
184 Thread->AdjustIncrement);
185 }
186
187 /* Make sure that the priority decrement is valid */
188 ASSERT((Thread->PriorityDecrement >= 0) &&
189 (Thread->PriorityDecrement <= OldPriority));
190
191 /* Set this new priority */
192 Thread->Priority = (SCHAR)OldPriority;
193 }
194 }
195 }
196 else
197 {
198 /* It's a real-time thread, so just reset its quantum */
199 Thread->Quantum = Thread->QuantumReset;
200 }
201
202 /* Make sure the priority makes sense */
203 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
204
205 /* Release the thread lock and reset the adjust reason */
206 KiReleaseThreadLock(Thread);
207 Thread->AdjustReason = AdjustNone;
208 }
209
210 /* Clear thread preemption status and save current values */
211 Preempted = Thread->Preempted;
212 OldPriority = Thread->Priority;
213 Thread->Preempted = FALSE;
214
215 /* Queue the thread on CPU 0 and get the PRCB and lock it */
216 Thread->NextProcessor = 0;
217 Prcb = KiProcessorBlock[0];
218 KiAcquirePrcbLock(Prcb);
219
220 /* Check if we have an idle summary */
221 if (KiIdleSummary)
222 {
223 /* Clear it and set this thread as the next one */
224 KiIdleSummary = 0;
225 Thread->State = Standby;
226 Prcb->NextThread = Thread;
227
228 /* Unlock the PRCB and return */
229 KiReleasePrcbLock(Prcb);
230 return;
231 }
232
233 /* Set the CPU number */
234 Thread->NextProcessor = (UCHAR)Processor;
235
236 /* Get the next scheduled thread */
237 NextThread = Prcb->NextThread;
238 if (NextThread)
239 {
240 /* Sanity check */
241 ASSERT(NextThread->State == Standby);
242
243 /* Check if priority changed */
244 if (OldPriority > NextThread->Priority)
245 {
246 /* Preempt the thread */
247 NextThread->Preempted = TRUE;
248
249 /* Put this one as the next one */
250 Thread->State = Standby;
251 Prcb->NextThread = Thread;
252
253 /* Set it in deferred ready mode */
254 NextThread->State = DeferredReady;
255 NextThread->DeferredProcessor = Prcb->Number;
256 KiReleasePrcbLock(Prcb);
257 KiDeferredReadyThread(NextThread);
258 return;
259 }
260 }
261 else
262 {
263 /* Set the next thread as the current thread */
264 NextThread = Prcb->CurrentThread;
265 if (OldPriority > NextThread->Priority)
266 {
267 /* Preempt it if it's already running */
268 if (NextThread->State == Running) NextThread->Preempted = TRUE;
269
270 /* Set the thread on standby and as the next thread */
271 Thread->State = Standby;
272 Prcb->NextThread = Thread;
273
274 /* Release the lock */
275 KiReleasePrcbLock(Prcb);
276
277 /* Check if we're running on another CPU */
278 if (KeGetCurrentProcessorNumber() != Thread->NextProcessor)
279 {
280 /* We are, send an IPI */
281 KiIpiSend(AFFINITY_MASK(Thread->NextProcessor), IPI_DPC);
282 }
283 return;
284 }
285 }
286
287 /* Sanity check */
288 ASSERT((OldPriority >= 0) && (OldPriority <= HIGH_PRIORITY));
289
290 /* Set this thread as ready */
291 Thread->State = Ready;
292 Thread->WaitTime = KeTickCount.LowPart;
293
294 /* Insert this thread in the appropriate order */
295 Preempted ? InsertHeadList(&Prcb->DispatcherReadyListHead[OldPriority],
296 &Thread->WaitListEntry) :
297 InsertTailList(&Prcb->DispatcherReadyListHead[OldPriority],
298 &Thread->WaitListEntry);
299
300 /* Update the ready summary */
301 Prcb->ReadySummary |= PRIORITY_MASK(OldPriority);
302
303 /* Sanity check */
304 ASSERT(OldPriority == Thread->Priority);
305
306 /* Release the lock */
307 KiReleasePrcbLock(Prcb);
308 }
309
310 PKTHREAD
311 FASTCALL
312 KiSelectNextThread(IN PKPRCB Prcb)
313 {
314 PKTHREAD Thread;
315
316 /* Select a ready thread */
317 Thread = KiSelectReadyThread(0, Prcb);
318 if (!Thread)
319 {
320 /* Didn't find any, get the current idle thread */
321 Thread = Prcb->IdleThread;
322
323 /* Enable idle scheduling */
324 InterlockedOr((PLONG) &KiIdleSummary, Prcb->SetMember);
325 Prcb->IdleSchedule = TRUE;
326
327 /* FIXME: SMT support */
328 ASSERTMSG("SMP: Not yet implemented\n", FALSE);
329 }
330
331 /* Sanity checks and return the thread */
332 ASSERT(Thread != NULL);
333 ASSERT((Thread->BasePriority == 0) || (Thread->Priority != 0));
334 return Thread;
335 }
336
337 LONG_PTR
338 FASTCALL
339 KiSwapThread(IN PKTHREAD CurrentThread,
340 IN PKPRCB Prcb)
341 {
342 BOOLEAN ApcState = FALSE;
343 KIRQL WaitIrql;
344 LONG_PTR WaitStatus;
345 PKTHREAD NextThread;
346 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
347
348 /* Acquire the PRCB lock */
349 KiAcquirePrcbLock(Prcb);
350
351 /* Get the next thread */
352 NextThread = Prcb->NextThread;
353 if (NextThread)
354 {
355 /* Already got a thread, set it up */
356 Prcb->NextThread = NULL;
357 Prcb->CurrentThread = NextThread;
358 NextThread->State = Running;
359 }
360 else
361 {
362 /* Try to find a ready thread */
363 NextThread = KiSelectReadyThread(0, Prcb);
364 if (NextThread)
365 {
366 /* Switch to it */
367 Prcb->CurrentThread = NextThread;
368 NextThread->State = Running;
369 }
370 else
371 {
372 /* Set the idle summary */
373 InterlockedOr((PLONG)&KiIdleSummary, Prcb->SetMember);
374
375 /* Schedule the idle thread */
376 NextThread = Prcb->IdleThread;
377 Prcb->CurrentThread = NextThread;
378 NextThread->State = Running;
379 }
380 }
381
382 /* Sanity check and release the PRCB */
383 ASSERT(CurrentThread != Prcb->IdleThread);
384 KiReleasePrcbLock(Prcb);
385
386 /* Save the wait IRQL */
387 WaitIrql = CurrentThread->WaitIrql;
388
389 /* Swap contexts */
390 ApcState = KiSwapContext(WaitIrql, CurrentThread);
391
392 /* Get the wait status */
393 WaitStatus = CurrentThread->WaitStatus;
394
395 /* Check if we need to deliver APCs */
396 if (ApcState)
397 {
398 /* Lower to APC_LEVEL */
399 KeLowerIrql(APC_LEVEL);
400
401 /* Deliver APCs */
402 KiDeliverApc(KernelMode, NULL, NULL);
403 ASSERT(WaitIrql == 0);
404 }
405
406 /* Lower IRQL back to what it was and return the wait status */
407 KeLowerIrql(WaitIrql);
408 return WaitStatus;
409 }
410
411 VOID
412 NTAPI
413 KiReadyThread(IN PKTHREAD Thread)
414 {
415 IN PKPROCESS Process = Thread->ApcState.Process;
416
417 /* Check if the process is paged out */
418 if (Process->State != ProcessInMemory)
419 {
420 /* We don't page out processes in ROS */
421 ASSERT(FALSE);
422 }
423 else if (!Thread->KernelStackResident)
424 {
425 /* Increase the stack count */
426 ASSERT(Process->StackCount != MAXULONG_PTR);
427 Process->StackCount++;
428
429 /* Set the thread to transition */
430 ASSERT(Thread->State != Transition);
431 Thread->State = Transition;
432
433 /* The stack is always resident in ROS */
434 ASSERT(FALSE);
435 }
436 else
437 {
438 /* Insert the thread on the deferred ready list */
439 KiInsertDeferredReadyList(Thread);
440 }
441 }
442
443 VOID
444 NTAPI
445 KiAdjustQuantumThread(IN PKTHREAD Thread)
446 {
447 PKPRCB Prcb = KeGetCurrentPrcb();
448 PKTHREAD NextThread;
449
450 /* Acquire thread and PRCB lock */
451 KiAcquireThreadLock(Thread);
452 KiAcquirePrcbLock(Prcb);
453
454 /* Don't adjust for RT threads */
455 if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
456 (Thread->BasePriority < (LOW_REALTIME_PRIORITY - 2)))
457 {
458 /* Decrease Quantum by one and see if we've ran out */
459 if (--Thread->Quantum <= 0)
460 {
461 /* Return quantum */
462 Thread->Quantum = Thread->QuantumReset;
463
464 /* Calculate new Priority */
465 Thread->Priority = KiComputeNewPriority(Thread, 1);
466
467 /* Check if there's no next thread scheduled */
468 if (!Prcb->NextThread)
469 {
470 /* Select a ready thread and check if we found one */
471 NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
472 if (NextThread)
473 {
474 /* Set it on standby and switch to it */
475 NextThread->State = Standby;
476 Prcb->NextThread = NextThread;
477 }
478 }
479 else
480 {
481 /* This thread can be preempted again */
482 Thread->Preempted = FALSE;
483 }
484 }
485 }
486
487 /* Release locks */
488 KiReleasePrcbLock(Prcb);
489 KiReleaseThreadLock(Thread);
490 KiExitDispatcher(Thread->WaitIrql);
491 }
492
493 VOID
494 FASTCALL
495 KiSetPriorityThread(IN PKTHREAD Thread,
496 IN KPRIORITY Priority)
497 {
498 PKPRCB Prcb;
499 ULONG Processor;
500 BOOLEAN RequestInterrupt = FALSE;
501 KPRIORITY OldPriority;
502 PKTHREAD NewThread;
503 ASSERT((Priority >= 0) && (Priority <= HIGH_PRIORITY));
504
505 /* Check if priority changed */
506 if (Thread->Priority != Priority)
507 {
508 /* Loop priority setting in case we need to start over */
509 for (;;)
510 {
511 /* Choose action based on thread's state */
512 if (Thread->State == Ready)
513 {
514 /* Make sure we're not on the ready queue */
515 if (!Thread->ProcessReadyQueue)
516 {
517 /* Get the PRCB for the thread and lock it */
518 Processor = Thread->NextProcessor;
519 Prcb = KiProcessorBlock[Processor];
520 KiAcquirePrcbLock(Prcb);
521
522 /* Make sure the thread is still ready and on this CPU */
523 if ((Thread->State == Ready) &&
524 (Thread->NextProcessor == Prcb->Number))
525 {
526 /* Sanity check */
527 ASSERT((Prcb->ReadySummary &
528 PRIORITY_MASK(Thread->Priority)));
529
530 /* Remove it from the current queue */
531 if (RemoveEntryList(&Thread->WaitListEntry))
532 {
533 /* Update the ready summary */
534 Prcb->ReadySummary ^= PRIORITY_MASK(Thread->
535 Priority);
536 }
537
538 /* Update priority */
539 Thread->Priority = (SCHAR)Priority;
540
541 /* Re-insert it at its current priority */
542 KiInsertDeferredReadyList(Thread);
543
544 /* Release the PRCB Lock */
545 KiReleasePrcbLock(Prcb);
546 }
547 else
548 {
549 /* Release the lock and loop again */
550 KiReleasePrcbLock(Prcb);
551 continue;
552 }
553 }
554 else
555 {
556 /* It's already on the ready queue, just update priority */
557 Thread->Priority = (SCHAR)Priority;
558 }
559 }
560 else if (Thread->State == Standby)
561 {
562 /* Get the PRCB for the thread and lock it */
563 Processor = Thread->NextProcessor;
564 Prcb = KiProcessorBlock[Processor];
565 KiAcquirePrcbLock(Prcb);
566
567 /* Check if we're still the next thread to run */
568 if (Thread == Prcb->NextThread)
569 {
570 /* Get the old priority and update ours */
571 OldPriority = Thread->Priority;
572 Thread->Priority = (SCHAR)Priority;
573
574 /* Check if there was a change */
575 if (Priority < OldPriority)
576 {
577 /* Find a new thread */
578 NewThread = KiSelectReadyThread(Priority + 1, Prcb);
579 if (NewThread)
580 {
581 /* Found a new one, set it on standby */
582 NewThread->State = Standby;
583 Prcb->NextThread = NewThread;
584
585 /* Dispatch our thread */
586 KiInsertDeferredReadyList(Thread);
587 }
588 }
589
590 /* Release the PRCB lock */
591 KiReleasePrcbLock(Prcb);
592 }
593 else
594 {
595 /* Release the lock and try again */
596 KiReleasePrcbLock(Prcb);
597 continue;
598 }
599 }
600 else if (Thread->State == Running)
601 {
602 /* Get the PRCB for the thread and lock it */
603 Processor = Thread->NextProcessor;
604 Prcb = KiProcessorBlock[Processor];
605 KiAcquirePrcbLock(Prcb);
606
607 /* Check if we're still the current thread running */
608 if (Thread == Prcb->CurrentThread)
609 {
610 /* Get the old priority and update ours */
611 OldPriority = Thread->Priority;
612 Thread->Priority = (SCHAR)Priority;
613
614 /* Check if there was a change and there's no new thread */
615 if ((Priority < OldPriority) && !(Prcb->NextThread))
616 {
617 /* Find a new thread */
618 NewThread = KiSelectReadyThread(Priority + 1, Prcb);
619 if (NewThread)
620 {
621 /* Found a new one, set it on standby */
622 NewThread->State = Standby;
623 Prcb->NextThread = NewThread;
624
625 /* Request an interrupt */
626 RequestInterrupt = TRUE;
627 }
628 }
629
630 /* Release the lock and check if we need an interrupt */
631 KiReleasePrcbLock(Prcb);
632 if (RequestInterrupt)
633 {
634 /* Check if we're running on another CPU */
635 if (KeGetCurrentProcessorNumber() != Processor)
636 {
637 /* We are, send an IPI */
638 KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC);
639 }
640 }
641 }
642 else
643 {
644 /* Thread changed, release lock and restart */
645 KiReleasePrcbLock(Prcb);
646 continue;
647 }
648 }
649 else if (Thread->State == DeferredReady)
650 {
651 /* FIXME: TODO */
652 DPRINT1("Deferred state not yet supported\n");
653 ASSERT(FALSE);
654 }
655 else
656 {
657 /* Any other state, just change priority */
658 Thread->Priority = (SCHAR)Priority;
659 }
660
661 /* If we got here, then thread state was consistent, so bail out */
662 break;
663 }
664 }
665 }
666
667 KAFFINITY
668 FASTCALL
669 KiSetAffinityThread(IN PKTHREAD Thread,
670 IN KAFFINITY Affinity)
671 {
672 KAFFINITY OldAffinity;
673
674 /* Get the current affinity */
675 OldAffinity = Thread->UserAffinity;
676
677 /* Make sure that the affinity is valid */
678 if (((Affinity & Thread->ApcState.Process->Affinity) != (Affinity)) ||
679 (!Affinity))
680 {
681 /* Bugcheck the system */
682 KeBugCheck(INVALID_AFFINITY_SET);
683 }
684
685 /* Update the new affinity */
686 Thread->UserAffinity = Affinity;
687
688 /* Check if system affinity is disabled */
689 if (!Thread->SystemAffinityActive)
690 {
691 /* FIXME: TODO */
692 DPRINT1("Affinity support disabled!\n");
693 }
694
695 /* Return the old affinity */
696 return OldAffinity;
697 }
698
699 /*
700 * @implemented
701 */
702 NTSTATUS
703 NTAPI
704 NtYieldExecution(VOID)
705 {
706 NTSTATUS Status = STATUS_NO_YIELD_PERFORMED;
707 KIRQL OldIrql;
708 PKPRCB Prcb = KeGetCurrentPrcb();
709 PKTHREAD Thread = KeGetCurrentThread(), NextThread;
710
711 /* Fail if there's no ready summary */
712 if (!Prcb->ReadySummary) return Status;
713
714 /* Raise IRQL to synch */
715 OldIrql = KeRaiseIrqlToSynchLevel();
716
717 /* Now check if there's still a ready summary */
718 if (Prcb->ReadySummary)
719 {
720 /* Acquire thread and PRCB lock */
721 KiAcquireThreadLock(Thread);
722 KiAcquirePrcbLock(Prcb);
723
724 /* Find a new thread to run if none was selected */
725 if (!Prcb->NextThread) Prcb->NextThread = KiSelectReadyThread(1, Prcb);
726
727 /* Make sure we still have a next thread to schedule */
728 NextThread = Prcb->NextThread;
729 if (NextThread)
730 {
731 /* Reset quantum and recalculate priority */
732 Thread->Quantum = Thread->QuantumReset;
733 Thread->Priority = KiComputeNewPriority(Thread, 1);
734
735 /* Release the thread lock */
736 KiReleaseThreadLock(Thread);
737
738 /* Set context swap busy */
739 KiSetThreadSwapBusy(Thread);
740
741 /* Set the new thread as running */
742 Prcb->NextThread = NULL;
743 Prcb->CurrentThread = NextThread;
744 NextThread->State = Running;
745
746 /* Setup a yield wait and queue the thread */
747 Thread->WaitReason = WrYieldExecution;
748 KxQueueReadyThread(Thread, Prcb);
749
750 /* Make it wait at APC_LEVEL */
751 Thread->WaitIrql = APC_LEVEL;
752
753 /* Sanity check */
754 ASSERT(OldIrql <= DISPATCH_LEVEL);
755
756 /* Swap to new thread */
757 KiSwapContext(APC_LEVEL, Thread);
758 Status = STATUS_SUCCESS;
759 }
760 else
761 {
762 /* Release the PRCB and thread lock */
763 KiReleasePrcbLock(Prcb);
764 KiReleaseThreadLock(Thread);
765 }
766 }
767
768 /* Lower IRQL and return */
769 KeLowerIrql(OldIrql);
770 return Status;
771 }