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