Merge 13159:13510 from trunk
[reactos.git] / reactos / ntoskrnl / ke / kthread.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/kthread.c
6 * PURPOSE: Microkernel thread support
7 *
8 * PROGRAMMERS: David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* FUNCTIONS *****************************************************************/
18
19 VOID
20 KiServiceCheck (VOID)
21 {
22 PETHREAD Thread;
23
24 Thread = PsGetCurrentThread();
25
26 if (Thread->Tcb.ServiceTable != KeServiceDescriptorTableShadow)
27 {
28 PsInitWin32Thread (Thread);
29
30 Thread->Tcb.ServiceTable = KeServiceDescriptorTableShadow;
31 }
32 }
33
34 /*
35 * @unimplemented
36 */
37 VOID
38 STDCALL
39 KeCapturePersistentThreadState(
40 IN PVOID CurrentThread,
41 IN ULONG Setting1,
42 IN ULONG Setting2,
43 IN ULONG Setting3,
44 IN ULONG Setting4,
45 IN ULONG Setting5,
46 IN PVOID ThreadState
47 )
48 {
49 UNIMPLEMENTED;
50 }
51
52 VOID
53 KeFreeStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
54 PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
55 {
56 ASSERT(SwapEntry == 0);
57 if (Page != 0)
58 {
59 MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
60 }
61 }
62
63 /*
64 * @implemented
65 */
66 KPRIORITY
67 STDCALL
68 KeQueryPriorityThread (
69 IN PKTHREAD Thread
70 )
71 {
72 return Thread->Priority;
73 }
74
75 NTSTATUS
76 KeReleaseThread(PKTHREAD Thread)
77 /*
78 * FUNCTION: Releases the resource allocated for a thread by
79 * KeInitializeThread
80 * NOTE: The thread had better not be running when this is called
81 */
82 {
83 extern unsigned int init_stack;
84
85 /* FIXME - lock the process */
86 RemoveEntryList(&Thread->ThreadListEntry);
87
88 if (Thread->StackLimit != (ULONG_PTR)init_stack)
89 {
90 MmLockAddressSpace(MmGetKernelAddressSpace());
91 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
92 (PVOID)Thread->StackLimit,
93 KeFreeStackPage,
94 NULL);
95 MmUnlockAddressSpace(MmGetKernelAddressSpace());
96 }
97 Thread->StackLimit = 0;
98 Thread->InitialStack = NULL;
99 Thread->StackBase = NULL;
100 Thread->KernelStack = NULL;
101 return(STATUS_SUCCESS);
102 }
103
104 /*
105 * @implemented
106 */
107 BOOLEAN
108 STDCALL
109 KeSetKernelStackSwapEnable(
110 IN BOOLEAN Enable
111 )
112 {
113 PKTHREAD Thread;
114 BOOLEAN PreviousState;
115
116 Thread = KeGetCurrentThread();
117
118 /* Save Old State */
119 PreviousState = Thread->EnableStackSwap;
120
121 /* Set New State */
122 Thread->EnableStackSwap = Enable;
123
124 /* Return Old State */
125 return PreviousState;
126 }
127
128 VOID
129 KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First)
130 /*
131 * FUNCTION: Initialize the microkernel state of the thread
132 */
133 {
134 PVOID KernelStack;
135 NTSTATUS Status;
136 extern unsigned int init_stack_top;
137 extern unsigned int init_stack;
138 PMEMORY_AREA StackArea;
139 ULONG i;
140 PHYSICAL_ADDRESS BoundaryAddressMultiple;
141
142 BoundaryAddressMultiple.QuadPart = 0;
143
144 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
145 InternalThreadType,
146 sizeof(ETHREAD),
147 FALSE);
148 InitializeListHead(&Thread->MutantListHead);
149 if (!First)
150 {
151 PFN_TYPE Page[MM_STACK_SIZE / PAGE_SIZE];
152 KernelStack = NULL;
153
154 MmLockAddressSpace(MmGetKernelAddressSpace());
155 Status = MmCreateMemoryArea(NULL,
156 MmGetKernelAddressSpace(),
157 MEMORY_AREA_KERNEL_STACK,
158 &KernelStack,
159 MM_STACK_SIZE,
160 0,
161 &StackArea,
162 FALSE,
163 FALSE,
164 BoundaryAddressMultiple);
165 MmUnlockAddressSpace(MmGetKernelAddressSpace());
166
167 if (!NT_SUCCESS(Status))
168 {
169 DPRINT1("Failed to create thread stack\n");
170 KEBUGCHECK(0);
171 }
172 for (i = 0; i < (MM_STACK_SIZE / PAGE_SIZE); i++)
173 {
174 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]);
175 if (!NT_SUCCESS(Status))
176 {
177 KEBUGCHECK(0);
178 }
179 }
180 Status = MmCreateVirtualMapping(NULL,
181 KernelStack,
182 PAGE_READWRITE,
183 Page,
184 MM_STACK_SIZE / PAGE_SIZE);
185 if (!NT_SUCCESS(Status))
186 {
187 KEBUGCHECK(0);
188 }
189 Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE;
190 Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE;
191 Thread->StackLimit = (ULONG_PTR)KernelStack;
192 Thread->KernelStack = (PCHAR)KernelStack + MM_STACK_SIZE;
193 }
194 else
195 {
196 Thread->InitialStack = (PCHAR)init_stack_top;
197 Thread->StackBase = (PCHAR)init_stack_top;
198 Thread->StackLimit = (ULONG_PTR)init_stack;
199 Thread->KernelStack = (PCHAR)init_stack_top;
200 }
201
202 /*
203 * Establish the pde's for the new stack and the thread structure within the
204 * address space of the new process. They are accessed while taskswitching or
205 * while handling page faults. At this point it isn't possible to call the
206 * page fault handler for the missing pde's.
207 */
208
209 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
210 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
211
212 /*
213 * The Native API function will initialize the TEB field later
214 */
215 Thread->Teb = NULL;
216 Thread->TlsArray = NULL;
217 Thread->DebugActive = 0;
218 Thread->State = THREAD_STATE_INITIALIZED;
219 Thread->Alerted[0] = 0;
220 Thread->Alerted[1] = 0;
221 Thread->Iopl = 0;
222 /*
223 * FIXME: Think how this might work
224 */
225 Thread->NpxState = 0;
226
227 Thread->Saturation = 0;
228 Thread->Priority = Process->BasePriority;
229 InitializeListHead(&Thread->ApcState.ApcListHead[0]);
230 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
231 Thread->ApcState.Process = Process;
232 Thread->ApcState.KernelApcInProgress = 0;
233 Thread->ApcState.KernelApcPending = 0;
234 Thread->ApcState.UserApcPending = 0;
235 Thread->ContextSwitches = 0;
236 Thread->WaitStatus = STATUS_SUCCESS;
237 Thread->WaitIrql = PASSIVE_LEVEL;
238 Thread->WaitMode = 0;
239 Thread->WaitNext = FALSE;
240 Thread->WaitBlockList = NULL;
241 Thread->WaitListEntry.Flink = NULL;
242 Thread->WaitListEntry.Blink = NULL;
243 Thread->WaitTime = 0;
244 Thread->BasePriority = Process->BasePriority;
245 Thread->DecrementCount = 0;
246 Thread->PriorityDecrement = 0;
247 Thread->Quantum = Process->ThreadQuantum;
248 memset(Thread->WaitBlock, 0, sizeof(KWAIT_BLOCK)*4);
249 Thread->LegoData = 0;
250 Thread->UserAffinity = Process->Affinity;
251 Thread->SystemAffinityActive = 0;
252 Thread->PowerState = 0;
253 Thread->NpxIrql = 0;
254 Thread->ServiceTable = KeServiceDescriptorTable;
255 Thread->Queue = NULL;
256 KeInitializeSpinLock(&Thread->ApcQueueLock);
257 memset(&Thread->Timer, 0, sizeof(KTIMER));
258 KeInitializeTimer(&Thread->Timer);
259 Thread->QueueListEntry.Flink = NULL;
260 Thread->QueueListEntry.Blink = NULL;
261 Thread->Affinity = Process->Affinity;
262 Thread->Preempted = 0;
263 Thread->ProcessReadyQueue = 0;
264 Thread->KernelStackResident = 1;
265 Thread->NextProcessor = 0;
266 Thread->CallbackStack = NULL;
267 Thread->Win32Thread = NULL;
268 Thread->TrapFrame = NULL;
269 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
270 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
271 Thread->EnableStackSwap = 0;
272 Thread->LargeStack = 0;
273 Thread->ResourceIndex = 0;
274 Thread->PreviousMode = KernelMode;
275 Thread->KernelTime = 0;
276 Thread->UserTime = 0;
277 memset(&Thread->SavedApcState, 0, sizeof(KAPC_STATE));
278
279 Thread->ApcStateIndex = OriginalApcEnvironment;
280 Thread->ApcQueueable = TRUE;
281 Thread->AutoAlignment = Process->AutoAlignment;
282
283 KeInitializeApc(&Thread->SuspendApc,
284 Thread,
285 OriginalApcEnvironment,
286 PiSuspendThreadKernelRoutine,
287 PiSuspendThreadRundownRoutine,
288 PiSuspendThreadNormalRoutine,
289 KernelMode,
290 NULL);
291 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128);
292
293 InsertTailList(&Process->ThreadListHead,
294 &Thread->ThreadListEntry);
295 Thread->FreezeCount = 0;
296 Thread->SuspendCount = 0;
297
298 /*
299 * Do x86 specific part
300 */
301 }
302
303 /*
304 * @implemented
305 */
306 VOID
307 STDCALL
308 KeRevertToUserAffinityThread(VOID)
309 {
310 #ifdef CONFIG_SMP
311 PKTHREAD CurrentThread;
312 KIRQL oldIrql;
313
314 oldIrql = KeAcquireDispatcherDatabaseLock();
315
316 CurrentThread = KeGetCurrentThread();
317
318 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
319
320 /* Return to User Affinity */
321 CurrentThread->Affinity = CurrentThread->UserAffinity;
322
323 /* Disable System Affinity */
324 CurrentThread->SystemAffinityActive = FALSE;
325
326 if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber()))
327 {
328 KeReleaseDispatcherDatabaseLock(oldIrql);
329 }
330 else
331 {
332 CurrentThread->WaitIrql = oldIrql;
333 PsDispatchThreadNoLock(THREAD_STATE_READY);
334 KeLowerIrql(oldIrql);
335 }
336 #endif
337 }
338
339 /*
340 * @implemented
341 */
342 CCHAR
343 STDCALL
344 KeSetIdealProcessorThread (
345 IN PKTHREAD Thread,
346 IN CCHAR Processor)
347 {
348 CCHAR PreviousIdealProcessor;
349
350 /* Save Old Ideal Processor */
351 PreviousIdealProcessor = Thread->IdealProcessor;
352
353 /* Set New Ideal Processor */
354 Thread->IdealProcessor = Processor;
355
356 /* Return Old Ideal Processor */
357 return PreviousIdealProcessor;
358 }
359
360 /*
361 * @implemented
362 */
363 VOID
364 STDCALL
365 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
366 {
367 #ifdef CONFIG_SMP
368 PKTHREAD CurrentThread;
369 KIRQL oldIrql;
370
371 oldIrql = KeAcquireDispatcherDatabaseLock();
372
373 CurrentThread = KeGetCurrentThread();
374
375 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
376
377 /* Set the System Affinity Specified */
378 CurrentThread->Affinity = Affinity;
379
380 /* Enable System Affinity */
381 CurrentThread->SystemAffinityActive = TRUE;
382
383 if (Affinity & (1 << KeGetCurrentProcessorNumber()))
384 {
385 KeReleaseDispatcherDatabaseLock(oldIrql);
386 }
387 else
388 {
389 CurrentThread->WaitIrql = oldIrql;
390 PsDispatchThreadNoLock(THREAD_STATE_READY);
391 KeLowerIrql(oldIrql);
392 }
393 #endif
394 }
395
396 /*
397 * @implemented
398 */
399 VOID
400 STDCALL
401 KeTerminateThread(IN KPRIORITY Increment)
402 {
403 /* The Increment Argument seems to be ignored by NT and always 0 when called */
404
405 /* Call our own internal routine */
406 PsTerminateCurrentThread(0);
407 }