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