2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/thrdobj.c
5 * PURPOSE: Implements routines to manage the Kernel Thread Object
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
13 #include <internal/debug.h>
15 extern EX_WORK_QUEUE ExWorkerQueue
[MaximumWorkQueue
];
16 extern LIST_ENTRY PspReaperListHead
;
18 ULONG KiMask32Array
[MAXIMUM_PRIORITY
] =
20 0x1, 0x2, 0x4, 0x8, 0x10, 0x20,
21 0x40, 0x80, 0x100, 0x200, 0x4000, 0x800,
22 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000,
23 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000,
24 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000,
25 0x40000000, 0x80000000
28 /* FUNCTIONS *****************************************************************/
32 KeFindNextRightSetAffinity(IN UCHAR Number
,
38 /* Calculate the mask */
39 Bit
= (AFFINITY_MASK(Number
) - 1) & Set
;
41 /* If it's 0, use the one we got */
44 /* Now find the right set and return it */
45 BitScanReverse(&Result
, Bit
);
51 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
56 ASSERT_THREAD(Thread
);
57 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
59 /* Raise IRQL to synch level */
60 OldIrql
= KeRaiseIrqlToSynchLevel();
63 KiAcquireThreadLock(Thread
);
66 Process
= Thread
->ApcStatePointer
[0]->Process
;
68 /* Calculate the base increment */
69 BaseIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
71 /* If saturation occured, return the saturation increment instead */
72 if (Thread
->Saturation
) BaseIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
75 /* Release thread lock */
76 KiReleaseThreadLock(Thread
);
78 /* Lower IRQl and return Increment */
85 KeReadyThread(IN PKTHREAD Thread
)
88 ASSERT_THREAD(Thread
);
89 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
91 /* Lock the Dispatcher Database */
92 OldIrql
= KiAcquireDispatcherLock();
94 /* Make the thread ready */
95 KiReadyThread(Thread
);
97 /* Unlock dispatcher database */
98 KiReleaseDispatcherLock(OldIrql
);
103 KeAlertResumeThread(IN PKTHREAD Thread
)
106 KLOCK_QUEUE_HANDLE ApcLock
;
107 ASSERT_THREAD(Thread
);
108 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
110 /* Lock the Dispatcher Database and the APC Queue */
111 KiAcquireApcLock(Thread
, &ApcLock
);
112 KiAcquireDispatcherLockAtDpcLevel();
114 /* Return if Thread is already alerted. */
115 if (!Thread
->Alerted
[KernelMode
])
117 /* If it's Blocked, unblock if it we should */
118 if ((Thread
->State
== Waiting
) && (Thread
->Alertable
))
121 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
125 /* If not, simply Alert it */
126 Thread
->Alerted
[KernelMode
] = TRUE
;
130 /* Save the old Suspend Count */
131 PreviousCount
= Thread
->SuspendCount
;
133 /* If the thread is suspended, decrease one of the suspend counts */
136 /* Decrease count. If we are now zero, unwait it completely */
137 if (--Thread
->SuspendCount
)
139 /* Signal and satisfy */
140 Thread
->SuspendSemaphore
.Header
.SignalState
++;
141 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
145 /* Release Locks and return the Old State */
146 KiReleaseDispatcherLockFromDpcLevel();
147 KiReleaseApcLockFromDpcLevel(&ApcLock
);
148 KiExitDispatcher(ApcLock
.OldIrql
);
149 return PreviousCount
;
154 KeAlertThread(IN PKTHREAD Thread
,
155 IN KPROCESSOR_MODE AlertMode
)
157 BOOLEAN PreviousState
;
158 KLOCK_QUEUE_HANDLE ApcLock
;
159 ASSERT_THREAD(Thread
);
160 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
162 /* Lock the Dispatcher Database and the APC Queue */
163 KiAcquireApcLock(Thread
, &ApcLock
);
164 KiAcquireDispatcherLockAtDpcLevel();
166 /* Save the Previous State */
167 PreviousState
= Thread
->Alerted
[AlertMode
];
169 /* Check if it's already alerted */
172 /* Check if the thread is alertable, and blocked in the given mode */
173 if ((Thread
->State
== Waiting
) &&
174 ((AlertMode
== KernelMode
) || (Thread
->WaitMode
== AlertMode
)) &&
177 /* Abort the wait to alert the thread */
178 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
182 /* Otherwise, merely set the alerted state */
183 Thread
->Alerted
[AlertMode
] = TRUE
;
187 /* Release the Dispatcher Lock */
188 KiReleaseDispatcherLockFromDpcLevel();
189 KiReleaseApcLockFromDpcLevel(&ApcLock
);
190 KiExitDispatcher(ApcLock
.OldIrql
);
192 /* Return the old state */
193 return PreviousState
;
198 KeForceResumeThread(IN PKTHREAD Thread
)
200 KLOCK_QUEUE_HANDLE ApcLock
;
202 ASSERT_THREAD(Thread
);
203 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
205 /* Lock the APC Queue */
206 KiAcquireApcLock(Thread
, &ApcLock
);
208 /* Save the old Suspend Count */
209 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
211 /* If the thread is suspended, wake it up!!! */
214 /* Unwait it completely */
215 Thread
->SuspendCount
= 0;
216 Thread
->FreezeCount
= 0;
218 /* Lock the dispatcher */
219 KiAcquireDispatcherLockAtDpcLevel();
221 /* Signal and satisfy */
222 Thread
->SuspendSemaphore
.Header
.SignalState
++;
223 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
225 /* Release the dispatcher */
226 KiReleaseDispatcherLockFromDpcLevel();
229 /* Release Lock and return the Old State */
230 KiReleaseApcLockFromDpcLevel(&ApcLock
);
231 KiExitDispatcher(ApcLock
.OldIrql
);
232 return PreviousCount
;
236 * Used by the debugging code to freeze all the process's threads
237 * while the debugger is examining their state.
241 KeFreezeAllThreads(IN PKPROCESS Process
)
243 KLOCK_QUEUE_HANDLE LockHandle
, ApcLock
;
244 PKTHREAD Current
, CurrentThread
= KeGetCurrentThread();
245 PLIST_ENTRY ListHead
, NextEntry
;
247 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
249 /* Lock the process */
250 KiAcquireProcessLock(Process
, &LockHandle
);
252 /* If someone is already trying to free us, try again */
253 while (CurrentThread
->FreezeCount
)
255 /* Release and re-acquire the process lock so the APC will go through */
256 KiReleaseProcessLock(&LockHandle
);
257 KiAcquireProcessLock(Process
, &LockHandle
);
260 /* Enter a critical region */
261 KeEnterCriticalRegion();
263 /* Loop the Process's Threads */
264 ListHead
= &Process
->ThreadListHead
;
265 NextEntry
= ListHead
->Flink
;
266 while (NextEntry
!= ListHead
)
268 /* Get the current thread */
269 Current
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
272 KiAcquireApcLockAtDpcLevel(Current
, &ApcLock
);
274 /* Make sure it's not ours, and check if APCs are enabled */
275 if ((Current
!= CurrentThread
) && (Current
->ApcQueueable
))
278 OldCount
= Current
->SuspendCount
;
279 ASSERT(OldCount
!= MAXIMUM_SUSPEND_COUNT
);
281 /* Increase the freeze count */
282 Current
->FreezeCount
++;
284 /* Make sure it wasn't already suspended */
285 if (!(OldCount
) && !(Current
->SuspendCount
))
287 /* Did we already insert it? */
288 if (!Current
->SuspendApc
.Inserted
)
291 Current
->SuspendApc
.Inserted
= TRUE
;
292 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
296 /* Lock the dispatcher */
297 KiAcquireDispatcherLockAtDpcLevel();
299 /* Unsignal the semaphore, the APC was already inserted */
300 Current
->SuspendSemaphore
.Header
.SignalState
--;
302 /* Release the dispatcher */
303 KiReleaseDispatcherLockFromDpcLevel();
308 /* Release the APC lock */
309 KiReleaseApcLockFromDpcLevel(&ApcLock
);
312 /* Release the process lock and exit the dispatcher */
313 KiReleaseProcessLock(&LockHandle
);
314 KiExitDispatcher(LockHandle
.OldIrql
);
319 KeResumeThread(IN PKTHREAD Thread
)
321 KLOCK_QUEUE_HANDLE ApcLock
;
323 ASSERT_THREAD(Thread
);
324 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
326 /* Lock the APC Queue */
327 KiAcquireApcLock(Thread
, &ApcLock
);
329 /* Save the Old Count */
330 PreviousCount
= Thread
->SuspendCount
;
332 /* Check if it existed */
335 /* Decrease the suspend count */
336 Thread
->SuspendCount
--;
338 /* Check if the thrad is still suspended or not */
339 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
))
341 /* Acquire the dispatcher lock */
342 KiAcquireDispatcherLockAtDpcLevel();
344 /* Signal the Suspend Semaphore */
345 Thread
->SuspendSemaphore
.Header
.SignalState
++;
346 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
348 /* Release the dispatcher lock */
349 KiReleaseDispatcherLockFromDpcLevel();
353 /* Release APC Queue lock and return the Old State */
354 KiReleaseApcLockFromDpcLevel(&ApcLock
);
355 KiExitDispatcher(ApcLock
.OldIrql
);
356 return PreviousCount
;
361 KeRundownThread(VOID
)
364 PKTHREAD Thread
= KeGetCurrentThread();
365 PLIST_ENTRY NextEntry
, ListHead
;
368 /* Optimized path if nothing is on the list at the moment */
369 if (IsListEmpty(&Thread
->MutantListHead
)) return;
371 /* Lock the Dispatcher Database */
372 OldIrql
= KiAcquireDispatcherLock();
374 /* Get the List Pointers */
375 ListHead
= &Thread
->MutantListHead
;
376 NextEntry
= ListHead
->Flink
;
377 while (NextEntry
!= ListHead
)
380 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
382 /* Make sure it's not terminating with APCs off */
383 if (Mutant
->ApcDisable
)
385 /* Bugcheck the system */
386 KEBUGCHECKEX(0, //THREAD_TERMINATE_HELD_MUTEX,
393 /* Now we can remove it */
394 RemoveEntryList(&Mutant
->MutantListEntry
);
396 /* Unconditionally abandon it */
397 Mutant
->Header
.SignalState
= 1;
398 Mutant
->Abandoned
= TRUE
;
399 Mutant
->OwnerThread
= NULL
;
401 /* Check if the Wait List isn't empty */
402 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
404 /* Wake the Mutant */
405 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
409 NextEntry
= NextEntry
->Flink
;
412 /* Release the Lock */
413 KiReleaseDispatcherLock(OldIrql
);
418 KeStartThread(IN OUT PKTHREAD Thread
)
420 KLOCK_QUEUE_HANDLE LockHandle
;
426 UCHAR IdealProcessor
= 0;
427 PKPROCESS Process
= Thread
->ApcState
.Process
;
429 /* Setup static fields from parent */
431 Thread
->Iopl
= Process
->Iopl
;
433 Thread
->Quantum
= Process
->QuantumReset
;
434 Thread
->QuantumReset
= Process
->QuantumReset
;
435 Thread
->SystemAffinityActive
= FALSE
;
437 /* Lock the process */
438 KiAcquireProcessLock(Process
, &LockHandle
);
440 /* Setup volatile data */
441 Thread
->Priority
= Process
->BasePriority
;
442 Thread
->BasePriority
= Process
->BasePriority
;
443 Thread
->Affinity
= Process
->Affinity
;
444 Thread
->UserAffinity
= Process
->Affinity
;
447 /* Get the KNODE and its PRCB */
448 Node
= KeNodeBlock
[Process
->IdealNode
];
449 NodePrcb
= (PKPRCB
)(KPCR_BASE
+ (Process
->ThreadSeed
* PAGE_SIZE
));
451 /* Calculate affinity mask */
452 Set
= ~NodePrcb
->MultiThreadProcessorSet
;
453 Mask
= (ULONG
)(Node
->ProcessorMask
& Process
->Affinity
);
457 /* Get the new thread seed */
458 IdealProcessor
= KeFindNextRightSetAffinity(Process
->ThreadSeed
, Mask
);
459 Process
->ThreadSeed
= IdealProcessor
;
462 ASSERT((Thread
->UserAffinity
& AFFINITY_MASK(IdealProcessor
)));
465 /* Set the Ideal Processor */
466 Thread
->IdealProcessor
= IdealProcessor
;
467 Thread
->UserIdealProcessor
= IdealProcessor
;
469 /* Lock the Dispatcher Database */
470 KiAcquireDispatcherLockAtDpcLevel();
472 /* Insert the thread into the process list */
473 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
475 /* Increase the stack count */
476 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
477 Process
->StackCount
++;
479 /* Release locks and return */
480 KiReleaseDispatcherLockFromDpcLevel();
481 KiReleaseProcessLock(&LockHandle
);
486 KiSuspendRundown(IN PKAPC Apc
)
489 UNREFERENCED_PARAMETER(Apc
);
494 KiSuspendNop(IN PKAPC Apc
,
495 IN PKNORMAL_ROUTINE
*NormalRoutine
,
496 IN PVOID
*NormalContext
,
497 IN PVOID
*SystemArgument1
,
498 IN PVOID
*SystemArgument2
)
501 UNREFERENCED_PARAMETER(Apc
);
502 UNREFERENCED_PARAMETER(NormalRoutine
);
503 UNREFERENCED_PARAMETER(NormalContext
);
504 UNREFERENCED_PARAMETER(SystemArgument1
);
505 UNREFERENCED_PARAMETER(SystemArgument2
);
510 KiSuspendThread(IN PVOID NormalContext
,
511 IN PVOID SystemArgument1
,
512 IN PVOID SystemArgument2
)
514 /* Non-alertable kernel-mode suspended wait */
515 KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore
,
524 KeSuspendThread(PKTHREAD Thread
)
526 KLOCK_QUEUE_HANDLE ApcLock
;
528 ASSERT_THREAD(Thread
);
529 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
531 /* Lock the APC Queue */
532 KiAcquireApcLock(Thread
, &ApcLock
);
534 /* Save the Old Count */
535 PreviousCount
= Thread
->SuspendCount
;
537 /* Handle the maximum */
538 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
540 /* Raise an exception */
541 KiReleaseApcLock(&ApcLock
);
542 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
545 /* Should we bother to queue at all? */
546 if (Thread
->ApcQueueable
)
548 /* Increment the suspend count */
549 Thread
->SuspendCount
++;
551 /* Check if we should suspend it */
552 if (!(PreviousCount
) && !(Thread
->FreezeCount
))
554 /* Is the APC already inserted? */
555 if (!Thread
->SuspendApc
.Inserted
)
557 /* Not inserted, insert it */
558 Thread
->SuspendApc
.Inserted
= TRUE
;
559 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
563 /* Lock the dispatcher */
564 KiAcquireDispatcherLockAtDpcLevel();
566 /* Unsignal the semaphore, the APC was already inserted */
567 Thread
->SuspendSemaphore
.Header
.SignalState
--;
569 /* Release the dispatcher */
570 KiReleaseDispatcherLockFromDpcLevel();
575 /* Release Lock and return the Old State */
576 KiReleaseApcLockFromDpcLevel(&ApcLock
);
577 KiExitDispatcher(ApcLock
.OldIrql
);
578 return PreviousCount
;
583 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
585 PKTHREAD Thread
= KeGetCurrentThread();
587 KLOCK_QUEUE_HANDLE ApcLock
;
588 ASSERT_THREAD(Thread
);
589 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
591 /* Lock the Dispatcher Database and the APC Queue */
592 KiAcquireApcLock(Thread
, &ApcLock
);
593 KiAcquireDispatcherLockAtDpcLevel();
595 /* Save the old State */
596 OldState
= Thread
->Alerted
[AlertMode
];
598 /* Check the Thread is alerted */
601 /* Disable alert for this mode */
602 Thread
->Alerted
[AlertMode
] = FALSE
;
604 else if ((AlertMode
!= KernelMode
) &&
605 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
607 /* If the mode is User and the Queue isn't empty, set Pending */
608 Thread
->ApcState
.UserApcPending
= TRUE
;
611 /* Release Locks and return the Old State */
612 KiReleaseDispatcherLockFromDpcLevel();
613 KiReleaseApcLockFromDpcLevel(&ApcLock
);
614 KiExitDispatcher(ApcLock
.OldIrql
);
620 KeInitThread(IN OUT PKTHREAD Thread
,
621 IN PVOID KernelStack
,
622 IN PKSYSTEM_ROUTINE SystemRoutine
,
623 IN PKSTART_ROUTINE StartRoutine
,
624 IN PVOID StartContext
,
627 IN PKPROCESS Process
)
629 BOOLEAN AllocatedStack
= FALSE
;
631 PKWAIT_BLOCK TimerWaitBlock
;
635 /* Initalize the Dispatcher Header */
636 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
638 sizeof(KTHREAD
) / sizeof(LONG
),
641 /* Initialize the Mutant List */
642 InitializeListHead(&Thread
->MutantListHead
);
644 /* Initialize the wait blocks */
645 for (i
= 0; i
< (THREAD_WAIT_OBJECTS
+ 1); i
++)
647 /* Put our pointer */
648 Thread
->WaitBlock
[i
].Thread
= Thread
;
651 /* Set swap settings */
652 Thread
->EnableStackSwap
= FALSE
;//TRUE;
653 Thread
->IdealProcessor
= 1;
654 Thread
->SwapBusy
= FALSE
;
655 Thread
->AdjustReason
= 0;
657 /* Initialize the lock */
658 KeInitializeSpinLock(&Thread
->ThreadLock
);
660 /* Setup the Service Descriptor Table for Native Calls */
661 Thread
->ServiceTable
= KeServiceDescriptorTable
;
663 /* Setup APC Fields */
664 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
665 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
666 Thread
->ApcState
.Process
= Process
;
667 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
668 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
669 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
670 Thread
->ApcQueueable
= TRUE
;
671 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
673 /* Initialize the Suspend APC */
674 KeInitializeApc(&Thread
->SuspendApc
,
676 OriginalApcEnvironment
,
683 /* Initialize the Suspend Semaphore */
684 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 2);
686 /* Setup the timer */
687 Timer
= &Thread
->Timer
;
688 KeInitializeTimer(Timer
);
689 TimerWaitBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
690 TimerWaitBlock
->Object
= Timer
;
691 TimerWaitBlock
->WaitKey
= STATUS_TIMEOUT
;
692 TimerWaitBlock
->WaitType
= WaitAny
;
693 TimerWaitBlock
->NextWaitBlock
= NULL
;
695 /* Link the two wait lists together */
696 TimerWaitBlock
->WaitListEntry
.Flink
= &Timer
->Header
.WaitListHead
;
697 TimerWaitBlock
->WaitListEntry
.Blink
= &Timer
->Header
.WaitListHead
;
702 /* Check if we have a kernel stack */
705 /* We don't, allocate one */
706 KernelStack
= (PVOID
)((ULONG_PTR
)MmCreateKernelStack(FALSE
) +
708 if (!KernelStack
) return STATUS_INSUFFICIENT_RESOURCES
;
710 /* Remember for later */
711 AllocatedStack
= TRUE
;
714 /* Set the Thread Stacks */
715 Thread
->InitialStack
= (PCHAR
)KernelStack
;
716 Thread
->StackBase
= (PCHAR
)KernelStack
;
717 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
- KERNEL_STACK_SIZE
;
718 Thread
->KernelStackResident
= TRUE
;
721 MmUpdatePageDir((PEPROCESS
)Process
,
722 (PVOID
)Thread
->StackLimit
,
724 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
726 /* Enter SEH to avoid crashes due to user mode */
727 Status
= STATUS_SUCCESS
;
730 /* Initalize the Thread Context */
732 Ke386InitThreadWithContext(Thread
,
741 /* Set failure status */
742 Status
= STATUS_UNSUCCESSFUL
;
744 /* Check if a stack was allocated */
747 /* Delete the stack */
748 MmDeleteKernelStack(Thread
->StackBase
, FALSE
);
749 Thread
->InitialStack
= NULL
;
754 /* Set the Thread to initalized */
755 Thread
->State
= Initialized
;
761 KeInitializeThread(IN PKPROCESS Process
,
762 IN OUT PKTHREAD Thread
,
763 IN PKSYSTEM_ROUTINE SystemRoutine
,
764 IN PKSTART_ROUTINE StartRoutine
,
765 IN PVOID StartContext
,
768 IN PVOID KernelStack
)
770 /* Initailize and start the thread on success */
771 if (NT_SUCCESS(KeInitThread(Thread
,
781 KeStartThread(Thread
);
787 KeUninitThread(IN PKTHREAD Thread
)
789 /* Delete the stack */
790 MmDeleteKernelStack(Thread
->StackBase
, FALSE
);
791 Thread
->InitialStack
= NULL
;
794 /* PUBLIC FUNCTIONS **********************************************************/
801 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
807 IN PVOID ThreadState
)
815 #undef KeGetCurrentThread
818 KeGetCurrentThread(VOID
)
821 /* Return the current thread on this PCR */
822 return ((PKIPCR
)KeGetPcr())->PrcbData
.CurrentThread
;
823 #elif defined(_M_PPC)
824 return KeGetCurrentKPCR()->Prcb
->CurrentThread
;
833 KeQueryRuntimeThread(IN PKTHREAD Thread
,
836 ASSERT_THREAD(Thread
);
838 /* Return the User Time */
839 *UserTime
= Thread
->UserTime
;
841 /* Return the Kernel Time */
842 return Thread
->KernelTime
;
850 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
852 BOOLEAN PreviousState
;
853 PKTHREAD Thread
= KeGetCurrentThread();
856 PreviousState
= Thread
->EnableStackSwap
;
859 Thread
->EnableStackSwap
= Enable
;
861 /* Return Old State */
862 return PreviousState
;
870 KeQueryPriorityThread(IN PKTHREAD Thread
)
872 ASSERT_THREAD(Thread
);
874 /* Return the current priority */
875 return Thread
->Priority
;
883 KeRevertToUserAffinityThread(VOID
)
887 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
888 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
889 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
891 /* Lock the Dispatcher Database */
892 OldIrql
= KiAcquireDispatcherLock();
894 /* Set the user affinity and processor and disable system affinity */
895 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
896 CurrentThread
->IdealProcessor
= CurrentThread
->UserIdealProcessor
;
897 CurrentThread
->SystemAffinityActive
= FALSE
;
899 /* Get the current PRCB and check if it doesn't match this affinity */
900 Prcb
= KeGetCurrentPrcb();
901 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
904 KiAcquirePrcbLock(Prcb
);
907 /* Check if there's no next thread scheduled */
908 if (!Prcb
->NextThread
)
910 /* Select a new thread and set it on standby */
911 NextThread
= KiSelectNextThread(Prcb
);
912 NextThread
->State
= Standby
;
913 Prcb
->NextThread
= NextThread
;
916 /* We need to dispatch a new thread */
918 CurrentThread
->WaitIrql
= OldIrql
;
919 KiDispatchThreadNoLock(Ready
);
920 KeLowerIrql(OldIrql
);
924 /* Release the PRCB lock */
925 KiReleasePrcbLock(Prcb
);
928 /* Unlock dispatcher database */
929 KiReleaseDispatcherLock(OldIrql
);
937 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
940 CCHAR OldIdealProcessor
;
942 ASSERT(Processor
<= MAXIMUM_PROCESSORS
);
944 /* Lock the Dispatcher Database */
945 OldIrql
= KiAcquireDispatcherLock();
947 /* Save Old Ideal Processor */
948 OldIdealProcessor
= Thread
->UserIdealProcessor
;
950 /* Make sure a valid CPU was given */
951 if (Processor
< MAXIMUM_PROCESSORS
)
953 /* Check if the user ideal CPU is in the affinity */
954 if (Thread
->UserIdealProcessor
& AFFINITY_MASK(Processor
))
956 /* Set the ideal processor */
957 Thread
->IdealProcessor
= Processor
;
959 /* Check if system affinity is used */
960 if (!Thread
->SystemAffinityActive
)
962 /* It's not, so update the user CPU too */
963 Thread
->UserIdealProcessor
= Processor
;
968 /* Release dispatcher lock and return the old ideal CPU */
969 KiReleaseDispatcherLock(OldIrql
);
970 return OldIdealProcessor
;
978 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
982 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
983 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
984 ASSERT((Affinity
& KeActiveProcessors
) != 0);
986 /* Lock the Dispatcher Database */
987 OldIrql
= KiAcquireDispatcherLock();
989 /* Restore the affinity and enable system affinity */
990 CurrentThread
->Affinity
= Affinity
;
991 CurrentThread
->SystemAffinityActive
= TRUE
;
993 /* Check if the ideal processor is part of the affinity */
995 if (!(Affinity
& AFFINITY_MASK(CurrentThread
->IdealProcessor
)))
997 ULONG AffinitySet
, NodeMask
;
999 /* It's not! Get the PRCB */
1000 Prcb
= KiProcessorBlock
[CurrentThread
->IdealProcessor
];
1002 /* Calculate the affinity set */
1003 AffinitySet
= KeActiveProcessors
& Affinity
;
1004 NodeMask
= Prcb
->ParentNode
->ProcessorMask
& AffinitySet
;
1007 /* Use the Node set instead */
1008 AffinitySet
= NodeMask
;
1011 /* Calculate the ideal CPU from the affinity set */
1012 BitScanReverse(&NodeMask
, AffinitySet
);
1013 CurrentThread
->IdealProcessor
= (UCHAR
)NodeMask
;
1017 /* Get the current PRCB and check if it doesn't match this affinity */
1018 Prcb
= KeGetCurrentPrcb();
1019 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
1022 KiAcquirePrcbLock(Prcb
);
1024 #ifdef NEW_SCHEDULER
1025 /* Check if there's no next thread scheduled */
1026 if (!Prcb
->NextThread
)
1028 /* Select a new thread and set it on standby */
1029 NextThread
= KiSelectNextThread(Prcb
);
1030 NextThread
->State
= Standby
;
1031 Prcb
->NextThread
= NextThread
;
1034 /* We need to dispatch a new thread */
1036 CurrentThread
->WaitIrql
= OldIrql
;
1037 KiDispatchThreadNoLock(Ready
);
1038 KeLowerIrql(OldIrql
);
1042 /* Release the PRCB lock */
1043 KiReleasePrcbLock(Prcb
);
1046 /* Unlock dispatcher database */
1047 KiReleaseDispatcherLock(OldIrql
);
1055 KeSetBasePriorityThread(IN PKTHREAD Thread
,
1059 KPRIORITY OldBasePriority
, Priority
, BasePriority
;
1063 ASSERT_THREAD(Thread
);
1064 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1066 /* Get the process */
1067 Process
= Thread
->ApcState
.Process
;
1069 /* Lock the Dispatcher Database */
1070 OldIrql
= KiAcquireDispatcherLock();
1072 /* Lock the thread */
1073 KiAcquireThreadLock(Thread
);
1075 /* Save the old base priority and increment */
1076 OldBasePriority
= Thread
->BasePriority
;
1077 OldIncrement
= OldBasePriority
- Process
->BasePriority
;
1079 /* If priority saturation happened, use the saturated increment */
1080 if (Thread
->Saturation
) OldIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1083 /* Now check if saturation is being used for the new value */
1084 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1086 /* Check if we need positive or negative saturation */
1087 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1090 /* Normalize the Base Priority */
1091 BasePriority
= Process
->BasePriority
+ Increment
;
1092 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1094 /* Check if it's too low */
1095 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1097 /* Set it to the lowest real time level */
1098 BasePriority
= LOW_REALTIME_PRIORITY
;
1101 /* Check if it's too high */
1102 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1104 /* We are at real time, so use the raw base priority */
1105 Priority
= BasePriority
;
1109 /* Check if it's entering the real time range */
1110 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1112 /* Set it to the highest dynamic level */
1113 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1116 /* Check if it's too low and normalize it */
1117 if (BasePriority
<= LOW_PRIORITY
) BasePriority
= 1;
1119 /* Check if Saturation is used */
1120 if (Thread
->Saturation
)
1122 /* Use the raw base priority */
1123 Priority
= BasePriority
;
1127 /* Otherwise, calculate the new priority */
1128 Priority
= KiComputeNewPriority(Thread
);
1130 /* Check if it entered the real-time range */
1131 if (Priority
>= LOW_REALTIME_PRIORITY
)
1133 /* Normalize it down to the highest dynamic priority */
1134 Priority
= LOW_REALTIME_PRIORITY
- 1;
1139 /* Finally set the new base priority */
1140 Thread
->BasePriority
= (SCHAR
)BasePriority
;
1142 /* Reset the decrements */
1143 Thread
->PriorityDecrement
= 0;
1145 /* Check if we're changing priority after all */
1146 if (Priority
!= Thread
->Priority
)
1148 /* Reset the quantum and do the actual priority modification */
1149 Thread
->Quantum
= Thread
->QuantumReset
;
1150 KiSetPriorityThread(Thread
, Priority
, &Released
);
1153 /* Release thread lock */
1154 KiReleaseThreadLock(Thread
);
1156 /* Check if lock was released */
1159 /* Release the dispatcher database */
1160 KiReleaseDispatcherLock(OldIrql
);
1164 /* Lower IRQL only */
1165 KeLowerIrql(OldIrql
);
1168 /* Return old increment */
1169 return OldIncrement
;
1177 KeSetAffinityThread(IN PKTHREAD Thread
,
1178 IN KAFFINITY Affinity
)
1181 KAFFINITY OldAffinity
;
1183 ASSERT_THREAD(Thread
);
1184 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1186 /* Lock the dispatcher database */
1187 OldIrql
= KiAcquireDispatcherLock();
1189 /* Call the internal function */
1190 OldAffinity
= KiSetAffinityThread(Thread
, Affinity
, &Released
);
1192 /* Check if lock was released */
1195 /* Release the dispatcher database */
1196 KiReleaseDispatcherLock(OldIrql
);
1200 /* Lower IRQL only */
1201 KeLowerIrql(OldIrql
);
1204 /* Return old affinity */
1213 KeSetPriorityThread(IN PKTHREAD Thread
,
1214 IN KPRIORITY Priority
)
1217 KPRIORITY OldPriority
;
1219 ASSERT_THREAD(Thread
);
1220 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1221 ASSERT((Priority
<= HIGH_PRIORITY
) && (Priority
>= LOW_PRIORITY
));
1223 /* Lock the Dispatcher Database */
1224 OldIrql
= KiAcquireDispatcherLock();
1226 /* Lock the thread */
1227 KiAcquireThreadLock(Thread
);
1229 /* Save the old Priority */
1230 OldPriority
= Thread
->Priority
;
1232 /* Make sure that an actual change is being done */
1233 if (OldPriority
!= Priority
)
1235 /* Reset the Quantum and Decrements */
1236 Thread
->Quantum
= Thread
->QuantumReset
;
1237 Thread
->PriorityDecrement
= 0;
1239 /* Set the new Priority */
1240 KiSetPriorityThread(Thread
, Priority
, &Released
);
1243 /* Release thread lock */
1244 KiReleaseThreadLock(Thread
);
1246 /* Check if lock was released */
1249 /* Release the dispatcher database */
1250 KiReleaseDispatcherLock(OldIrql
);
1254 /* Lower IRQL only */
1255 KeLowerIrql(OldIrql
);
1258 /* Return Old Priority */
1267 KeTerminateThread(IN KPRIORITY Increment
)
1269 PLIST_ENTRY
*ListHead
;
1270 PETHREAD Entry
, SavedEntry
;
1271 PETHREAD
*ThreadAddr
;
1272 KLOCK_QUEUE_HANDLE LockHandle
;
1273 PKTHREAD Thread
= KeGetCurrentThread();
1274 PKPROCESS Process
= Thread
->ApcState
.Process
;
1275 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1277 /* Lock the process */
1278 KiAcquireProcessLock(Process
, &LockHandle
);
1280 /* Make sure we won't get Swapped */
1281 KiSetThreadSwapBusy(Thread
);
1283 /* Save the Kernel and User Times */
1284 Process
->KernelTime
+= Thread
->KernelTime
;
1285 Process
->UserTime
+= Thread
->UserTime
;
1287 /* Get the current entry and our Port */
1288 Entry
= (PETHREAD
)PspReaperListHead
.Flink
;
1289 ThreadAddr
= &((PETHREAD
)Thread
)->ReaperLink
;
1291 /* Add it to the reaper's list */
1294 /* Get the list head */
1295 ListHead
= &PspReaperListHead
.Flink
;
1297 /* Link ourselves */
1298 *ThreadAddr
= Entry
;
1301 /* Now try to do the exchange */
1302 Entry
= InterlockedCompareExchangePointer(ListHead
, ThreadAddr
, Entry
);
1304 /* Break out if the change was succesful */
1305 } while (Entry
!= SavedEntry
);
1307 /* Acquire the dispatcher lock */
1308 KiAcquireDispatcherLockAtDpcLevel();
1310 /* Check if the reaper wasn't active */
1313 /* Activate it as a work item, directly through its Queue */
1314 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1315 &PspReaperWorkItem
.List
,
1319 /* Check the thread has an associated queue */
1322 /* Remove it from the list, and handle the queue */
1323 RemoveEntryList(&Thread
->QueueListEntry
);
1324 KiActivateWaiterQueue(Thread
->Queue
);
1327 /* Signal the thread */
1328 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1329 if (!IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
))
1331 /* Unwait the threads */
1332 KxUnwaitThread(&Thread
->DispatcherHeader
, Increment
);
1335 /* Remove the thread from the list */
1336 RemoveEntryList(&Thread
->ThreadListEntry
);
1338 /* Release the process lock */
1339 KiReleaseProcessLockFromDpcLevel(&LockHandle
);
1341 /* Set us as terminated, decrease the Process's stack count */
1342 Thread
->State
= Terminated
;
1344 /* Decrease stack count */
1345 ASSERT(Process
->StackCount
!= 0);
1346 ASSERT(Process
->State
== ProcessInMemory
);
1347 Process
->StackCount
--;
1348 if (!Process
->StackCount
)
1350 /* FIXME: Swap stacks */
1353 /* Rundown arch-specific parts */
1354 KiRundownThread(Thread
);
1356 /* Swap to a new thread */
1357 KiReleaseDispatcherLockFromDpcLevel();
1358 KiSwapThread(Thread
, KeGetCurrentPrcb());