- most of the churn here is from code and headers imported from trunk.
[reactos.git] / reactos / ntoskrnl / ke / thrdobj.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <internal/debug.h>
14
15 extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
16 extern LIST_ENTRY PspReaperListHead;
17
18 ULONG KiMask32Array[MAXIMUM_PRIORITY] =
19 {
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
26 };
27
28 /* FUNCTIONS *****************************************************************/
29
30 UCHAR
31 NTAPI
32 KeFindNextRightSetAffinity(IN UCHAR Number,
33 IN ULONG Set)
34 {
35 ULONG Bit, Result;
36 ASSERT(Set != 0);
37
38 /* Calculate the mask */
39 Bit = (AFFINITY_MASK(Number) - 1) & Set;
40
41 /* If it's 0, use the one we got */
42 if (!Bit) Bit = Set;
43
44 /* Now find the right set and return it */
45 BitScanReverse(&Result, Bit);
46 return (UCHAR)Result;
47 }
48
49 KPRIORITY
50 NTAPI
51 KeQueryBasePriorityThread(IN PKTHREAD Thread)
52 {
53 LONG BaseIncrement;
54 KIRQL OldIrql;
55 PKPROCESS Process;
56 ASSERT_THREAD(Thread);
57 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
58
59 /* Raise IRQL to synch level */
60 OldIrql = KeRaiseIrqlToSynchLevel();
61
62 /* Lock the thread */
63 KiAcquireThreadLock(Thread);
64
65 /* Get the Process */
66 Process = Thread->ApcStatePointer[0]->Process;
67
68 /* Calculate the base increment */
69 BaseIncrement = Thread->BasePriority - Process->BasePriority;
70
71 /* If saturation occured, return the saturation increment instead */
72 if (Thread->Saturation) BaseIncrement = (HIGH_PRIORITY + 1) / 2 *
73 Thread->Saturation;
74
75 /* Release thread lock */
76 KiReleaseThreadLock(Thread);
77
78 /* Lower IRQl and return Increment */
79 KeLowerIrql(OldIrql);
80 return BaseIncrement;
81 }
82
83 VOID
84 NTAPI
85 KeReadyThread(IN PKTHREAD Thread)
86 {
87 KIRQL OldIrql;
88 ASSERT_THREAD(Thread);
89 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
90
91 /* Lock the Dispatcher Database */
92 OldIrql = KiAcquireDispatcherLock();
93
94 /* Make the thread ready */
95 KiReadyThread(Thread);
96
97 /* Unlock dispatcher database */
98 KiReleaseDispatcherLock(OldIrql);
99 }
100
101 ULONG
102 NTAPI
103 KeAlertResumeThread(IN PKTHREAD Thread)
104 {
105 ULONG PreviousCount;
106 KLOCK_QUEUE_HANDLE ApcLock;
107 ASSERT_THREAD(Thread);
108 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
109
110 /* Lock the Dispatcher Database and the APC Queue */
111 KiAcquireApcLock(Thread, &ApcLock);
112 KiAcquireDispatcherLockAtDpcLevel();
113
114 /* Return if Thread is already alerted. */
115 if (!Thread->Alerted[KernelMode])
116 {
117 /* If it's Blocked, unblock if it we should */
118 if ((Thread->State == Waiting) && (Thread->Alertable))
119 {
120 /* Abort the wait */
121 KiUnwaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
122 }
123 else
124 {
125 /* If not, simply Alert it */
126 Thread->Alerted[KernelMode] = TRUE;
127 }
128 }
129
130 /* Save the old Suspend Count */
131 PreviousCount = Thread->SuspendCount;
132
133 /* If the thread is suspended, decrease one of the suspend counts */
134 if (PreviousCount)
135 {
136 /* Decrease count. If we are now zero, unwait it completely */
137 if (--Thread->SuspendCount)
138 {
139 /* Signal and satisfy */
140 Thread->SuspendSemaphore.Header.SignalState++;
141 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
142 }
143 }
144
145 /* Release Locks and return the Old State */
146 KiReleaseDispatcherLockFromDpcLevel();
147 KiReleaseApcLockFromDpcLevel(&ApcLock);
148 KiExitDispatcher(ApcLock.OldIrql);
149 return PreviousCount;
150 }
151
152 BOOLEAN
153 NTAPI
154 KeAlertThread(IN PKTHREAD Thread,
155 IN KPROCESSOR_MODE AlertMode)
156 {
157 BOOLEAN PreviousState;
158 KLOCK_QUEUE_HANDLE ApcLock;
159 ASSERT_THREAD(Thread);
160 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
161
162 /* Lock the Dispatcher Database and the APC Queue */
163 KiAcquireApcLock(Thread, &ApcLock);
164 KiAcquireDispatcherLockAtDpcLevel();
165
166 /* Save the Previous State */
167 PreviousState = Thread->Alerted[AlertMode];
168
169 /* Check if it's already alerted */
170 if (!PreviousState)
171 {
172 /* Check if the thread is alertable, and blocked in the given mode */
173 if ((Thread->State == Waiting) &&
174 ((AlertMode == KernelMode) || (Thread->WaitMode == AlertMode)) &&
175 (Thread->Alertable))
176 {
177 /* Abort the wait to alert the thread */
178 KiUnwaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
179 }
180 else
181 {
182 /* Otherwise, merely set the alerted state */
183 Thread->Alerted[AlertMode] = TRUE;
184 }
185 }
186
187 /* Release the Dispatcher Lock */
188 KiReleaseDispatcherLockFromDpcLevel();
189 KiReleaseApcLockFromDpcLevel(&ApcLock);
190 KiExitDispatcher(ApcLock.OldIrql);
191
192 /* Return the old state */
193 return PreviousState;
194 }
195
196 ULONG
197 NTAPI
198 KeForceResumeThread(IN PKTHREAD Thread)
199 {
200 KLOCK_QUEUE_HANDLE ApcLock;
201 ULONG PreviousCount;
202 ASSERT_THREAD(Thread);
203 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
204
205 /* Lock the APC Queue */
206 KiAcquireApcLock(Thread, &ApcLock);
207
208 /* Save the old Suspend Count */
209 PreviousCount = Thread->SuspendCount + Thread->FreezeCount;
210
211 /* If the thread is suspended, wake it up!!! */
212 if (PreviousCount)
213 {
214 /* Unwait it completely */
215 Thread->SuspendCount = 0;
216 Thread->FreezeCount = 0;
217
218 /* Lock the dispatcher */
219 KiAcquireDispatcherLockAtDpcLevel();
220
221 /* Signal and satisfy */
222 Thread->SuspendSemaphore.Header.SignalState++;
223 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
224
225 /* Release the dispatcher */
226 KiReleaseDispatcherLockFromDpcLevel();
227 }
228
229 /* Release Lock and return the Old State */
230 KiReleaseApcLockFromDpcLevel(&ApcLock);
231 KiExitDispatcher(ApcLock.OldIrql);
232 return PreviousCount;
233 }
234
235 /*
236 * Used by the debugging code to freeze all the process's threads
237 * while the debugger is examining their state.
238 */
239 VOID
240 NTAPI
241 KeFreezeAllThreads(IN PKPROCESS Process)
242 {
243 KLOCK_QUEUE_HANDLE LockHandle, ApcLock;
244 PKTHREAD Current, CurrentThread = KeGetCurrentThread();
245 PLIST_ENTRY ListHead, NextEntry;
246 LONG OldCount;
247 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
248
249 /* Lock the process */
250 KiAcquireProcessLock(Process, &LockHandle);
251
252 /* If someone is already trying to free us, try again */
253 while (CurrentThread->FreezeCount)
254 {
255 /* Release and re-acquire the process lock so the APC will go through */
256 KiReleaseProcessLock(&LockHandle);
257 KiAcquireProcessLock(Process, &LockHandle);
258 }
259
260 /* Enter a critical region */
261 KeEnterCriticalRegion();
262
263 /* Loop the Process's Threads */
264 ListHead = &Process->ThreadListHead;
265 NextEntry = ListHead->Flink;
266 while (NextEntry != ListHead)
267 {
268 /* Get the current thread */
269 Current = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
270
271 /* Lock it */
272 KiAcquireApcLockAtDpcLevel(Current, &ApcLock);
273
274 /* Make sure it's not ours, and check if APCs are enabled */
275 if ((Current != CurrentThread) && (Current->ApcQueueable))
276 {
277 /* Sanity check */
278 OldCount = Current->SuspendCount;
279 ASSERT(OldCount != MAXIMUM_SUSPEND_COUNT);
280
281 /* Increase the freeze count */
282 Current->FreezeCount++;
283
284 /* Make sure it wasn't already suspended */
285 if (!(OldCount) && !(Current->SuspendCount))
286 {
287 /* Did we already insert it? */
288 if (!Current->SuspendApc.Inserted)
289 {
290 /* Insert the APC */
291 Current->SuspendApc.Inserted = TRUE;
292 KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT);
293 }
294 else
295 {
296 /* Lock the dispatcher */
297 KiAcquireDispatcherLockAtDpcLevel();
298
299 /* Unsignal the semaphore, the APC was already inserted */
300 Current->SuspendSemaphore.Header.SignalState--;
301
302 /* Release the dispatcher */
303 KiReleaseDispatcherLockFromDpcLevel();
304 }
305 }
306 }
307
308 /* Release the APC lock */
309 KiReleaseApcLockFromDpcLevel(&ApcLock);
310 }
311
312 /* Release the process lock and exit the dispatcher */
313 KiReleaseProcessLock(&LockHandle);
314 KiExitDispatcher(LockHandle.OldIrql);
315 }
316
317 ULONG
318 NTAPI
319 KeResumeThread(IN PKTHREAD Thread)
320 {
321 KLOCK_QUEUE_HANDLE ApcLock;
322 ULONG PreviousCount;
323 ASSERT_THREAD(Thread);
324 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
325
326 /* Lock the APC Queue */
327 KiAcquireApcLock(Thread, &ApcLock);
328
329 /* Save the Old Count */
330 PreviousCount = Thread->SuspendCount;
331
332 /* Check if it existed */
333 if (PreviousCount)
334 {
335 /* Decrease the suspend count */
336 Thread->SuspendCount--;
337
338 /* Check if the thrad is still suspended or not */
339 if ((!Thread->SuspendCount) && (!Thread->FreezeCount))
340 {
341 /* Acquire the dispatcher lock */
342 KiAcquireDispatcherLockAtDpcLevel();
343
344 /* Signal the Suspend Semaphore */
345 Thread->SuspendSemaphore.Header.SignalState++;
346 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
347
348 /* Release the dispatcher lock */
349 KiReleaseDispatcherLockFromDpcLevel();
350 }
351 }
352
353 /* Release APC Queue lock and return the Old State */
354 KiReleaseApcLockFromDpcLevel(&ApcLock);
355 KiExitDispatcher(ApcLock.OldIrql);
356 return PreviousCount;
357 }
358
359 VOID
360 NTAPI
361 KeRundownThread(VOID)
362 {
363 KIRQL OldIrql;
364 PKTHREAD Thread = KeGetCurrentThread();
365 PLIST_ENTRY NextEntry, ListHead;
366 PKMUTANT Mutant;
367
368 /* Optimized path if nothing is on the list at the moment */
369 if (IsListEmpty(&Thread->MutantListHead)) return;
370
371 /* Lock the Dispatcher Database */
372 OldIrql = KiAcquireDispatcherLock();
373
374 /* Get the List Pointers */
375 ListHead = &Thread->MutantListHead;
376 NextEntry = ListHead->Flink;
377 while (NextEntry != ListHead)
378 {
379 /* Get the Mutant */
380 Mutant = CONTAINING_RECORD(NextEntry, KMUTANT, MutantListEntry);
381
382 /* Make sure it's not terminating with APCs off */
383 if (Mutant->ApcDisable)
384 {
385 /* Bugcheck the system */
386 KEBUGCHECKEX(0, //THREAD_TERMINATE_HELD_MUTEX,
387 (ULONG_PTR)Thread,
388 (ULONG_PTR)Mutant,
389 0,
390 0);
391 }
392
393 /* Now we can remove it */
394 RemoveEntryList(&Mutant->MutantListEntry);
395
396 /* Unconditionally abandon it */
397 Mutant->Header.SignalState = 1;
398 Mutant->Abandoned = TRUE;
399 Mutant->OwnerThread = NULL;
400
401 /* Check if the Wait List isn't empty */
402 if (!IsListEmpty(&Mutant->Header.WaitListHead))
403 {
404 /* Wake the Mutant */
405 KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
406 }
407
408 /* Move on */
409 NextEntry = NextEntry->Flink;
410 }
411
412 /* Release the Lock */
413 KiReleaseDispatcherLock(OldIrql);
414 }
415
416 VOID
417 NTAPI
418 KeStartThread(IN OUT PKTHREAD Thread)
419 {
420 KLOCK_QUEUE_HANDLE LockHandle;
421 #ifdef CONFIG_SMP
422 PKNODE Node;
423 PKPRCB NodePrcb;
424 ULONG Set, Mask;
425 #endif
426 UCHAR IdealProcessor = 0;
427 PKPROCESS Process = Thread->ApcState.Process;
428
429 /* Setup static fields from parent */
430 #ifdef _M_IX86
431 Thread->Iopl = Process->Iopl;
432 #endif
433 Thread->Quantum = Process->QuantumReset;
434 Thread->QuantumReset = Process->QuantumReset;
435 Thread->SystemAffinityActive = FALSE;
436
437 /* Lock the process */
438 KiAcquireProcessLock(Process, &LockHandle);
439
440 /* Setup volatile data */
441 Thread->Priority = Process->BasePriority;
442 Thread->BasePriority = Process->BasePriority;
443 Thread->Affinity = Process->Affinity;
444 Thread->UserAffinity = Process->Affinity;
445
446 #ifdef CONFIG_SMP
447 /* Get the KNODE and its PRCB */
448 Node = KeNodeBlock[Process->IdealNode];
449 NodePrcb = (PKPRCB)(KPCR_BASE + (Process->ThreadSeed * PAGE_SIZE));
450
451 /* Calculate affinity mask */
452 Set = ~NodePrcb->MultiThreadProcessorSet;
453 Mask = (ULONG)(Node->ProcessorMask & Process->Affinity);
454 Set &= Mask;
455 if (Set) Mask = Set;
456
457 /* Get the new thread seed */
458 IdealProcessor = KeFindNextRightSetAffinity(Process->ThreadSeed, Mask);
459 Process->ThreadSeed = IdealProcessor;
460
461 /* Sanity check */
462 ASSERT((Thread->UserAffinity & AFFINITY_MASK(IdealProcessor)));
463 #endif
464
465 /* Set the Ideal Processor */
466 Thread->IdealProcessor = IdealProcessor;
467 Thread->UserIdealProcessor = IdealProcessor;
468
469 /* Lock the Dispatcher Database */
470 KiAcquireDispatcherLockAtDpcLevel();
471
472 /* Insert the thread into the process list */
473 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
474
475 /* Increase the stack count */
476 ASSERT(Process->StackCount != MAXULONG_PTR);
477 Process->StackCount++;
478
479 /* Release locks and return */
480 KiReleaseDispatcherLockFromDpcLevel();
481 KiReleaseProcessLock(&LockHandle);
482 }
483
484 VOID
485 NTAPI
486 KiSuspendRundown(IN PKAPC Apc)
487 {
488 /* Does nothing */
489 UNREFERENCED_PARAMETER(Apc);
490 }
491
492 VOID
493 NTAPI
494 KiSuspendNop(IN PKAPC Apc,
495 IN PKNORMAL_ROUTINE *NormalRoutine,
496 IN PVOID *NormalContext,
497 IN PVOID *SystemArgument1,
498 IN PVOID *SystemArgument2)
499 {
500 /* Does nothing */
501 UNREFERENCED_PARAMETER(Apc);
502 UNREFERENCED_PARAMETER(NormalRoutine);
503 UNREFERENCED_PARAMETER(NormalContext);
504 UNREFERENCED_PARAMETER(SystemArgument1);
505 UNREFERENCED_PARAMETER(SystemArgument2);
506 }
507
508 VOID
509 NTAPI
510 KiSuspendThread(IN PVOID NormalContext,
511 IN PVOID SystemArgument1,
512 IN PVOID SystemArgument2)
513 {
514 /* Non-alertable kernel-mode suspended wait */
515 KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore,
516 Suspended,
517 KernelMode,
518 FALSE,
519 NULL);
520 }
521
522 NTSTATUS
523 NTAPI
524 KeSuspendThread(PKTHREAD Thread)
525 {
526 KLOCK_QUEUE_HANDLE ApcLock;
527 ULONG PreviousCount;
528 ASSERT_THREAD(Thread);
529 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
530
531 /* Lock the APC Queue */
532 KiAcquireApcLock(Thread, &ApcLock);
533
534 /* Save the Old Count */
535 PreviousCount = Thread->SuspendCount;
536
537 /* Handle the maximum */
538 if (PreviousCount == MAXIMUM_SUSPEND_COUNT)
539 {
540 /* Raise an exception */
541 KiReleaseApcLock(&ApcLock);
542 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
543 }
544
545 /* Should we bother to queue at all? */
546 if (Thread->ApcQueueable)
547 {
548 /* Increment the suspend count */
549 Thread->SuspendCount++;
550
551 /* Check if we should suspend it */
552 if (!(PreviousCount) && !(Thread->FreezeCount))
553 {
554 /* Is the APC already inserted? */
555 if (!Thread->SuspendApc.Inserted)
556 {
557 /* Not inserted, insert it */
558 Thread->SuspendApc.Inserted = TRUE;
559 KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT);
560 }
561 else
562 {
563 /* Lock the dispatcher */
564 KiAcquireDispatcherLockAtDpcLevel();
565
566 /* Unsignal the semaphore, the APC was already inserted */
567 Thread->SuspendSemaphore.Header.SignalState--;
568
569 /* Release the dispatcher */
570 KiReleaseDispatcherLockFromDpcLevel();
571 }
572 }
573 }
574
575 /* Release Lock and return the Old State */
576 KiReleaseApcLockFromDpcLevel(&ApcLock);
577 KiExitDispatcher(ApcLock.OldIrql);
578 return PreviousCount;
579 }
580
581 BOOLEAN
582 NTAPI
583 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
584 {
585 PKTHREAD Thread = KeGetCurrentThread();
586 BOOLEAN OldState;
587 KLOCK_QUEUE_HANDLE ApcLock;
588 ASSERT_THREAD(Thread);
589 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
590
591 /* Lock the Dispatcher Database and the APC Queue */
592 KiAcquireApcLock(Thread, &ApcLock);
593 KiAcquireDispatcherLockAtDpcLevel();
594
595 /* Save the old State */
596 OldState = Thread->Alerted[AlertMode];
597
598 /* Check the Thread is alerted */
599 if (OldState)
600 {
601 /* Disable alert for this mode */
602 Thread->Alerted[AlertMode] = FALSE;
603 }
604 else if ((AlertMode != KernelMode) &&
605 (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
606 {
607 /* If the mode is User and the Queue isn't empty, set Pending */
608 Thread->ApcState.UserApcPending = TRUE;
609 }
610
611 /* Release Locks and return the Old State */
612 KiReleaseDispatcherLockFromDpcLevel();
613 KiReleaseApcLockFromDpcLevel(&ApcLock);
614 KiExitDispatcher(ApcLock.OldIrql);
615 return OldState;
616 }
617
618 NTSTATUS
619 NTAPI
620 KeInitThread(IN OUT PKTHREAD Thread,
621 IN PVOID KernelStack,
622 IN PKSYSTEM_ROUTINE SystemRoutine,
623 IN PKSTART_ROUTINE StartRoutine,
624 IN PVOID StartContext,
625 IN PCONTEXT Context,
626 IN PVOID Teb,
627 IN PKPROCESS Process)
628 {
629 BOOLEAN AllocatedStack = FALSE;
630 ULONG i;
631 PKWAIT_BLOCK TimerWaitBlock;
632 PKTIMER Timer;
633 NTSTATUS Status;
634
635 /* Initalize the Dispatcher Header */
636 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
637 ThreadObject,
638 sizeof(KTHREAD) / sizeof(LONG),
639 FALSE);
640
641 /* Initialize the Mutant List */
642 InitializeListHead(&Thread->MutantListHead);
643
644 /* Initialize the wait blocks */
645 for (i = 0; i< (THREAD_WAIT_OBJECTS + 1); i++)
646 {
647 /* Put our pointer */
648 Thread->WaitBlock[i].Thread = Thread;
649 }
650
651 /* Set swap settings */
652 Thread->EnableStackSwap = FALSE;//TRUE;
653 Thread->IdealProcessor = 1;
654 Thread->SwapBusy = FALSE;
655 Thread->AdjustReason = 0;
656
657 /* Initialize the lock */
658 KeInitializeSpinLock(&Thread->ThreadLock);
659
660 /* Setup the Service Descriptor Table for Native Calls */
661 Thread->ServiceTable = KeServiceDescriptorTable;
662
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);
672
673 /* Initialize the Suspend APC */
674 KeInitializeApc(&Thread->SuspendApc,
675 Thread,
676 OriginalApcEnvironment,
677 KiSuspendNop,
678 KiSuspendRundown,
679 KiSuspendThread,
680 KernelMode,
681 NULL);
682
683 /* Initialize the Suspend Semaphore */
684 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 2);
685
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;
694
695 /* Link the two wait lists together */
696 TimerWaitBlock->WaitListEntry.Flink = &Timer->Header.WaitListHead;
697 TimerWaitBlock->WaitListEntry.Blink = &Timer->Header.WaitListHead;
698
699 /* Set the TEB */
700 Thread->Teb = Teb;
701
702 /* Check if we have a kernel stack */
703 if (!KernelStack)
704 {
705 /* We don't, allocate one */
706 KernelStack = (PVOID)((ULONG_PTR)MmCreateKernelStack(FALSE) +
707 KERNEL_STACK_SIZE);
708 if (!KernelStack) return STATUS_INSUFFICIENT_RESOURCES;
709
710 /* Remember for later */
711 AllocatedStack = TRUE;
712 }
713
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;
719
720 /* ROS Mm HACK */
721 MmUpdatePageDir((PEPROCESS)Process,
722 (PVOID)Thread->StackLimit,
723 KERNEL_STACK_SIZE);
724 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
725
726 /* Enter SEH to avoid crashes due to user mode */
727 Status = STATUS_SUCCESS;
728 _SEH_TRY
729 {
730 /* Initalize the Thread Context */
731 #ifdef _M_IX86
732 Ke386InitThreadWithContext(Thread,
733 SystemRoutine,
734 StartRoutine,
735 StartContext,
736 Context);
737 #endif
738 }
739 _SEH_HANDLE
740 {
741 /* Set failure status */
742 Status = STATUS_UNSUCCESSFUL;
743
744 /* Check if a stack was allocated */
745 if (AllocatedStack)
746 {
747 /* Delete the stack */
748 MmDeleteKernelStack(Thread->StackBase, FALSE);
749 Thread->InitialStack = NULL;
750 }
751 }
752 _SEH_END;
753
754 /* Set the Thread to initalized */
755 Thread->State = Initialized;
756 return Status;
757 }
758
759 VOID
760 NTAPI
761 KeInitializeThread(IN PKPROCESS Process,
762 IN OUT PKTHREAD Thread,
763 IN PKSYSTEM_ROUTINE SystemRoutine,
764 IN PKSTART_ROUTINE StartRoutine,
765 IN PVOID StartContext,
766 IN PCONTEXT Context,
767 IN PVOID Teb,
768 IN PVOID KernelStack)
769 {
770 /* Initailize and start the thread on success */
771 if (NT_SUCCESS(KeInitThread(Thread,
772 KernelStack,
773 SystemRoutine,
774 StartRoutine,
775 StartContext,
776 Context,
777 Teb,
778 Process)))
779 {
780 /* Start it */
781 KeStartThread(Thread);
782 }
783 }
784
785 VOID
786 NTAPI
787 KeUninitThread(IN PKTHREAD Thread)
788 {
789 /* Delete the stack */
790 MmDeleteKernelStack(Thread->StackBase, FALSE);
791 Thread->InitialStack = NULL;
792 }
793
794 /* PUBLIC FUNCTIONS **********************************************************/
795
796 /*
797 * @unimplemented
798 */
799 VOID
800 NTAPI
801 KeCapturePersistentThreadState(IN PVOID CurrentThread,
802 IN ULONG Setting1,
803 IN ULONG Setting2,
804 IN ULONG Setting3,
805 IN ULONG Setting4,
806 IN ULONG Setting5,
807 IN PVOID ThreadState)
808 {
809 UNIMPLEMENTED;
810 }
811
812 /*
813 * @implemented
814 */
815 #undef KeGetCurrentThread
816 PKTHREAD
817 NTAPI
818 KeGetCurrentThread(VOID)
819 {
820 #ifdef _M_IX86
821 /* Return the current thread on this PCR */
822 return ((PKIPCR)KeGetPcr())->PrcbData.CurrentThread;
823 #elif defined(_M_PPC)
824 return KeGetCurrentKPCR()->Prcb->CurrentThread;
825 #endif
826 }
827
828 /*
829 * @implemented
830 */
831 ULONG
832 NTAPI
833 KeQueryRuntimeThread(IN PKTHREAD Thread,
834 OUT PULONG UserTime)
835 {
836 ASSERT_THREAD(Thread);
837
838 /* Return the User Time */
839 *UserTime = Thread->UserTime;
840
841 /* Return the Kernel Time */
842 return Thread->KernelTime;
843 }
844
845 /*
846 * @implemented
847 */
848 BOOLEAN
849 NTAPI
850 KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
851 {
852 BOOLEAN PreviousState;
853 PKTHREAD Thread = KeGetCurrentThread();
854
855 /* Save Old State */
856 PreviousState = Thread->EnableStackSwap;
857
858 /* Set New State */
859 Thread->EnableStackSwap = Enable;
860
861 /* Return Old State */
862 return PreviousState;
863 }
864
865 /*
866 * @implemented
867 */
868 KPRIORITY
869 NTAPI
870 KeQueryPriorityThread(IN PKTHREAD Thread)
871 {
872 ASSERT_THREAD(Thread);
873
874 /* Return the current priority */
875 return Thread->Priority;
876 }
877
878 /*
879 * @implemented
880 */
881 VOID
882 NTAPI
883 KeRevertToUserAffinityThread(VOID)
884 {
885 KIRQL OldIrql;
886 PKPRCB Prcb;
887 PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();
888 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
889 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
890
891 /* Lock the Dispatcher Database */
892 OldIrql = KiAcquireDispatcherLock();
893
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;
898
899 /* Get the current PRCB and check if it doesn't match this affinity */
900 Prcb = KeGetCurrentPrcb();
901 if (!(Prcb->SetMember & CurrentThread->Affinity))
902 {
903 /* Lock the PRCB */
904 KiAcquirePrcbLock(Prcb);
905
906 #ifdef NEW_SCHEDULER
907 /* Check if there's no next thread scheduled */
908 if (!Prcb->NextThread)
909 {
910 /* Select a new thread and set it on standby */
911 NextThread = KiSelectNextThread(Prcb);
912 NextThread->State = Standby;
913 Prcb->NextThread = NextThread;
914 }
915 #else
916 /* We need to dispatch a new thread */
917 NextThread = NULL;
918 CurrentThread->WaitIrql = OldIrql;
919 KiDispatchThreadNoLock(Ready);
920 KeLowerIrql(OldIrql);
921 return;
922 #endif
923
924 /* Release the PRCB lock */
925 KiReleasePrcbLock(Prcb);
926 }
927
928 /* Unlock dispatcher database */
929 KiReleaseDispatcherLock(OldIrql);
930 }
931
932 /*
933 * @implemented
934 */
935 UCHAR
936 NTAPI
937 KeSetIdealProcessorThread(IN PKTHREAD Thread,
938 IN UCHAR Processor)
939 {
940 CCHAR OldIdealProcessor;
941 KIRQL OldIrql;
942 ASSERT(Processor <= MAXIMUM_PROCESSORS);
943
944 /* Lock the Dispatcher Database */
945 OldIrql = KiAcquireDispatcherLock();
946
947 /* Save Old Ideal Processor */
948 OldIdealProcessor = Thread->UserIdealProcessor;
949
950 /* Make sure a valid CPU was given */
951 if (Processor < MAXIMUM_PROCESSORS)
952 {
953 /* Check if the user ideal CPU is in the affinity */
954 if (Thread->UserIdealProcessor & AFFINITY_MASK(Processor))
955 {
956 /* Set the ideal processor */
957 Thread->IdealProcessor = Processor;
958
959 /* Check if system affinity is used */
960 if (!Thread->SystemAffinityActive)
961 {
962 /* It's not, so update the user CPU too */
963 Thread->UserIdealProcessor = Processor;
964 }
965 }
966 }
967
968 /* Release dispatcher lock and return the old ideal CPU */
969 KiReleaseDispatcherLock(OldIrql);
970 return OldIdealProcessor;
971 }
972
973 /*
974 * @implemented
975 */
976 VOID
977 NTAPI
978 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
979 {
980 KIRQL OldIrql;
981 PKPRCB Prcb;
982 PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();
983 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
984 ASSERT((Affinity & KeActiveProcessors) != 0);
985
986 /* Lock the Dispatcher Database */
987 OldIrql = KiAcquireDispatcherLock();
988
989 /* Restore the affinity and enable system affinity */
990 CurrentThread->Affinity = Affinity;
991 CurrentThread->SystemAffinityActive = TRUE;
992
993 /* Check if the ideal processor is part of the affinity */
994 #ifdef CONFIG_SMP
995 if (!(Affinity & AFFINITY_MASK(CurrentThread->IdealProcessor)))
996 {
997 ULONG AffinitySet, NodeMask;
998
999 /* It's not! Get the PRCB */
1000 Prcb = KiProcessorBlock[CurrentThread->IdealProcessor];
1001
1002 /* Calculate the affinity set */
1003 AffinitySet = KeActiveProcessors & Affinity;
1004 NodeMask = Prcb->ParentNode->ProcessorMask & AffinitySet;
1005 if (NodeMask)
1006 {
1007 /* Use the Node set instead */
1008 AffinitySet = NodeMask;
1009 }
1010
1011 /* Calculate the ideal CPU from the affinity set */
1012 BitScanReverse(&NodeMask, AffinitySet);
1013 CurrentThread->IdealProcessor = (UCHAR)NodeMask;
1014 }
1015 #endif
1016
1017 /* Get the current PRCB and check if it doesn't match this affinity */
1018 Prcb = KeGetCurrentPrcb();
1019 if (!(Prcb->SetMember & CurrentThread->Affinity))
1020 {
1021 /* Lock the PRCB */
1022 KiAcquirePrcbLock(Prcb);
1023
1024 #ifdef NEW_SCHEDULER
1025 /* Check if there's no next thread scheduled */
1026 if (!Prcb->NextThread)
1027 {
1028 /* Select a new thread and set it on standby */
1029 NextThread = KiSelectNextThread(Prcb);
1030 NextThread->State = Standby;
1031 Prcb->NextThread = NextThread;
1032 }
1033 #else
1034 /* We need to dispatch a new thread */
1035 NextThread = NULL;
1036 CurrentThread->WaitIrql = OldIrql;
1037 KiDispatchThreadNoLock(Ready);
1038 KeLowerIrql(OldIrql);
1039 return;
1040 #endif
1041
1042 /* Release the PRCB lock */
1043 KiReleasePrcbLock(Prcb);
1044 }
1045
1046 /* Unlock dispatcher database */
1047 KiReleaseDispatcherLock(OldIrql);
1048 }
1049
1050 /*
1051 * @implemented
1052 */
1053 LONG
1054 NTAPI
1055 KeSetBasePriorityThread(IN PKTHREAD Thread,
1056 IN LONG Increment)
1057 {
1058 KIRQL OldIrql;
1059 KPRIORITY OldBasePriority, Priority, BasePriority;
1060 LONG OldIncrement;
1061 PKPROCESS Process;
1062 BOOLEAN Released;
1063 ASSERT_THREAD(Thread);
1064 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1065
1066 /* Get the process */
1067 Process = Thread->ApcState.Process;
1068
1069 /* Lock the Dispatcher Database */
1070 OldIrql = KiAcquireDispatcherLock();
1071
1072 /* Lock the thread */
1073 KiAcquireThreadLock(Thread);
1074
1075 /* Save the old base priority and increment */
1076 OldBasePriority = Thread->BasePriority;
1077 OldIncrement = OldBasePriority - Process->BasePriority;
1078
1079 /* If priority saturation happened, use the saturated increment */
1080 if (Thread->Saturation) OldIncrement = (HIGH_PRIORITY + 1) / 2 *
1081 Thread->Saturation;
1082
1083 /* Now check if saturation is being used for the new value */
1084 if (abs(Increment) >= ((HIGH_PRIORITY + 1) / 2))
1085 {
1086 /* Check if we need positive or negative saturation */
1087 Thread->Saturation = (Increment > 0) ? 1 : -1;
1088 }
1089
1090 /* Normalize the Base Priority */
1091 BasePriority = Process->BasePriority + Increment;
1092 if (Process->BasePriority >= LOW_REALTIME_PRIORITY)
1093 {
1094 /* Check if it's too low */
1095 if (BasePriority < LOW_REALTIME_PRIORITY)
1096 {
1097 /* Set it to the lowest real time level */
1098 BasePriority = LOW_REALTIME_PRIORITY;
1099 }
1100
1101 /* Check if it's too high */
1102 if (BasePriority > HIGH_PRIORITY) BasePriority = HIGH_PRIORITY;
1103
1104 /* We are at real time, so use the raw base priority */
1105 Priority = BasePriority;
1106 }
1107 else
1108 {
1109 /* Check if it's entering the real time range */
1110 if (BasePriority >= LOW_REALTIME_PRIORITY)
1111 {
1112 /* Set it to the highest dynamic level */
1113 BasePriority = LOW_REALTIME_PRIORITY - 1;
1114 }
1115
1116 /* Check if it's too low and normalize it */
1117 if (BasePriority <= LOW_PRIORITY) BasePriority = 1;
1118
1119 /* Check if Saturation is used */
1120 if (Thread->Saturation)
1121 {
1122 /* Use the raw base priority */
1123 Priority = BasePriority;
1124 }
1125 else
1126 {
1127 /* Otherwise, calculate the new priority */
1128 Priority = KiComputeNewPriority(Thread);
1129
1130 /* Check if it entered the real-time range */
1131 if (Priority >= LOW_REALTIME_PRIORITY)
1132 {
1133 /* Normalize it down to the highest dynamic priority */
1134 Priority = LOW_REALTIME_PRIORITY - 1;
1135 }
1136 }
1137 }
1138
1139 /* Finally set the new base priority */
1140 Thread->BasePriority = (SCHAR)BasePriority;
1141
1142 /* Reset the decrements */
1143 Thread->PriorityDecrement = 0;
1144
1145 /* Check if we're changing priority after all */
1146 if (Priority != Thread->Priority)
1147 {
1148 /* Reset the quantum and do the actual priority modification */
1149 Thread->Quantum = Thread->QuantumReset;
1150 KiSetPriorityThread(Thread, Priority, &Released);
1151 }
1152
1153 /* Release thread lock */
1154 KiReleaseThreadLock(Thread);
1155
1156 /* Check if lock was released */
1157 if (!Released)
1158 {
1159 /* Release the dispatcher database */
1160 KiReleaseDispatcherLock(OldIrql);
1161 }
1162 else
1163 {
1164 /* Lower IRQL only */
1165 KeLowerIrql(OldIrql);
1166 }
1167
1168 /* Return old increment */
1169 return OldIncrement;
1170 }
1171
1172 /*
1173 * @implemented
1174 */
1175 KAFFINITY
1176 NTAPI
1177 KeSetAffinityThread(IN PKTHREAD Thread,
1178 IN KAFFINITY Affinity)
1179 {
1180 KIRQL OldIrql;
1181 KAFFINITY OldAffinity;
1182 BOOLEAN Released;
1183 ASSERT_THREAD(Thread);
1184 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1185
1186 /* Lock the dispatcher database */
1187 OldIrql = KiAcquireDispatcherLock();
1188
1189 /* Call the internal function */
1190 OldAffinity = KiSetAffinityThread(Thread, Affinity, &Released);
1191
1192 /* Check if lock was released */
1193 if (!Released)
1194 {
1195 /* Release the dispatcher database */
1196 KiReleaseDispatcherLock(OldIrql);
1197 }
1198 else
1199 {
1200 /* Lower IRQL only */
1201 KeLowerIrql(OldIrql);
1202 }
1203
1204 /* Return old affinity */
1205 return OldAffinity;
1206 }
1207
1208 /*
1209 * @implemented
1210 */
1211 KPRIORITY
1212 NTAPI
1213 KeSetPriorityThread(IN PKTHREAD Thread,
1214 IN KPRIORITY Priority)
1215 {
1216 KIRQL OldIrql;
1217 KPRIORITY OldPriority;
1218 BOOLEAN Released;
1219 ASSERT_THREAD(Thread);
1220 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1221 ASSERT((Priority <= HIGH_PRIORITY) && (Priority >= LOW_PRIORITY));
1222
1223 /* Lock the Dispatcher Database */
1224 OldIrql = KiAcquireDispatcherLock();
1225
1226 /* Lock the thread */
1227 KiAcquireThreadLock(Thread);
1228
1229 /* Save the old Priority */
1230 OldPriority = Thread->Priority;
1231
1232 /* Make sure that an actual change is being done */
1233 if (OldPriority != Priority)
1234 {
1235 /* Reset the Quantum and Decrements */
1236 Thread->Quantum = Thread->QuantumReset;
1237 Thread->PriorityDecrement = 0;
1238
1239 /* Set the new Priority */
1240 KiSetPriorityThread(Thread, Priority, &Released);
1241 }
1242
1243 /* Release thread lock */
1244 KiReleaseThreadLock(Thread);
1245
1246 /* Check if lock was released */
1247 if (!Released)
1248 {
1249 /* Release the dispatcher database */
1250 KiReleaseDispatcherLock(OldIrql);
1251 }
1252 else
1253 {
1254 /* Lower IRQL only */
1255 KeLowerIrql(OldIrql);
1256 }
1257
1258 /* Return Old Priority */
1259 return OldPriority;
1260 }
1261
1262 /*
1263 * @implemented
1264 */
1265 VOID
1266 NTAPI
1267 KeTerminateThread(IN KPRIORITY Increment)
1268 {
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);
1276
1277 /* Lock the process */
1278 KiAcquireProcessLock(Process, &LockHandle);
1279
1280 /* Make sure we won't get Swapped */
1281 KiSetThreadSwapBusy(Thread);
1282
1283 /* Save the Kernel and User Times */
1284 Process->KernelTime += Thread->KernelTime;
1285 Process->UserTime += Thread->UserTime;
1286
1287 /* Get the current entry and our Port */
1288 Entry = (PETHREAD)PspReaperListHead.Flink;
1289 ThreadAddr = &((PETHREAD)Thread)->ReaperLink;
1290
1291 /* Add it to the reaper's list */
1292 do
1293 {
1294 /* Get the list head */
1295 ListHead = &PspReaperListHead.Flink;
1296
1297 /* Link ourselves */
1298 *ThreadAddr = Entry;
1299 SavedEntry = Entry;
1300
1301 /* Now try to do the exchange */
1302 Entry = InterlockedCompareExchangePointer(ListHead, ThreadAddr, Entry);
1303
1304 /* Break out if the change was succesful */
1305 } while (Entry != SavedEntry);
1306
1307 /* Acquire the dispatcher lock */
1308 KiAcquireDispatcherLockAtDpcLevel();
1309
1310 /* Check if the reaper wasn't active */
1311 if (!Entry)
1312 {
1313 /* Activate it as a work item, directly through its Queue */
1314 KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
1315 &PspReaperWorkItem.List,
1316 FALSE);
1317 }
1318
1319 /* Check the thread has an associated queue */
1320 if (Thread->Queue)
1321 {
1322 /* Remove it from the list, and handle the queue */
1323 RemoveEntryList(&Thread->QueueListEntry);
1324 KiActivateWaiterQueue(Thread->Queue);
1325 }
1326
1327 /* Signal the thread */
1328 Thread->DispatcherHeader.SignalState = TRUE;
1329 if (!IsListEmpty(&Thread->DispatcherHeader.WaitListHead))
1330 {
1331 /* Unwait the threads */
1332 KxUnwaitThread(&Thread->DispatcherHeader, Increment);
1333 }
1334
1335 /* Remove the thread from the list */
1336 RemoveEntryList(&Thread->ThreadListEntry);
1337
1338 /* Release the process lock */
1339 KiReleaseProcessLockFromDpcLevel(&LockHandle);
1340
1341 /* Set us as terminated, decrease the Process's stack count */
1342 Thread->State = Terminated;
1343
1344 /* Decrease stack count */
1345 ASSERT(Process->StackCount != 0);
1346 ASSERT(Process->State == ProcessInMemory);
1347 Process->StackCount--;
1348 if (!Process->StackCount)
1349 {
1350 /* FIXME: Swap stacks */
1351 }
1352
1353 /* Rundown arch-specific parts */
1354 KiRundownThread(Thread);
1355
1356 /* Swap to a new thread */
1357 KiReleaseDispatcherLockFromDpcLevel();
1358 KiSwapThread(Thread, KeGetCurrentPrcb());
1359 }