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