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 ******************************************************************/
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, 0x400, 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 KeReadStateThread(IN PKTHREAD Thread
)
53 ASSERT_THREAD(Thread
);
55 /* Return signal state */
56 return (BOOLEAN
)Thread
->Header
.SignalState
;
61 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
66 ASSERT_THREAD(Thread
);
67 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
69 /* Raise IRQL to synch level */
70 OldIrql
= KeRaiseIrqlToSynchLevel();
73 KiAcquireThreadLock(Thread
);
76 Process
= Thread
->ApcStatePointer
[0]->Process
;
78 /* Calculate the base increment */
79 BaseIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
81 /* If saturation occured, return the saturation increment instead */
82 if (Thread
->Saturation
) BaseIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
85 /* Release thread lock */
86 KiReleaseThreadLock(Thread
);
88 /* Lower IRQl and return Increment */
95 KeSetDisableBoostThread(IN OUT PKTHREAD Thread
,
98 ASSERT_THREAD(Thread
);
100 /* Check if we're enabling or disabling */
104 return InterlockedBitTestAndSet(&Thread
->ThreadFlags
, 1);
109 return InterlockedBitTestAndReset(&Thread
->ThreadFlags
, 1);
115 KeReadyThread(IN PKTHREAD Thread
)
118 ASSERT_THREAD(Thread
);
119 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
121 /* Lock the Dispatcher Database */
122 OldIrql
= KiAcquireDispatcherLock();
124 /* Make the thread ready */
125 KiReadyThread(Thread
);
127 /* Unlock dispatcher database */
128 KiReleaseDispatcherLock(OldIrql
);
133 KeAlertResumeThread(IN PKTHREAD Thread
)
136 KLOCK_QUEUE_HANDLE ApcLock
;
137 ASSERT_THREAD(Thread
);
138 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
140 /* Lock the Dispatcher Database and the APC Queue */
141 KiAcquireApcLockRaiseToSynch(Thread
, &ApcLock
);
142 KiAcquireDispatcherLockAtSynchLevel();
144 /* Return if Thread is already alerted. */
145 if (!Thread
->Alerted
[KernelMode
])
147 /* If it's Blocked, unblock if it we should */
148 if ((Thread
->State
== Waiting
) && (Thread
->Alertable
))
151 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
155 /* If not, simply Alert it */
156 Thread
->Alerted
[KernelMode
] = TRUE
;
160 /* Save the old Suspend Count */
161 PreviousCount
= Thread
->SuspendCount
;
163 /* If the thread is suspended, decrease one of the suspend counts */
166 /* Decrease count. If we are now zero, unwait it completely */
167 Thread
->SuspendCount
--;
168 if (!(Thread
->SuspendCount
) && !(Thread
->FreezeCount
))
170 /* Signal and satisfy */
171 Thread
->SuspendSemaphore
.Header
.SignalState
++;
172 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
176 /* Release Locks and return the Old State */
177 KiReleaseDispatcherLockFromSynchLevel();
178 KiReleaseApcLockFromSynchLevel(&ApcLock
);
179 KiExitDispatcher(ApcLock
.OldIrql
);
180 return PreviousCount
;
185 KeAlertThread(IN PKTHREAD Thread
,
186 IN KPROCESSOR_MODE AlertMode
)
188 BOOLEAN PreviousState
;
189 KLOCK_QUEUE_HANDLE ApcLock
;
190 ASSERT_THREAD(Thread
);
191 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
193 /* Lock the Dispatcher Database and the APC Queue */
194 KiAcquireApcLockRaiseToSynch(Thread
, &ApcLock
);
195 KiAcquireDispatcherLockAtSynchLevel();
197 /* Save the Previous State */
198 PreviousState
= Thread
->Alerted
[AlertMode
];
200 /* Check if it's already alerted */
203 /* Check if the thread is alertable, and blocked in the given mode */
204 if ((Thread
->State
== Waiting
) &&
205 (Thread
->Alertable
) &&
206 (AlertMode
<= Thread
->WaitMode
))
208 /* Abort the wait to alert the thread */
209 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
213 /* Otherwise, merely set the alerted state */
214 Thread
->Alerted
[AlertMode
] = TRUE
;
218 /* Release the Dispatcher Lock */
219 KiReleaseDispatcherLockFromSynchLevel();
220 KiReleaseApcLockFromSynchLevel(&ApcLock
);
221 KiExitDispatcher(ApcLock
.OldIrql
);
223 /* Return the old state */
224 return PreviousState
;
229 KeBoostPriorityThread(IN PKTHREAD Thread
,
230 IN KPRIORITY Increment
)
234 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
236 /* Lock the Dispatcher Database */
237 OldIrql
= KiAcquireDispatcherLock();
239 /* Only threads in the dynamic range get boosts */
240 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
)
242 /* Lock the thread */
243 KiAcquireThreadLock(Thread
);
245 /* Check again, and make sure there's not already a boost */
246 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
247 !(Thread
->PriorityDecrement
))
249 /* Compute the new priority and see if it's higher */
250 Priority
= Thread
->BasePriority
+ Increment
;
251 if (Priority
> Thread
->Priority
)
253 if (Priority
>= LOW_REALTIME_PRIORITY
)
255 Priority
= LOW_REALTIME_PRIORITY
- 1;
258 /* Reset the quantum */
259 Thread
->Quantum
= Thread
->QuantumReset
;
261 /* Set the new Priority */
262 KiSetPriorityThread(Thread
, Priority
);
266 /* Release thread lock */
267 KiReleaseThreadLock(Thread
);
270 /* Release the dispatcher lokc */
271 KiReleaseDispatcherLock(OldIrql
);
276 KeForceResumeThread(IN PKTHREAD Thread
)
278 KLOCK_QUEUE_HANDLE ApcLock
;
280 ASSERT_THREAD(Thread
);
281 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
283 /* Lock the APC Queue */
284 KiAcquireApcLockRaiseToSynch(Thread
, &ApcLock
);
286 /* Save the old Suspend Count */
287 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
289 /* If the thread is suspended, wake it up!!! */
292 /* Unwait it completely */
293 Thread
->SuspendCount
= 0;
294 Thread
->FreezeCount
= 0;
296 /* Lock the dispatcher */
297 KiAcquireDispatcherLockAtSynchLevel();
299 /* Signal and satisfy */
300 Thread
->SuspendSemaphore
.Header
.SignalState
++;
301 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
303 /* Release the dispatcher */
304 KiReleaseDispatcherLockFromSynchLevel();
307 /* Release Lock and return the Old State */
308 KiReleaseApcLockFromSynchLevel(&ApcLock
);
309 KiExitDispatcher(ApcLock
.OldIrql
);
310 return PreviousCount
;
315 KeFreezeAllThreads(VOID
)
317 KLOCK_QUEUE_HANDLE LockHandle
, ApcLock
;
318 PKTHREAD Current
, CurrentThread
= KeGetCurrentThread();
319 PKPROCESS Process
= CurrentThread
->ApcState
.Process
;
320 PLIST_ENTRY ListHead
, NextEntry
;
322 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
324 /* Lock the process */
325 KiAcquireProcessLockRaiseToSynch(Process
, &LockHandle
);
327 /* If someone is already trying to free us, try again */
328 while (CurrentThread
->FreezeCount
)
330 /* Release and re-acquire the process lock so the APC will go through */
331 KiReleaseProcessLock(&LockHandle
);
332 KiAcquireProcessLockRaiseToSynch(Process
, &LockHandle
);
335 /* Enter a critical region */
336 KeEnterCriticalRegion();
338 /* Loop the Process's Threads */
339 ListHead
= &Process
->ThreadListHead
;
340 NextEntry
= ListHead
->Flink
;
343 /* Get the current thread */
344 Current
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
347 KiAcquireApcLockAtSynchLevel(Current
, &ApcLock
);
349 /* Make sure it's not ours, and check if APCs are enabled */
350 if ((Current
!= CurrentThread
) && (Current
->ApcQueueable
))
353 OldCount
= Current
->SuspendCount
;
354 ASSERT(OldCount
!= MAXIMUM_SUSPEND_COUNT
);
356 /* Increase the freeze count */
357 Current
->FreezeCount
++;
359 /* Make sure it wasn't already suspended */
360 if (!(OldCount
) && !(Current
->SuspendCount
))
362 /* Did we already insert it? */
363 if (!Current
->SuspendApc
.Inserted
)
366 Current
->SuspendApc
.Inserted
= TRUE
;
367 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
371 /* Lock the dispatcher */
372 KiAcquireDispatcherLockAtSynchLevel();
374 /* Unsignal the semaphore, the APC was already inserted */
375 Current
->SuspendSemaphore
.Header
.SignalState
--;
377 /* Release the dispatcher */
378 KiReleaseDispatcherLockFromSynchLevel();
383 /* Release the APC lock */
384 KiReleaseApcLockFromSynchLevel(&ApcLock
);
386 /* Move to the next thread */
387 NextEntry
= NextEntry
->Flink
;
388 } while (NextEntry
!= ListHead
);
390 /* Release the process lock and exit the dispatcher */
391 KiReleaseProcessLockFromSynchLevel(&LockHandle
);
392 KiExitDispatcher(LockHandle
.OldIrql
);
397 KeResumeThread(IN PKTHREAD Thread
)
399 KLOCK_QUEUE_HANDLE ApcLock
;
401 ASSERT_THREAD(Thread
);
402 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
404 /* Lock the APC Queue */
405 KiAcquireApcLockRaiseToSynch(Thread
, &ApcLock
);
407 /* Save the Old Count */
408 PreviousCount
= Thread
->SuspendCount
;
410 /* Check if it existed */
413 /* Decrease the suspend count */
414 Thread
->SuspendCount
--;
416 /* Check if the thrad is still suspended or not */
417 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
))
419 /* Acquire the dispatcher lock */
420 KiAcquireDispatcherLockAtSynchLevel();
422 /* Signal the Suspend Semaphore */
423 Thread
->SuspendSemaphore
.Header
.SignalState
++;
424 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
426 /* Release the dispatcher lock */
427 KiReleaseDispatcherLockFromSynchLevel();
431 /* Release APC Queue lock and return the Old State */
432 KiReleaseApcLockFromSynchLevel(&ApcLock
);
433 KiExitDispatcher(ApcLock
.OldIrql
);
434 return PreviousCount
;
439 KeRundownThread(VOID
)
442 PKTHREAD Thread
= KeGetCurrentThread();
443 PLIST_ENTRY NextEntry
, ListHead
;
445 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
447 /* Optimized path if nothing is on the list at the moment */
448 if (IsListEmpty(&Thread
->MutantListHead
)) return;
450 /* Lock the Dispatcher Database */
451 OldIrql
= KiAcquireDispatcherLock();
453 /* Get the List Pointers */
454 ListHead
= &Thread
->MutantListHead
;
455 NextEntry
= ListHead
->Flink
;
456 while (NextEntry
!= ListHead
)
459 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
460 ASSERT_MUTANT(Mutant
);
462 /* Make sure it's not terminating with APCs off */
463 if (Mutant
->ApcDisable
)
465 /* Bugcheck the system */
466 KeBugCheckEx(THREAD_TERMINATE_HELD_MUTEX
,
473 /* Now we can remove it */
474 RemoveEntryList(&Mutant
->MutantListEntry
);
476 /* Unconditionally abandon it */
477 Mutant
->Header
.SignalState
= 1;
478 Mutant
->Abandoned
= TRUE
;
479 Mutant
->OwnerThread
= NULL
;
481 /* Check if the Wait List isn't empty */
482 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
484 /* Wake the Mutant */
485 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
489 NextEntry
= Thread
->MutantListHead
.Flink
;
492 /* Release the Lock */
493 KiReleaseDispatcherLock(OldIrql
);
498 KeStartThread(IN OUT PKTHREAD Thread
)
500 KLOCK_QUEUE_HANDLE LockHandle
;
506 UCHAR IdealProcessor
= 0;
507 PKPROCESS Process
= Thread
->ApcState
.Process
;
509 /* Setup static fields from parent */
510 Thread
->DisableBoost
= Process
->DisableBoost
;
512 Thread
->Iopl
= Process
->Iopl
;
514 Thread
->Quantum
= Process
->QuantumReset
;
515 Thread
->QuantumReset
= Process
->QuantumReset
;
516 Thread
->SystemAffinityActive
= FALSE
;
518 /* Lock the process */
519 KiAcquireProcessLockRaiseToSynch(Process
, &LockHandle
);
521 /* Setup volatile data */
522 Thread
->Priority
= Process
->BasePriority
;
523 Thread
->BasePriority
= Process
->BasePriority
;
524 Thread
->Affinity
= Process
->Affinity
;
525 Thread
->UserAffinity
= Process
->Affinity
;
528 /* Get the KNODE and its PRCB */
529 Node
= KeNodeBlock
[Process
->IdealNode
];
530 NodePrcb
= KiProcessorBlock
[Process
->ThreadSeed
];
532 /* Calculate affinity mask */
537 Set
= ~NodePrcb
->MultiThreadProcessorSet
;
539 Mask
= (ULONG
)(Node
->ProcessorMask
& Process
->Affinity
);
543 /* Get the new thread seed */
544 IdealProcessor
= KeFindNextRightSetAffinity(Process
->ThreadSeed
, Mask
);
545 Process
->ThreadSeed
= IdealProcessor
;
548 ASSERT((Thread
->UserAffinity
& AFFINITY_MASK(IdealProcessor
)));
551 /* Set the Ideal Processor */
552 Thread
->IdealProcessor
= IdealProcessor
;
553 Thread
->UserIdealProcessor
= IdealProcessor
;
555 /* Lock the Dispatcher Database */
556 KiAcquireDispatcherLockAtSynchLevel();
558 /* Insert the thread into the process list */
559 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
561 /* Increase the stack count */
562 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
563 Process
->StackCount
++;
565 /* Release locks and return */
566 KiReleaseDispatcherLockFromSynchLevel();
567 KiReleaseProcessLock(&LockHandle
);
572 KiSuspendRundown(IN PKAPC Apc
)
575 UNREFERENCED_PARAMETER(Apc
);
580 KiSuspendNop(IN PKAPC Apc
,
581 IN PKNORMAL_ROUTINE
*NormalRoutine
,
582 IN PVOID
*NormalContext
,
583 IN PVOID
*SystemArgument1
,
584 IN PVOID
*SystemArgument2
)
587 UNREFERENCED_PARAMETER(Apc
);
588 UNREFERENCED_PARAMETER(NormalRoutine
);
589 UNREFERENCED_PARAMETER(NormalContext
);
590 UNREFERENCED_PARAMETER(SystemArgument1
);
591 UNREFERENCED_PARAMETER(SystemArgument2
);
596 KiSuspendThread(IN PVOID NormalContext
,
597 IN PVOID SystemArgument1
,
598 IN PVOID SystemArgument2
)
600 /* Non-alertable kernel-mode suspended wait */
601 KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore
,
610 KeSuspendThread(PKTHREAD Thread
)
612 KLOCK_QUEUE_HANDLE ApcLock
;
614 ASSERT_THREAD(Thread
);
615 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
617 /* Lock the APC Queue */
618 KiAcquireApcLockRaiseToSynch(Thread
, &ApcLock
);
620 /* Save the Old Count */
621 PreviousCount
= Thread
->SuspendCount
;
623 /* Handle the maximum */
624 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
626 /* Raise an exception */
627 KiReleaseApcLock(&ApcLock
);
628 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
631 /* Should we bother to queue at all? */
632 if (Thread
->ApcQueueable
)
634 /* Increment the suspend count */
635 Thread
->SuspendCount
++;
637 /* Check if we should suspend it */
638 if (!(PreviousCount
) && !(Thread
->FreezeCount
))
640 /* Is the APC already inserted? */
641 if (!Thread
->SuspendApc
.Inserted
)
643 /* Not inserted, insert it */
644 Thread
->SuspendApc
.Inserted
= TRUE
;
645 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
649 /* Lock the dispatcher */
650 KiAcquireDispatcherLockAtSynchLevel();
652 /* Unsignal the semaphore, the APC was already inserted */
653 Thread
->SuspendSemaphore
.Header
.SignalState
--;
655 /* Release the dispatcher */
656 KiReleaseDispatcherLockFromSynchLevel();
661 /* Release Lock and return the Old State */
662 KiReleaseApcLockFromSynchLevel(&ApcLock
);
663 KiExitDispatcher(ApcLock
.OldIrql
);
664 return PreviousCount
;
669 KeThawAllThreads(VOID
)
671 KLOCK_QUEUE_HANDLE LockHandle
, ApcLock
;
672 PKTHREAD Current
, CurrentThread
= KeGetCurrentThread();
673 PKPROCESS Process
= CurrentThread
->ApcState
.Process
;
674 PLIST_ENTRY ListHead
, NextEntry
;
676 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
678 /* Lock the process */
679 KiAcquireProcessLockRaiseToSynch(Process
, &LockHandle
);
681 /* Loop the Process's Threads */
682 ListHead
= &Process
->ThreadListHead
;
683 NextEntry
= ListHead
->Flink
;
686 /* Get the current thread */
687 Current
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
690 KiAcquireApcLockAtSynchLevel(Current
, &ApcLock
);
692 /* Make sure we are frozen */
693 OldCount
= Current
->FreezeCount
;
696 /* Decrease the freeze count */
697 Current
->FreezeCount
--;
699 /* Check if both counts are zero now */
700 if (!(Current
->SuspendCount
) && (!Current
->FreezeCount
))
702 /* Lock the dispatcher */
703 KiAcquireDispatcherLockAtSynchLevel();
705 /* Signal the suspend semaphore and wake it */
706 Current
->SuspendSemaphore
.Header
.SignalState
++;
707 KiWaitTest(&Current
->SuspendSemaphore
, 0);
709 /* Unlock the dispatcher */
710 KiReleaseDispatcherLockFromSynchLevel();
714 /* Release the APC lock */
715 KiReleaseApcLockFromSynchLevel(&ApcLock
);
717 /* Go to the next one */
718 NextEntry
= NextEntry
->Flink
;
719 } while (NextEntry
!= ListHead
);
721 /* Release the process lock and exit the dispatcher */
722 KiReleaseProcessLockFromSynchLevel(&LockHandle
);
723 KiExitDispatcher(LockHandle
.OldIrql
);
725 /* Leave the critical region */
726 KeLeaveCriticalRegion();
731 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
733 PKTHREAD Thread
= KeGetCurrentThread();
735 KLOCK_QUEUE_HANDLE ApcLock
;
736 ASSERT_THREAD(Thread
);
737 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
739 /* Lock the Dispatcher Database and the APC Queue */
740 KiAcquireApcLockRaiseToSynch(Thread
, &ApcLock
);
742 /* Save the old State */
743 OldState
= Thread
->Alerted
[AlertMode
];
745 /* Check the Thread is alerted */
748 /* Disable alert for this mode */
749 Thread
->Alerted
[AlertMode
] = FALSE
;
751 else if ((AlertMode
!= KernelMode
) &&
752 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
754 /* If the mode is User and the Queue isn't empty, set Pending */
755 Thread
->ApcState
.UserApcPending
= TRUE
;
758 /* Release Locks and return the Old State */
759 KiReleaseApcLock(&ApcLock
);
765 KeInitThread(IN OUT PKTHREAD Thread
,
766 IN PVOID KernelStack
,
767 IN PKSYSTEM_ROUTINE SystemRoutine
,
768 IN PKSTART_ROUTINE StartRoutine
,
769 IN PVOID StartContext
,
772 IN PKPROCESS Process
)
774 BOOLEAN AllocatedStack
= FALSE
;
776 PKWAIT_BLOCK TimerWaitBlock
;
780 /* Initialize the Dispatcher Header */
781 Thread
->Header
.Type
= ThreadObject
;
782 Thread
->Header
.ThreadControlFlags
= 0;
783 Thread
->Header
.DebugActive
= FALSE
;
784 Thread
->Header
.SignalState
= 0;
785 InitializeListHead(&(Thread
->Header
.WaitListHead
));
787 /* Initialize the Mutant List */
788 InitializeListHead(&Thread
->MutantListHead
);
790 /* Initialize the wait blocks */
791 for (i
= 0; i
< (THREAD_WAIT_OBJECTS
+ 1); i
++)
793 /* Put our pointer */
794 Thread
->WaitBlock
[i
].Thread
= Thread
;
797 /* Set swap settings */
798 Thread
->EnableStackSwap
= TRUE
;
799 Thread
->IdealProcessor
= 1;
800 Thread
->SwapBusy
= FALSE
;
801 Thread
->KernelStackResident
= TRUE
;
802 Thread
->AdjustReason
= AdjustNone
;
804 /* Initialize the lock */
805 KeInitializeSpinLock(&Thread
->ThreadLock
);
807 /* Setup the Service Descriptor Table for Native Calls */
808 Thread
->ServiceTable
= KeServiceDescriptorTable
;
810 /* Setup APC Fields */
811 InitializeListHead(&Thread
->ApcState
.ApcListHead
[KernelMode
]);
812 InitializeListHead(&Thread
->ApcState
.ApcListHead
[UserMode
]);
813 Thread
->ApcState
.Process
= Process
;
814 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
815 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
816 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
817 Thread
->ApcQueueable
= TRUE
;
818 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
820 /* Initialize the Suspend APC */
821 KeInitializeApc(&Thread
->SuspendApc
,
823 OriginalApcEnvironment
,
830 /* Initialize the Suspend Semaphore */
831 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 2);
833 /* Setup the timer */
834 Timer
= &Thread
->Timer
;
835 KeInitializeTimer(Timer
);
836 TimerWaitBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
837 TimerWaitBlock
->Object
= Timer
;
838 TimerWaitBlock
->WaitKey
= STATUS_TIMEOUT
;
839 TimerWaitBlock
->WaitType
= WaitAny
;
840 TimerWaitBlock
->NextWaitBlock
= NULL
;
842 /* Link the two wait lists together */
843 TimerWaitBlock
->WaitListEntry
.Flink
= &Timer
->Header
.WaitListHead
;
844 TimerWaitBlock
->WaitListEntry
.Blink
= &Timer
->Header
.WaitListHead
;
846 /* Set the TEB and process */
848 Thread
->Process
= Process
;
850 /* Check if we have a kernel stack */
853 /* We don't, allocate one */
854 KernelStack
= MmCreateKernelStack(FALSE
, 0);
855 if (!KernelStack
) return STATUS_INSUFFICIENT_RESOURCES
;
857 /* Remember for later */
858 AllocatedStack
= TRUE
;
861 /* Set the Thread Stacks */
862 Thread
->InitialStack
= KernelStack
;
863 Thread
->StackBase
= KernelStack
;
864 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
- KERNEL_STACK_SIZE
;
865 Thread
->KernelStackResident
= TRUE
;
867 /* Enter SEH to avoid crashes due to user mode */
868 Status
= STATUS_SUCCESS
;
871 /* Initialize the Thread Context */
872 KiInitializeContextThread(Thread
,
878 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
880 /* Set failure status */
881 Status
= STATUS_UNSUCCESSFUL
;
883 /* Check if a stack was allocated */
886 /* Delete the stack */
887 MmDeleteKernelStack((PVOID
)Thread
->StackBase
, FALSE
);
888 Thread
->InitialStack
= NULL
;
893 /* Set the Thread to initialized */
894 Thread
->State
= Initialized
;
900 KeInitializeThread(IN PKPROCESS Process
,
901 IN OUT PKTHREAD Thread
,
902 IN PKSYSTEM_ROUTINE SystemRoutine
,
903 IN PKSTART_ROUTINE StartRoutine
,
904 IN PVOID StartContext
,
907 IN PVOID KernelStack
)
909 /* Initialize and start the thread on success */
910 if (NT_SUCCESS(KeInitThread(Thread
,
920 KeStartThread(Thread
);
926 KeUninitThread(IN PKTHREAD Thread
)
928 /* Delete the stack */
929 MmDeleteKernelStack((PVOID
)Thread
->StackBase
, FALSE
);
930 Thread
->InitialStack
= NULL
;
933 /* PUBLIC FUNCTIONS **********************************************************/
940 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
946 IN PVOID ThreadState
)
954 #undef KeGetCurrentThread
957 KeGetCurrentThread(VOID
)
959 /* Return the current thread on this PCR */
960 return _KeGetCurrentThread();
966 #undef KeGetPreviousMode
969 KeGetPreviousMode(VOID
)
971 /* Return the previous mode of this thread */
972 return _KeGetPreviousMode();
980 KeQueryRuntimeThread(IN PKTHREAD Thread
,
983 ASSERT_THREAD(Thread
);
985 /* Return the User Time */
986 *UserTime
= Thread
->UserTime
;
988 /* Return the Kernel Time */
989 return Thread
->KernelTime
;
997 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
999 BOOLEAN PreviousState
;
1000 PKTHREAD Thread
= KeGetCurrentThread();
1002 /* Save Old State */
1003 PreviousState
= Thread
->EnableStackSwap
;
1006 Thread
->EnableStackSwap
= Enable
;
1008 /* Return Old State */
1009 return PreviousState
;
1017 KeQueryPriorityThread(IN PKTHREAD Thread
)
1019 ASSERT_THREAD(Thread
);
1021 /* Return the current priority */
1022 return Thread
->Priority
;
1030 KeRevertToUserAffinityThread(VOID
)
1034 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
1035 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1036 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
1038 /* Lock the Dispatcher Database */
1039 OldIrql
= KiAcquireDispatcherLock();
1041 /* Set the user affinity and processor and disable system affinity */
1042 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
1043 CurrentThread
->IdealProcessor
= CurrentThread
->UserIdealProcessor
;
1044 CurrentThread
->SystemAffinityActive
= FALSE
;
1046 /* Get the current PRCB and check if it doesn't match this affinity */
1047 Prcb
= KeGetCurrentPrcb();
1048 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
1051 KiAcquirePrcbLock(Prcb
);
1053 /* Check if there's no next thread scheduled */
1054 if (!Prcb
->NextThread
)
1056 /* Select a new thread and set it on standby */
1057 NextThread
= KiSelectNextThread(Prcb
);
1058 NextThread
->State
= Standby
;
1059 Prcb
->NextThread
= NextThread
;
1062 /* Release the PRCB lock */
1063 KiReleasePrcbLock(Prcb
);
1066 /* Unlock dispatcher database */
1067 KiReleaseDispatcherLock(OldIrql
);
1075 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
1078 CCHAR OldIdealProcessor
;
1080 ASSERT(Processor
<= MAXIMUM_PROCESSORS
);
1082 /* Lock the Dispatcher Database */
1083 OldIrql
= KiAcquireDispatcherLock();
1085 /* Save Old Ideal Processor */
1086 OldIdealProcessor
= Thread
->UserIdealProcessor
;
1088 /* Make sure a valid CPU was given */
1089 if (Processor
< KeNumberProcessors
)
1091 /* Check if the user ideal CPU is in the affinity */
1092 if (Thread
->Affinity
& AFFINITY_MASK(Processor
))
1094 /* Set the ideal processor */
1095 Thread
->IdealProcessor
= Processor
;
1097 /* Check if system affinity is used */
1098 if (!Thread
->SystemAffinityActive
)
1100 /* It's not, so update the user CPU too */
1101 Thread
->UserIdealProcessor
= Processor
;
1106 /* Release dispatcher lock and return the old ideal CPU */
1107 KiReleaseDispatcherLock(OldIrql
);
1108 return OldIdealProcessor
;
1116 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1120 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
1121 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1122 ASSERT((Affinity
& KeActiveProcessors
) != 0);
1124 /* Lock the Dispatcher Database */
1125 OldIrql
= KiAcquireDispatcherLock();
1127 /* Restore the affinity and enable system affinity */
1128 CurrentThread
->Affinity
= Affinity
;
1129 CurrentThread
->SystemAffinityActive
= TRUE
;
1131 /* Check if the ideal processor is part of the affinity */
1133 if (!(Affinity
& AFFINITY_MASK(CurrentThread
->IdealProcessor
)))
1135 ULONG AffinitySet
, NodeMask
;
1137 /* It's not! Get the PRCB */
1138 Prcb
= KiProcessorBlock
[CurrentThread
->IdealProcessor
];
1140 /* Calculate the affinity set */
1141 AffinitySet
= KeActiveProcessors
& Affinity
;
1142 NodeMask
= Prcb
->ParentNode
->ProcessorMask
& AffinitySet
;
1145 /* Use the Node set instead */
1146 AffinitySet
= NodeMask
;
1149 /* Calculate the ideal CPU from the affinity set */
1150 BitScanReverse(&NodeMask
, AffinitySet
);
1151 CurrentThread
->IdealProcessor
= (UCHAR
)NodeMask
;
1155 /* Get the current PRCB and check if it doesn't match this affinity */
1156 Prcb
= KeGetCurrentPrcb();
1157 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
1160 KiAcquirePrcbLock(Prcb
);
1162 /* Check if there's no next thread scheduled */
1163 if (!Prcb
->NextThread
)
1165 /* Select a new thread and set it on standby */
1166 NextThread
= KiSelectNextThread(Prcb
);
1167 NextThread
->State
= Standby
;
1168 Prcb
->NextThread
= NextThread
;
1171 /* Release the PRCB lock */
1172 KiReleasePrcbLock(Prcb
);
1175 /* Unlock dispatcher database */
1176 KiReleaseDispatcherLock(OldIrql
);
1184 KeSetBasePriorityThread(IN PKTHREAD Thread
,
1188 KPRIORITY OldBasePriority
, Priority
, BasePriority
;
1191 ASSERT_THREAD(Thread
);
1192 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1194 /* Get the process */
1195 Process
= Thread
->ApcState
.Process
;
1197 /* Lock the Dispatcher Database */
1198 OldIrql
= KiAcquireDispatcherLock();
1200 /* Lock the thread */
1201 KiAcquireThreadLock(Thread
);
1203 /* Save the old base priority and increment */
1204 OldBasePriority
= Thread
->BasePriority
;
1205 OldIncrement
= OldBasePriority
- Process
->BasePriority
;
1207 /* If priority saturation happened, use the saturated increment */
1208 if (Thread
->Saturation
) OldIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1211 /* Reset the saturation value */
1212 Thread
->Saturation
= 0;
1214 /* Now check if saturation is being used for the new value */
1215 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1217 /* Check if we need positive or negative saturation */
1218 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1221 /* Normalize the Base Priority */
1222 BasePriority
= Process
->BasePriority
+ Increment
;
1223 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1225 /* Check if it's too low */
1226 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1228 /* Set it to the lowest real time level */
1229 BasePriority
= LOW_REALTIME_PRIORITY
;
1232 /* Check if it's too high */
1233 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1235 /* We are at real time, so use the raw base priority */
1236 Priority
= BasePriority
;
1240 /* Check if it's entering the real time range */
1241 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1243 /* Set it to the highest dynamic level */
1244 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1247 /* Check if it's too low and normalize it */
1248 if (BasePriority
<= LOW_PRIORITY
) BasePriority
= 1;
1250 /* Check if Saturation is used */
1251 if (Thread
->Saturation
)
1253 /* Use the raw base priority */
1254 Priority
= BasePriority
;
1258 /* Otherwise, calculate the new priority */
1259 Priority
= KiComputeNewPriority(Thread
, 0);
1260 Priority
+= (BasePriority
- OldBasePriority
);
1262 /* Check if it entered the real-time range */
1263 if (Priority
>= LOW_REALTIME_PRIORITY
)
1265 /* Normalize it down to the highest dynamic priority */
1266 Priority
= LOW_REALTIME_PRIORITY
- 1;
1268 else if (Priority
<= LOW_PRIORITY
)
1270 /* It went too low, normalize it */
1276 /* Finally set the new base priority */
1277 Thread
->BasePriority
= (SCHAR
)BasePriority
;
1279 /* Reset the decrements */
1280 Thread
->PriorityDecrement
= 0;
1282 /* Check if we're changing priority after all */
1283 if (Priority
!= Thread
->Priority
)
1285 /* Reset the quantum and do the actual priority modification */
1286 Thread
->Quantum
= Thread
->QuantumReset
;
1287 KiSetPriorityThread(Thread
, Priority
);
1290 /* Release thread lock */
1291 KiReleaseThreadLock(Thread
);
1293 /* Release the dispatcher database and return old increment */
1294 KiReleaseDispatcherLock(OldIrql
);
1295 return OldIncrement
;
1303 KeSetAffinityThread(IN PKTHREAD Thread
,
1304 IN KAFFINITY Affinity
)
1307 KAFFINITY OldAffinity
;
1308 ASSERT_THREAD(Thread
);
1309 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1311 /* Lock the dispatcher database */
1312 OldIrql
= KiAcquireDispatcherLock();
1314 /* Call the internal function */
1315 OldAffinity
= KiSetAffinityThread(Thread
, Affinity
);
1317 /* Release the dispatcher database and return old affinity */
1318 KiReleaseDispatcherLock(OldIrql
);
1327 KeSetPriorityThread(IN PKTHREAD Thread
,
1328 IN KPRIORITY Priority
)
1331 KPRIORITY OldPriority
;
1332 ASSERT_THREAD(Thread
);
1333 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1334 ASSERT((Priority
<= HIGH_PRIORITY
) && (Priority
>= LOW_PRIORITY
));
1335 ASSERT(KeIsExecutingDpc() == FALSE
);
1337 /* Lock the Dispatcher Database */
1338 OldIrql
= KiAcquireDispatcherLock();
1340 /* Lock the thread */
1341 KiAcquireThreadLock(Thread
);
1343 /* Save the old Priority and reset decrement */
1344 OldPriority
= Thread
->Priority
;
1345 Thread
->PriorityDecrement
= 0;
1347 /* Make sure that an actual change is being done */
1348 if (Priority
!= Thread
->Priority
)
1350 /* Reset the quantum */
1351 Thread
->Quantum
= Thread
->QuantumReset
;
1353 /* Check if priority is being set too low and normalize if so */
1354 if ((Thread
->BasePriority
!= 0) && !(Priority
)) Priority
= 1;
1356 /* Set the new Priority */
1357 KiSetPriorityThread(Thread
, Priority
);
1360 /* Release thread lock */
1361 KiReleaseThreadLock(Thread
);
1363 /* Release the dispatcher database */
1364 KiReleaseDispatcherLock(OldIrql
);
1366 /* Return Old Priority */
1375 KeTerminateThread(IN KPRIORITY Increment
)
1377 PLIST_ENTRY
*ListHead
;
1378 PETHREAD Entry
, SavedEntry
;
1379 PETHREAD
*ThreadAddr
;
1380 KLOCK_QUEUE_HANDLE LockHandle
;
1381 PKTHREAD Thread
= KeGetCurrentThread();
1382 PKPROCESS Process
= Thread
->ApcState
.Process
;
1383 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1385 /* Lock the process */
1386 KiAcquireProcessLockRaiseToSynch(Process
, &LockHandle
);
1388 /* Make sure we won't get Swapped */
1389 KiSetThreadSwapBusy(Thread
);
1391 /* Save the Kernel and User Times */
1392 Process
->KernelTime
+= Thread
->KernelTime
;
1393 Process
->UserTime
+= Thread
->UserTime
;
1395 /* Get the current entry and our Port */
1396 Entry
= (PETHREAD
)PspReaperListHead
.Flink
;
1397 ThreadAddr
= &((PETHREAD
)Thread
)->ReaperLink
;
1399 /* Add it to the reaper's list */
1402 /* Get the list head */
1403 ListHead
= &PspReaperListHead
.Flink
;
1405 /* Link ourselves */
1406 *ThreadAddr
= Entry
;
1409 /* Now try to do the exchange */
1410 Entry
= InterlockedCompareExchangePointer((PVOID
*)ListHead
,
1414 /* Break out if the change was succesful */
1415 } while (Entry
!= SavedEntry
);
1417 /* Acquire the dispatcher lock */
1418 KiAcquireDispatcherLockAtSynchLevel();
1420 /* Check if the reaper wasn't active */
1423 /* Activate it as a work item, directly through its Queue */
1424 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1425 &PspReaperWorkItem
.List
,
1429 /* Check the thread has an associated queue */
1432 /* Remove it from the list, and handle the queue */
1433 RemoveEntryList(&Thread
->QueueListEntry
);
1434 KiActivateWaiterQueue(Thread
->Queue
);
1437 /* Signal the thread */
1438 Thread
->Header
.SignalState
= TRUE
;
1439 if (!IsListEmpty(&Thread
->Header
.WaitListHead
))
1441 /* Unwait the threads */
1442 KxUnwaitThread(&Thread
->Header
, Increment
);
1445 /* Remove the thread from the list */
1446 RemoveEntryList(&Thread
->ThreadListEntry
);
1448 /* Release the process lock */
1449 KiReleaseProcessLockFromSynchLevel(&LockHandle
);
1451 /* Set us as terminated, decrease the Process's stack count */
1452 Thread
->State
= Terminated
;
1454 /* Decrease stack count */
1455 ASSERT(Process
->StackCount
!= 0);
1456 ASSERT(Process
->State
== ProcessInMemory
);
1457 Process
->StackCount
--;
1458 if (!(Process
->StackCount
) && !(IsListEmpty(&Process
->ThreadListHead
)))
1460 /* FIXME: Swap stacks */
1463 /* Rundown arch-specific parts */
1464 KiRundownThread(Thread
);
1466 /* Swap to a new thread */
1467 KiReleaseDispatcherLockFromSynchLevel();
1468 KiSwapThread(Thread
, KeGetCurrentPrcb());