Thread/Process Termination/Repeaing Rewrite + Fixes
[reactos.git] / reactos / ntoskrnl / ke / kthread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/kthread.c
5 * PURPOSE: Microkernel thread support
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 #define THREAD_ALERT_INCREMENT 2
18
19 extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
20
21 /*
22 * PURPOSE: List of threads associated with each priority level
23 */
24 LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
25 static ULONG PriorityListMask = 0;
26 ULONG IdleProcessorMask = 0;
27 extern BOOLEAN DoneInitYet;
28
29 /* FUNCTIONS *****************************************************************/
30
31 STATIC
32 VOID
33 KiRequestReschedule(CCHAR Processor)
34 {
35 PKPCR Pcr;
36
37 Pcr = (PKPCR)(KPCR_BASE + Processor * PAGE_SIZE);
38 Pcr->Prcb->QuantumEnd = TRUE;
39 KiIpiSendRequest(1 << Processor, IPI_REQUEST_DPC);
40 }
41
42 STATIC
43 VOID
44 KiInsertIntoThreadList(KPRIORITY Priority,
45 PKTHREAD Thread)
46 {
47 ASSERT(THREAD_STATE_READY == Thread->State);
48 ASSERT(Thread->Priority == Priority);
49
50 if (Priority >= MAXIMUM_PRIORITY || Priority < LOW_PRIORITY) {
51
52 DPRINT1("Invalid thread priority (%d)\n", Priority);
53 KEBUGCHECK(0);
54 }
55
56 InsertTailList(&PriorityListHead[Priority], &Thread->QueueListEntry);
57 PriorityListMask |= (1 << Priority);
58 }
59
60 STATIC
61 VOID
62 KiRemoveFromThreadList(PKTHREAD Thread)
63 {
64 ASSERT(THREAD_STATE_READY == Thread->State);
65 RemoveEntryList(&Thread->QueueListEntry);
66 if (IsListEmpty(&PriorityListHead[(ULONG)Thread->Priority])) {
67
68 PriorityListMask &= ~(1 << Thread->Priority);
69 }
70 }
71
72 STATIC
73 PKTHREAD
74 KiScanThreadList(KPRIORITY Priority,
75 KAFFINITY Affinity)
76 {
77 PLIST_ENTRY current_entry;
78 PKTHREAD current;
79 ULONG Mask;
80
81 Mask = (1 << Priority);
82
83 if (PriorityListMask & Mask) {
84
85 current_entry = PriorityListHead[Priority].Flink;
86
87 while (current_entry != &PriorityListHead[Priority]) {
88
89 current = CONTAINING_RECORD(current_entry, KTHREAD, QueueListEntry);
90
91 if (current->State != THREAD_STATE_READY) {
92
93 DPRINT1("%d/%d\n", &current, current->State);
94 }
95
96 ASSERT(current->State == THREAD_STATE_READY);
97
98 if (current->Affinity & Affinity) {
99
100 KiRemoveFromThreadList(current);
101 return(current);
102 }
103
104 current_entry = current_entry->Flink;
105 }
106 }
107
108 return(NULL);
109 }
110
111 VOID
112 STDCALL
113 KiDispatchThreadNoLock(ULONG NewThreadStatus)
114 {
115 KPRIORITY CurrentPriority;
116 PKTHREAD Candidate;
117 ULONG Affinity;
118 PKTHREAD CurrentThread = KeGetCurrentThread();
119
120 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
121 CurrentThread, NewThreadStatus, CurrentThread->State);
122
123 CurrentThread->State = (UCHAR)NewThreadStatus;
124
125 if (NewThreadStatus == THREAD_STATE_READY) {
126
127 KiInsertIntoThreadList(CurrentThread->Priority,
128 CurrentThread);
129 }
130
131 Affinity = 1 << KeGetCurrentProcessorNumber();
132
133 for (CurrentPriority = HIGH_PRIORITY; CurrentPriority >= LOW_PRIORITY; CurrentPriority--) {
134
135 Candidate = KiScanThreadList(CurrentPriority, Affinity);
136
137 if (Candidate == CurrentThread) {
138
139 Candidate->State = THREAD_STATE_RUNNING;
140 KeReleaseDispatcherDatabaseLockFromDpcLevel();
141 return;
142 }
143
144 if (Candidate != NULL) {
145
146 PKTHREAD OldThread;
147 PKTHREAD IdleThread;
148
149 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
150
151 Candidate->State = THREAD_STATE_RUNNING;
152
153 OldThread = CurrentThread;
154 CurrentThread = Candidate;
155 IdleThread = KeGetCurrentPrcb()->IdleThread;
156
157 if (OldThread == IdleThread) {
158
159 IdleProcessorMask &= ~Affinity;
160
161 } else if (CurrentThread == IdleThread) {
162
163 IdleProcessorMask |= Affinity;
164 }
165
166 MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD)CurrentThread)->ThreadsProcess, sizeof(EPROCESS));
167
168 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
169 KiArchContextSwitch(CurrentThread, OldThread);
170 return;
171 }
172 }
173
174 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
175 KEBUGCHECK(0);
176 }
177
178 VOID
179 STDCALL
180 KiBlockThread(PNTSTATUS Status,
181 UCHAR Alertable,
182 ULONG WaitMode,
183 UCHAR WaitReason)
184 {
185 PKTHREAD Thread = KeGetCurrentThread();
186 PKWAIT_BLOCK WaitBlock;
187
188 if (Thread->ApcState.KernelApcPending) {
189
190 DPRINT("Dispatching Thread as ready (APC!)\n");
191
192 /* Remove Waits */
193 WaitBlock = Thread->WaitBlockList;
194 while (WaitBlock) {
195 RemoveEntryList (&WaitBlock->WaitListEntry);
196 WaitBlock = WaitBlock->NextWaitBlock;
197 }
198 Thread->WaitBlockList = NULL;
199
200 /* Dispatch it and return status */
201 KiDispatchThreadNoLock (THREAD_STATE_READY);
202 if (Status != NULL) *Status = STATUS_KERNEL_APC;
203
204 } else {
205
206 /* Set the Thread Data as Requested */
207 DPRINT("Dispatching Thread as blocked\n");
208 Thread->Alertable = Alertable;
209 Thread->WaitMode = (UCHAR)WaitMode;
210 Thread->WaitReason = WaitReason;
211
212 /* Dispatch it and return status */
213 KiDispatchThreadNoLock(THREAD_STATE_BLOCKED);
214 if (Status != NULL) *Status = Thread->WaitStatus;
215 }
216
217 DPRINT("Releasing Dispatcher Lock\n");
218 KfLowerIrql(Thread->WaitIrql);
219 }
220
221 VOID
222 STDCALL
223 KiDispatchThread(ULONG NewThreadStatus)
224 {
225 KIRQL OldIrql;
226
227 if (!DoneInitYet || KeGetCurrentPrcb()->IdleThread == NULL) {
228 return;
229 }
230
231 OldIrql = KeAcquireDispatcherDatabaseLock();
232 KiDispatchThreadNoLock(NewThreadStatus);
233 KeLowerIrql(OldIrql);
234 }
235
236 VOID
237 STDCALL
238 KiUnblockThread(PKTHREAD Thread,
239 PNTSTATUS WaitStatus,
240 KPRIORITY Increment)
241 {
242 if (THREAD_STATE_TERMINATED_1 == Thread->State ||
243 THREAD_STATE_TERMINATED_2 == Thread->State) {
244
245 DPRINT("Can't unblock thread %d because it's terminating\n",
246 Thread->Cid.UniqueThread);
247
248 } else if (THREAD_STATE_READY == Thread->State ||
249 THREAD_STATE_RUNNING == Thread->State) {
250
251 DPRINT("Can't unblock thread %d because it's ready or running\n",
252 Thread->Cid.UniqueThread);
253
254 } else {
255
256 ULONG Processor;
257 KAFFINITY Affinity;
258
259 /* FIXME: This propably isn't the right way to do it... */
260 /* No it's not... i'll fix it later-- Alex */
261 if (Thread->Priority < LOW_REALTIME_PRIORITY &&
262 Thread->BasePriority < LOW_REALTIME_PRIORITY - 2) {
263
264 if (!Thread->PriorityDecrement && !Thread->DisableBoost) {
265
266 Thread->Priority = Thread->BasePriority + Increment;
267 Thread->PriorityDecrement = Increment;
268 }
269
270 } else {
271
272 Thread->Quantum = Thread->ApcState.Process->ThreadQuantum;
273 }
274
275 if (WaitStatus != NULL) {
276
277 Thread->WaitStatus = *WaitStatus;
278 }
279
280 Thread->State = THREAD_STATE_READY;
281 KiInsertIntoThreadList(Thread->Priority, Thread);
282 Processor = KeGetCurrentProcessorNumber();
283 Affinity = Thread->Affinity;
284
285 if (!(IdleProcessorMask & (1 << Processor) & Affinity) &&
286 (IdleProcessorMask & ~(1 << Processor) & Affinity)) {
287
288 ULONG i;
289
290 for (i = 0; i < KeNumberProcessors - 1; i++) {
291
292 Processor++;
293
294 if (Processor >= KeNumberProcessors) {
295
296 Processor = 0;
297 }
298
299 if (IdleProcessorMask & (1 << Processor) & Affinity) {
300 #if 0
301 /* FIXME:
302 * Reschedule the threads on an other processor
303 */
304 KeReleaseDispatcherDatabaseLockFromDpcLevel();
305 KiRequestReschedule(Processor);
306 KeAcquireDispatcherDatabaseLockAtDpcLevel();
307 #endif
308 break;
309 }
310 }
311 }
312 }
313 }
314
315 VOID
316 STDCALL
317 KiSuspendThreadKernelRoutine(PKAPC Apc,
318 PKNORMAL_ROUTINE* NormalRoutine,
319 PVOID* NormalContext,
320 PVOID* SystemArgument1,
321 PVOID* SystemArguemnt2)
322 {
323 }
324
325 VOID
326 STDCALL
327 KiSuspendThreadNormalRoutine(PVOID NormalContext,
328 PVOID SystemArgument1,
329 PVOID SystemArgument2)
330 {
331 PKTHREAD CurrentThread = KeGetCurrentThread();
332
333 /* Non-alertable kernel-mode suspended wait */
334 DPRINT1("Waiting...\n");
335 KeWaitForSingleObject(&CurrentThread->SuspendSemaphore,
336 Suspended,
337 KernelMode,
338 FALSE,
339 NULL);
340 DPRINT1("Done Waiting\n");
341 }
342
343 VOID
344 STDCALL
345 KeRundownThread(VOID)
346 {
347 KIRQL OldIrql;
348 PKTHREAD Thread = KeGetCurrentThread();
349 PLIST_ENTRY CurrentEntry;
350 PKMUTANT Mutant;
351
352 DPRINT("KeRundownThread: %x\n", Thread);
353
354 /* Lock the Dispatcher Database */
355 OldIrql = KeAcquireDispatcherDatabaseLock();
356
357 CurrentEntry = Thread->MutantListHead.Flink;
358 while (CurrentEntry != &Thread->MutantListHead) {
359
360 /* Get the Mutant */
361 Mutant = CONTAINING_RECORD(CurrentEntry, KMUTANT, MutantListEntry);
362 ASSERT(Mutant->ApcDisable);
363
364 /* Uncondtionally abandon it */
365 DPRINT("Abandonning the Mutant\n");
366 Mutant->Header.SignalState = 1;
367 Mutant->Abandoned = TRUE;
368 Mutant->OwnerThread = NULL;
369 RemoveEntryList(&Mutant->MutantListEntry);
370
371 /* Check if the Wait List isn't empty */
372 DPRINT("Checking whether to wake the Mutant\n");
373 if (!IsListEmpty(&Mutant->Header.WaitListHead)) {
374
375 /* Wake the Mutant */
376 DPRINT("Waking the Mutant\n");
377 KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
378 }
379 }
380
381 /* Release the Lock */
382 KeReleaseDispatcherDatabaseLock(OldIrql);
383 }
384
385 ULONG
386 STDCALL
387 KeResumeThread(PKTHREAD Thread)
388 {
389 ULONG PreviousCount;
390 KIRQL OldIrql;
391
392 DPRINT1("KeResumeThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount);
393
394 /* Lock the Dispatcher */
395 OldIrql = KeAcquireDispatcherDatabaseLock();
396
397 /* Save the Old Count */
398 PreviousCount = Thread->SuspendCount;
399
400 /* Check if it existed */
401 if (PreviousCount) {
402
403 Thread->SuspendCount--;
404
405 /* Decrease the current Suspend Count and Check Freeze Count */
406 if ((!Thread->SuspendCount) && (!Thread->FreezeCount)) {
407
408 /* Signal the Suspend Semaphore */
409 Thread->SuspendSemaphore.Header.SignalState++;
410 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
411 }
412 }
413
414 /* Release Lock and return the Old State */
415 KeReleaseDispatcherDatabaseLock(OldIrql);
416 return PreviousCount;
417 }
418
419 BOOLEAN
420 STDCALL
421 KiInsertQueueApc(PKAPC Apc,
422 KPRIORITY PriorityBoost);
423
424 NTSTATUS
425 STDCALL
426 KeSuspendThread(PKTHREAD Thread)
427 {
428 ULONG PreviousCount;
429 KIRQL OldIrql;
430
431 DPRINT1("KeSuspendThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount);
432
433 /* Lock the Dispatcher */
434 OldIrql = KeAcquireDispatcherDatabaseLock();
435
436 /* Save the Old Count */
437 PreviousCount = Thread->SuspendCount;
438
439 /* Increment it */
440 Thread->SuspendCount++;
441
442 /* Check if we should suspend it */
443 if (!PreviousCount && !Thread->FreezeCount) {
444
445 /* Insert the APC */
446 if (!KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT)) {
447
448 /* Unsignal the Semaphore, the APC already got inserted */
449 Thread->SuspendSemaphore.Header.SignalState--;
450 }
451 }
452
453 /* Release Lock and return the Old State */
454 KeReleaseDispatcherDatabaseLock(OldIrql);
455 return PreviousCount;
456 }
457
458 ULONG
459 STDCALL
460 KeForceResumeThread(IN PKTHREAD Thread)
461 {
462 KIRQL OldIrql;
463 ULONG PreviousCount;
464
465 /* Lock the Dispatcher Database and the APC Queue */
466 OldIrql = KeAcquireDispatcherDatabaseLock();
467
468 /* Save the old Suspend Count */
469 PreviousCount = Thread->SuspendCount + Thread->FreezeCount;
470
471 /* If the thread is suspended, wake it up!!! */
472 if (PreviousCount) {
473
474 /* Unwait it completely */
475 Thread->SuspendCount = 0;
476 Thread->FreezeCount = 0;
477
478 /* Signal and satisfy */
479 Thread->SuspendSemaphore.Header.SignalState++;
480 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
481 }
482
483 /* Release Lock and return the Old State */
484 KeReleaseDispatcherDatabaseLock(OldIrql);
485 return PreviousCount;
486 }
487
488 ULONG
489 STDCALL
490 KeAlertResumeThread(IN PKTHREAD Thread)
491 {
492 ULONG PreviousCount;
493 KIRQL OldIrql;
494
495 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
496
497 /* Lock the Dispatcher Database and the APC Queue */
498 OldIrql = KeAcquireDispatcherDatabaseLock();
499 KiAcquireSpinLock(&Thread->ApcQueueLock);
500
501 /* Return if Thread is already alerted. */
502 if (Thread->Alerted[KernelMode] == FALSE) {
503
504 /* If it's Blocked, unblock if it we should */
505 if (Thread->State == THREAD_STATE_BLOCKED && Thread->Alertable) {
506
507 DPRINT("Aborting Wait\n");
508 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
509
510 } else {
511
512 /* If not, simply Alert it */
513 Thread->Alerted[KernelMode] = TRUE;
514 }
515 }
516
517 /* Save the old Suspend Count */
518 PreviousCount = Thread->SuspendCount;
519
520 /* If the thread is suspended, decrease one of the suspend counts */
521 if (PreviousCount) {
522
523 /* Decrease count. If we are now zero, unwait it completely */
524 if (--Thread->SuspendCount) {
525
526 /* Signal and satisfy */
527 Thread->SuspendSemaphore.Header.SignalState++;
528 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
529 }
530 }
531
532 /* Release Locks and return the Old State */
533 KiReleaseSpinLock(&Thread->ApcQueueLock);
534 KeReleaseDispatcherDatabaseLock(OldIrql);
535 return PreviousCount;
536 }
537
538 BOOLEAN
539 STDCALL
540 KeAlertThread(PKTHREAD Thread,
541 KPROCESSOR_MODE AlertMode)
542 {
543 KIRQL OldIrql;
544 BOOLEAN PreviousState;
545
546 /* Acquire the Dispatcher Database Lock */
547 OldIrql = KeAcquireDispatcherDatabaseLock();
548
549 /* Save the Previous State */
550 PreviousState = Thread->Alerted[AlertMode];
551
552 /* Return if Thread is already alerted. */
553 if (PreviousState == FALSE) {
554
555 /* If it's Blocked, unblock if it we should */
556 if (Thread->State == THREAD_STATE_BLOCKED &&
557 (AlertMode == KernelMode || Thread->WaitMode == AlertMode) &&
558 Thread->Alertable) {
559
560 DPRINT("Aborting Wait\n");
561 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
562
563 } else {
564
565 /* If not, simply Alert it */
566 Thread->Alerted[AlertMode] = TRUE;
567 }
568 }
569
570 /* Release the Dispatcher Lock */
571 KeReleaseDispatcherDatabaseLock(OldIrql);
572
573 /* Return the old state */
574 return PreviousState;
575 }
576
577 /*
578 * @unimplemented
579 */
580 VOID
581 STDCALL
582 KeCapturePersistentThreadState(IN PVOID CurrentThread,
583 IN ULONG Setting1,
584 IN ULONG Setting2,
585 IN ULONG Setting3,
586 IN ULONG Setting4,
587 IN ULONG Setting5,
588 IN PVOID ThreadState)
589 {
590 UNIMPLEMENTED;
591 }
592
593 /*
594 * FUNCTION: Initialize the microkernel state of the thread
595 */
596 VOID
597 STDCALL
598 KeInitializeThread(PKPROCESS Process,
599 PKTHREAD Thread,
600 BOOLEAN First)
601 {
602 PVOID KernelStack;
603 NTSTATUS Status;
604 extern unsigned int init_stack_top;
605 extern unsigned int init_stack;
606 PMEMORY_AREA StackArea;
607 ULONG i;
608 PHYSICAL_ADDRESS BoundaryAddressMultiple;
609
610 /* Initialize the Boundary Address */
611 BoundaryAddressMultiple.QuadPart = 0;
612
613 /* Initalize the Dispatcher Header */
614 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
615 ThreadObject,
616 sizeof(KTHREAD),
617 FALSE);
618 InitializeListHead(&Thread->MutantListHead);
619
620 /* If this is isn't the first thread, allocate the Kernel Stack */
621 if (!First) {
622
623 PFN_TYPE Page[MM_STACK_SIZE / PAGE_SIZE];
624 KernelStack = NULL;
625
626 MmLockAddressSpace(MmGetKernelAddressSpace());
627 Status = MmCreateMemoryArea(NULL,
628 MmGetKernelAddressSpace(),
629 MEMORY_AREA_KERNEL_STACK,
630 &KernelStack,
631 MM_STACK_SIZE,
632 0,
633 &StackArea,
634 FALSE,
635 FALSE,
636 BoundaryAddressMultiple);
637 MmUnlockAddressSpace(MmGetKernelAddressSpace());
638
639 /* Check for Success */
640 if (!NT_SUCCESS(Status)) {
641
642 DPRINT1("Failed to create thread stack\n");
643 KEBUGCHECK(0);
644 }
645
646 /* Mark the Stack */
647 for (i = 0; i < (MM_STACK_SIZE / PAGE_SIZE); i++) {
648
649 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]);
650
651 /* Check for success */
652 if (!NT_SUCCESS(Status)) {
653
654 KEBUGCHECK(0);
655 }
656 }
657
658 /* Create a Virtual Mapping for it */
659 Status = MmCreateVirtualMapping(NULL,
660 KernelStack,
661 PAGE_READWRITE,
662 Page,
663 MM_STACK_SIZE / PAGE_SIZE);
664
665 /* Check for success */
666 if (!NT_SUCCESS(Status)) {
667
668 KEBUGCHECK(0);
669 }
670
671 /* Set the Kernel Stack */
672 Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE;
673 Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE;
674 Thread->StackLimit = (ULONG_PTR)KernelStack;
675 Thread->KernelStack = (PCHAR)KernelStack + MM_STACK_SIZE;
676
677 } else {
678
679 /* Use the Initial Stack */
680 Thread->InitialStack = (PCHAR)init_stack_top;
681 Thread->StackBase = (PCHAR)init_stack_top;
682 Thread->StackLimit = (ULONG_PTR)init_stack;
683 Thread->KernelStack = (PCHAR)init_stack_top;
684 }
685
686 /*
687 * Establish the pde's for the new stack and the thread structure within the
688 * address space of the new process. They are accessed while taskswitching or
689 * while handling page faults. At this point it isn't possible to call the
690 * page fault handler for the missing pde's.
691 */
692 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
693 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
694
695 /* Set the Thread to initalized */
696 Thread->State = THREAD_STATE_INITIALIZED;
697
698 /* The Native API function will initialize the TEB field later */
699 Thread->Teb = NULL;
700
701 /* Initialize stuff to zero */
702 Thread->TlsArray = NULL;
703 Thread->DebugActive = 0;
704 Thread->Alerted[0] = 0;
705 Thread->Alerted[1] = 0;
706 Thread->Iopl = 0;
707
708 /* Set up FPU/NPX Stuff */
709 Thread->NpxState = NPX_STATE_INVALID;
710 Thread->NpxIrql = 0;
711
712 /* Setup APC Fields */
713 InitializeListHead(&Thread->ApcState.ApcListHead[0]);
714 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
715 Thread->ApcState.Process = Process;
716 Thread->ApcState.KernelApcInProgress = 0;
717 Thread->ApcState.KernelApcPending = 0;
718 Thread->ApcState.UserApcPending = 0;
719 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
720 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
721 Thread->ApcStateIndex = OriginalApcEnvironment;
722 Thread->ApcQueueable = TRUE;
723 RtlZeroMemory(&Thread->SavedApcState, sizeof(KAPC_STATE));
724 KeInitializeSpinLock(&Thread->ApcQueueLock);
725
726 /* Setup Wait Fields */
727 Thread->WaitStatus = STATUS_SUCCESS;
728 Thread->WaitIrql = PASSIVE_LEVEL;
729 Thread->WaitMode = 0;
730 Thread->WaitNext = FALSE;
731 Thread->WaitListEntry.Flink = NULL;
732 Thread->WaitListEntry.Blink = NULL;
733 Thread->WaitTime = 0;
734 Thread->WaitBlockList = NULL;
735 RtlZeroMemory(Thread->WaitBlock, sizeof(KWAIT_BLOCK) * 4);
736 RtlZeroMemory(&Thread->Timer, sizeof(KTIMER));
737 KeInitializeTimer(&Thread->Timer);
738
739 /* Setup scheduler Fields */
740 Thread->BasePriority = Process->BasePriority;
741 Thread->DecrementCount = 0;
742 Thread->PriorityDecrement = 0;
743 Thread->Quantum = Process->ThreadQuantum;
744 Thread->Saturation = 0;
745 Thread->Priority = Process->BasePriority;
746 Thread->UserAffinity = Process->Affinity;
747 Thread->SystemAffinityActive = 0;
748 Thread->Affinity = Process->Affinity;
749 Thread->Preempted = 0;
750 Thread->ProcessReadyQueue = 0;
751 Thread->KernelStackResident = 1;
752 Thread->NextProcessor = 0;
753 Thread->ContextSwitches = 0;
754
755 /* Setup Queue Fields */
756 Thread->Queue = NULL;
757 Thread->QueueListEntry.Flink = NULL;
758 Thread->QueueListEntry.Blink = NULL;
759
760 /* Setup Misc Fields */
761 Thread->LegoData = 0;
762 Thread->PowerState = 0;
763 Thread->ServiceTable = KeServiceDescriptorTable;
764 Thread->CallbackStack = NULL;
765 Thread->Win32Thread = NULL;
766 Thread->TrapFrame = NULL;
767 Thread->EnableStackSwap = 0;
768 Thread->LargeStack = 0;
769 Thread->ResourceIndex = 0;
770 Thread->PreviousMode = KernelMode;
771 Thread->KernelTime = 0;
772 Thread->UserTime = 0;
773 Thread->AutoAlignment = Process->AutoAlignment;
774
775 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
776 #if 0
777 Thread->WaitBlock[3].Object = (PVOID)&Thread->Timer;
778 Thread->WaitBlock[3].Thread = Thread;
779 Thread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
780 Thread->WaitBlock[3].WaitType = WaitAny;
781 Thread->WaitBlock[3].NextWaitBlock = NULL;
782 InsertTailList(&Thread->Timer.Header.WaitListHead,
783 &Thread->WaitBlock[3].WaitListEntry);
784 #endif
785
786 /* Initialize the Suspend APC */
787 KeInitializeApc(&Thread->SuspendApc,
788 Thread,
789 OriginalApcEnvironment,
790 KiSuspendThreadKernelRoutine,
791 NULL,
792 KiSuspendThreadNormalRoutine,
793 KernelMode,
794 NULL);
795
796 /* Initialize the Suspend Semaphore */
797 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128);
798
799 /* Insert the Thread into the Process's Thread List */
800 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
801
802 /* Set up the Suspend Counts */
803 Thread->FreezeCount = 0;
804 Thread->SuspendCount = 0;
805
806 /* Do x86 specific part */
807 }
808
809 /*
810 * @implemented
811 */
812 KPRIORITY
813 STDCALL
814 KeQueryPriorityThread (IN PKTHREAD Thread)
815 {
816 return Thread->Priority;
817 }
818
819 /*
820 * @implemented
821 */
822 ULONG
823 STDCALL
824 KeQueryRuntimeThread(IN PKTHREAD Thread,
825 OUT PULONG UserTime)
826 {
827 /* Return the User Time */
828 *UserTime = Thread->UserTime;
829
830 /* Return the Kernel Time */
831 return Thread->KernelTime;
832 }
833
834 VOID
835 KeFreeStackPage(PVOID Context,
836 MEMORY_AREA* MemoryArea,
837 PVOID Address,
838 PFN_TYPE Page,
839 SWAPENTRY SwapEntry,
840 BOOLEAN Dirty)
841 {
842 ASSERT(SwapEntry == 0);
843 if (Page) MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
844 }
845
846 NTSTATUS
847 KeReleaseThread(PKTHREAD Thread)
848 /*
849 * FUNCTION: Releases the resource allocated for a thread by
850 * KeInitializeThread
851 * NOTE: The thread had better not be running when this is called
852 */
853 {
854 extern unsigned int init_stack;
855
856 /* FIXME - lock the process */
857 RemoveEntryList(&Thread->ThreadListEntry);
858
859 if (Thread->StackLimit != (ULONG_PTR)init_stack)
860 {
861 MmLockAddressSpace(MmGetKernelAddressSpace());
862 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
863 (PVOID)Thread->StackLimit,
864 KeFreeStackPage,
865 NULL);
866 MmUnlockAddressSpace(MmGetKernelAddressSpace());
867 }
868 Thread->StackLimit = 0;
869 Thread->InitialStack = NULL;
870 Thread->StackBase = NULL;
871 Thread->KernelStack = NULL;
872 return(STATUS_SUCCESS);
873 }
874
875 /*
876 * @implemented
877 */
878 BOOLEAN
879 STDCALL
880 KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
881 {
882 PKTHREAD Thread = KeGetCurrentThread();
883 BOOLEAN PreviousState;
884 KIRQL OldIrql;
885
886 /* Lock the Dispatcher Database */
887 OldIrql = KeAcquireDispatcherDatabaseLock();
888
889 /* Save Old State */
890 PreviousState = Thread->EnableStackSwap;
891
892 /* Set New State */
893 Thread->EnableStackSwap = Enable;
894
895 /* No, Release Lock */
896 KeReleaseDispatcherDatabaseLock(OldIrql);
897
898 /* Return Old State */
899 return PreviousState;
900 }
901
902 /*
903 * @implemented
904 */
905 VOID
906 STDCALL
907 KeRevertToUserAffinityThread(VOID)
908 {
909 PKTHREAD CurrentThread = KeGetCurrentThread();
910 KIRQL OldIrql;
911
912 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
913
914 /* Lock the Dispatcher Database */
915 OldIrql = KeAcquireDispatcherDatabaseLock();
916
917 /* Return to User Affinity */
918 CurrentThread->Affinity = CurrentThread->UserAffinity;
919
920 /* Disable System Affinity */
921 CurrentThread->SystemAffinityActive = FALSE;
922
923 /* Check if we need to Dispatch a New thread */
924 if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) {
925
926 /* No, just release */
927 KeReleaseDispatcherDatabaseLock(OldIrql);
928
929 } else {
930
931 /* We need to dispatch a new thread */
932 CurrentThread->WaitIrql = OldIrql;
933 KiDispatchThreadNoLock(THREAD_STATE_READY);
934 KeLowerIrql(OldIrql);
935 }
936 }
937
938 /*
939 * @implemented
940 */
941 CCHAR
942 STDCALL
943 KeSetIdealProcessorThread(IN PKTHREAD Thread,
944 IN CCHAR Processor)
945 {
946 CCHAR PreviousIdealProcessor;
947 KIRQL OldIrql;
948
949 /* Lock the Dispatcher Database */
950 OldIrql = KeAcquireDispatcherDatabaseLock();
951
952 /* Save Old Ideal Processor */
953 PreviousIdealProcessor = Thread->IdealProcessor;
954
955 /* Set New Ideal Processor */
956 Thread->IdealProcessor = Processor;
957
958 /* Release Lock */
959 KeReleaseDispatcherDatabaseLock(OldIrql);
960
961 /* Return Old Ideal Processor */
962 return PreviousIdealProcessor;
963 }
964
965 /*
966 * @implemented
967 */
968 VOID
969 STDCALL
970 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
971 {
972 PKTHREAD CurrentThread = KeGetCurrentThread();
973 KIRQL OldIrql;
974
975 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
976
977 /* Lock the Dispatcher Database */
978 OldIrql = KeAcquireDispatcherDatabaseLock();
979
980 /* Set the System Affinity Specified */
981 CurrentThread->Affinity = Affinity;
982
983 /* Enable System Affinity */
984 CurrentThread->SystemAffinityActive = TRUE;
985
986 /* Check if we need to Dispatch a New thread */
987 if (Affinity & (1 << KeGetCurrentProcessorNumber())) {
988
989 /* No, just release */
990 KeReleaseDispatcherDatabaseLock(OldIrql);
991
992 } else {
993
994 /* We need to dispatch a new thread */
995 CurrentThread->WaitIrql = OldIrql;
996 KiDispatchThreadNoLock(THREAD_STATE_READY);
997 KeLowerIrql(OldIrql);
998 }
999 }
1000
1001 /*
1002 * @implemented
1003 */
1004 LONG STDCALL
1005 KeSetBasePriorityThread (PKTHREAD Thread,
1006 LONG Increment)
1007 /*
1008 * Sets thread's base priority relative to the process' base priority
1009 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
1010 */
1011 {
1012 KPRIORITY Priority;
1013 if (Increment < -2)
1014 {
1015 Increment = -2;
1016 }
1017 else if (Increment > 2)
1018 {
1019 Increment = 2;
1020 }
1021 Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
1022 if (Priority < LOW_PRIORITY)
1023 {
1024 Priority = LOW_PRIORITY;
1025 }
1026 else if (Priority >= MAXIMUM_PRIORITY)
1027 {
1028 Thread->BasePriority = HIGH_PRIORITY;
1029 }
1030 KeSetPriorityThread(Thread, Priority);
1031 return 1;
1032 }
1033
1034 /*
1035 * @implemented
1036 */
1037 KPRIORITY
1038 STDCALL
1039 KeSetPriorityThread(PKTHREAD Thread,
1040 KPRIORITY Priority)
1041 {
1042 KPRIORITY OldPriority;
1043 KIRQL OldIrql;
1044 PKTHREAD CurrentThread;
1045 ULONG Mask;
1046 int i;
1047 PKPCR Pcr;
1048
1049 if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY) {
1050
1051 KEBUGCHECK(0);
1052 }
1053
1054 OldIrql = KeAcquireDispatcherDatabaseLock();
1055
1056 OldPriority = Thread->Priority;
1057
1058 if (OldPriority != Priority) {
1059
1060 CurrentThread = KeGetCurrentThread();
1061
1062 if (Thread->State == THREAD_STATE_READY) {
1063
1064 KiRemoveFromThreadList(Thread);
1065 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
1066 KiInsertIntoThreadList(Priority, Thread);
1067
1068 if (CurrentThread->Priority < Priority) {
1069
1070 KiDispatchThreadNoLock(THREAD_STATE_READY);
1071 KeLowerIrql(OldIrql);
1072 return (OldPriority);
1073 }
1074
1075 } else if (Thread->State == THREAD_STATE_RUNNING) {
1076
1077 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
1078
1079 if (Priority < OldPriority) {
1080
1081 /* Check for threads with a higher priority */
1082 Mask = ~((1 << (Priority + 1)) - 1);
1083 if (PriorityListMask & Mask) {
1084
1085 if (Thread == CurrentThread) {
1086
1087 KiDispatchThreadNoLock(THREAD_STATE_READY);
1088 KeLowerIrql(OldIrql);
1089 return (OldPriority);
1090
1091 } else {
1092
1093 for (i = 0; i < KeNumberProcessors; i++) {
1094
1095 Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
1096
1097 if (Pcr->Prcb->CurrentThread == Thread) {
1098
1099 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1100 KiRequestReschedule(i);
1101 KeLowerIrql(OldIrql);
1102 return (OldPriority);
1103 }
1104 }
1105 }
1106 }
1107 }
1108 } else {
1109
1110 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
1111 }
1112 }
1113
1114 KeReleaseDispatcherDatabaseLock(OldIrql);
1115 return(OldPriority);
1116 }
1117
1118 /*
1119 * @implemented
1120 *
1121 * Sets thread's affinity
1122 */
1123 NTSTATUS
1124 STDCALL
1125 KeSetAffinityThread(PKTHREAD Thread,
1126 KAFFINITY Affinity)
1127 {
1128 KIRQL OldIrql;
1129 ULONG i;
1130 PKPCR Pcr;
1131 KAFFINITY ProcessorMask;
1132
1133 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity);
1134
1135 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
1136
1137 OldIrql = KeAcquireDispatcherDatabaseLock();
1138
1139 Thread->UserAffinity = Affinity;
1140
1141 if (Thread->SystemAffinityActive == FALSE) {
1142
1143 Thread->Affinity = Affinity;
1144
1145 if (Thread->State == THREAD_STATE_RUNNING) {
1146
1147 ProcessorMask = 1 << KeGetCurrentKPCR()->ProcessorNumber;
1148 if (Thread == KeGetCurrentThread()) {
1149
1150 if (!(Affinity & ProcessorMask)) {
1151
1152 KiDispatchThreadNoLock(THREAD_STATE_READY);
1153 KeLowerIrql(OldIrql);
1154 return STATUS_SUCCESS;
1155 }
1156
1157 } else {
1158
1159 for (i = 0; i < KeNumberProcessors; i++) {
1160
1161 Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
1162 if (Pcr->Prcb->CurrentThread == Thread) {
1163
1164 if (!(Affinity & ProcessorMask)) {
1165
1166 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1167 KiRequestReschedule(i);
1168 KeLowerIrql(OldIrql);
1169 return STATUS_SUCCESS;
1170 }
1171
1172 break;
1173 }
1174 }
1175
1176 ASSERT (i < KeNumberProcessors);
1177 }
1178 }
1179 }
1180
1181 KeReleaseDispatcherDatabaseLock(OldIrql);
1182 return STATUS_SUCCESS;
1183 }
1184
1185 /*
1186 * @implemented
1187 */
1188 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1189 VOID
1190 STDCALL
1191 KeTerminateThread(IN KPRIORITY Increment)
1192 {
1193 KIRQL OldIrql;
1194 PKTHREAD Thread = KeGetCurrentThread();
1195
1196 /* Lock the Dispatcher Database and the APC Queue */
1197 DPRINT1("Terminating\n");
1198 OldIrql = KeAcquireDispatcherDatabaseLock();
1199
1200 /* Insert into the Reaper List */
1201 InsertTailList(&PspReaperListHead, &((PETHREAD)Thread)->TerminationPortList);
1202
1203 /* Check if it's active */
1204 if (PspReaping == FALSE) {
1205
1206 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1207 PspReaping = TRUE;
1208 DPRINT1("Terminating\n");
1209 KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
1210 &PspReaperWorkItem.List,
1211 FALSE);
1212 }
1213
1214 /* Handle Kernel Queues */
1215 if (Thread->Queue) {
1216
1217 DPRINT1("Waking Queue\n");
1218 RemoveEntryList(&Thread->QueueListEntry);
1219 KiWakeQueue(Thread->Queue);
1220 }
1221
1222 /* Signal the thread */
1223 Thread->DispatcherHeader.SignalState = TRUE;
1224 if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE) {
1225
1226 /* Satisfy waits */
1227 KiWaitTest((PVOID)Thread, Increment);
1228 }
1229
1230 /* Find a new Thread */
1231 KiDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
1232 }
1233
1234 /*
1235 * FUNCTION: Tests whether there are any pending APCs for the current thread
1236 * and if so the APCs will be delivered on exit from kernel mode
1237 */
1238 BOOLEAN
1239 STDCALL
1240 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
1241 {
1242 KIRQL OldIrql;
1243 PKTHREAD Thread = KeGetCurrentThread();
1244 BOOLEAN OldState;
1245
1246 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1247
1248 /* Lock the Dispatcher Database and the APC Queue */
1249 OldIrql = KeAcquireDispatcherDatabaseLock();
1250 KiAcquireSpinLock(&Thread->ApcQueueLock);
1251
1252 /* Save the old State */
1253 OldState = Thread->Alerted[AlertMode];
1254
1255 /* If the Thread is Alerted, Clear it */
1256 if (OldState) {
1257
1258 Thread->Alerted[AlertMode] = FALSE;
1259
1260 } else if ((AlertMode == UserMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) {
1261
1262 /* If the mode is User and the Queue isn't empty, set Pending */
1263 Thread->ApcState.UserApcPending = TRUE;
1264 }
1265
1266 /* Release Locks and return the Old State */
1267 KiReleaseSpinLock(&Thread->ApcQueueLock);
1268 KeReleaseDispatcherDatabaseLock(OldIrql);
1269 return OldState;
1270 }
1271
1272 VOID
1273 KiServiceCheck (VOID)
1274 {
1275 PKTHREAD Thread = KeGetCurrentThread();
1276
1277 /* Check if we need to inialize Win32 for this Thread */
1278 if (Thread->ServiceTable != KeServiceDescriptorTableShadow) {
1279
1280 /* We do. Initialize it and save the new table */
1281 PsInitWin32Thread((PETHREAD)Thread);
1282 Thread->ServiceTable = KeServiceDescriptorTableShadow;
1283 }
1284 }
1285
1286 /*
1287 *
1288 * NOT EXPORTED
1289 */
1290 NTSTATUS
1291 STDCALL
1292 NtAlertResumeThread(IN HANDLE ThreadHandle,
1293 OUT PULONG SuspendCount)
1294 {
1295 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1296 PETHREAD Thread;
1297 NTSTATUS Status;
1298 ULONG PreviousState;
1299
1300 /* Check if parameters are valid */
1301 if(PreviousMode != KernelMode) {
1302
1303 _SEH_TRY {
1304
1305 ProbeForWrite(SuspendCount,
1306 sizeof(HANDLE),
1307 sizeof(ULONG));
1308
1309 } _SEH_HANDLE {
1310
1311 Status = _SEH_GetExceptionCode();
1312
1313 } _SEH_END;
1314 }
1315
1316 /* Reference the Object */
1317 Status = ObReferenceObjectByHandle(ThreadHandle,
1318 THREAD_SUSPEND_RESUME,
1319 PsThreadType,
1320 PreviousMode,
1321 (PVOID*)&Thread,
1322 NULL);
1323
1324 /* Check for Success */
1325 if (NT_SUCCESS(Status)) {
1326
1327 /* Call the Kernel Function */
1328 PreviousState = KeAlertResumeThread(&Thread->Tcb);
1329
1330 /* Dereference Object */
1331 ObDereferenceObject(Thread);
1332
1333 if (SuspendCount) {
1334
1335 _SEH_TRY {
1336
1337 *SuspendCount = PreviousState;
1338
1339 } _SEH_HANDLE {
1340
1341 Status = _SEH_GetExceptionCode();
1342
1343 } _SEH_END;
1344 }
1345 }
1346
1347 /* Return status */
1348 return Status;
1349 }
1350
1351 /*
1352 * @implemented
1353 *
1354 * EXPORTED
1355 */
1356 NTSTATUS
1357 STDCALL
1358 NtAlertThread (IN HANDLE ThreadHandle)
1359 {
1360 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1361 PETHREAD Thread;
1362 NTSTATUS Status;
1363
1364 /* Reference the Object */
1365 Status = ObReferenceObjectByHandle(ThreadHandle,
1366 THREAD_SUSPEND_RESUME,
1367 PsThreadType,
1368 PreviousMode,
1369 (PVOID*)&Thread,
1370 NULL);
1371
1372 /* Check for Success */
1373 if (NT_SUCCESS(Status)) {
1374
1375 /*
1376 * Do an alert depending on the processor mode. If some kmode code wants to
1377 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1378 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1379 * use KeAlertThread() directly
1380 */
1381 KeAlertThread(&Thread->Tcb, PreviousMode);
1382
1383 /* Dereference Object */
1384 ObDereferenceObject(Thread);
1385 }
1386
1387 /* Return status */
1388 return Status;
1389 }
1390
1391 NTSTATUS
1392 STDCALL
1393 NtDelayExecution(IN BOOLEAN Alertable,
1394 IN PLARGE_INTEGER DelayInterval)
1395 {
1396 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1397 LARGE_INTEGER SafeInterval;
1398 NTSTATUS Status;
1399
1400 /* Check if parameters are valid */
1401 if(PreviousMode != KernelMode) {
1402
1403 _SEH_TRY {
1404
1405 ProbeForRead(DelayInterval,
1406 sizeof(LARGE_INTEGER),
1407 sizeof(ULONG));
1408
1409 /* make a copy on the kernel stack and let DelayInterval point to it so
1410 we don't need to wrap KeDelayExecutionThread in SEH! */
1411 SafeInterval = *DelayInterval;
1412
1413 } _SEH_HANDLE {
1414
1415 Status = _SEH_GetExceptionCode();
1416 } _SEH_END;
1417 }
1418
1419 /* Call the Kernel Function */
1420 Status = KeDelayExecutionThread(PreviousMode,
1421 Alertable,
1422 &SafeInterval);
1423
1424 /* Return Status */
1425 return Status;
1426 }