53e58201415f19932f7d746df775d977d9ad68b0
[reactos.git] / reactos / ntoskrnl / ps / thread.c
1 /* $Id: thread.c,v 1.81 2001/12/31 19:06:48 dwelch Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/thread.c
6 * PURPOSE: Thread managment
7 * PROGRAMMER: David Welch (welch@mcmail.com)
8 * REVISION HISTORY:
9 * 23/06/98: Created
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
11 */
12
13 /*
14 * NOTE:
15 *
16 * All of the routines that manipulate the thread queue synchronize on
17 * a single spinlock
18 *
19 */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/ob.h>
28 #include <internal/pool.h>
29
30 #define NDEBUG
31 #include <internal/debug.h>
32
33 /* TYPES *******************************************************************/
34
35 /* GLOBALS ******************************************************************/
36
37 POBJECT_TYPE EXPORTED PsThreadType = NULL;
38
39 KSPIN_LOCK PiThreadListLock;
40
41 /*
42 * PURPOSE: List of threads associated with each priority level
43 */
44 LIST_ENTRY PiThreadListHead;
45 static LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
46 static BOOLEAN DoneInitYet = FALSE;
47 static PETHREAD IdleThreads[MAXIMUM_PROCESSORS];
48 ULONG PiNrThreads = 0;
49 ULONG PiNrRunnableThreads = 0;
50
51 static GENERIC_MAPPING PiThreadMapping = {THREAD_READ,
52 THREAD_WRITE,
53 THREAD_EXECUTE,
54 THREAD_ALL_ACCESS};
55
56 /* FUNCTIONS ***************************************************************/
57
58 PKTHREAD STDCALL KeGetCurrentThread(VOID)
59 {
60 return(KeGetCurrentKPCR()->CurrentThread);
61 }
62
63 PETHREAD STDCALL PsGetCurrentThread(VOID)
64 {
65 PKTHREAD CurrentThread = KeGetCurrentKPCR()->CurrentThread;
66 return(CONTAINING_RECORD(CurrentThread, ETHREAD, Tcb));
67 }
68
69 HANDLE STDCALL PsGetCurrentThreadId(VOID)
70 {
71 return(PsGetCurrentThread()->Cid.UniqueThread);
72 }
73
74 VOID
75 PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
76 {
77 if (Priority >= MAXIMUM_PRIORITY || Priority < 0)
78 {
79 DPRINT1("Invalid thread priority\n");
80 KeBugCheck(0);
81 }
82 InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry);
83 PiNrRunnableThreads++;
84 }
85
86 VOID PsDumpThreads(BOOLEAN IncludeSystem)
87 {
88 PLIST_ENTRY current_entry;
89 PETHREAD current;
90 ULONG t;
91 ULONG i;
92
93 current_entry = PiThreadListHead.Flink;
94 t = 0;
95
96 while (current_entry != &PiThreadListHead)
97 {
98 PULONG Ebp;
99 PULONG Esp;
100
101 current = CONTAINING_RECORD(current_entry, ETHREAD,
102 Tcb.ThreadListEntry);
103 t++;
104 if (t > PiNrThreads)
105 {
106 DbgPrint("Too many threads on list\n");
107 return;
108 }
109 if (IncludeSystem || current->ThreadsProcess->UniqueProcessId >= 6)
110 {
111 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s\n",
112 current->Tcb.State, current->ThreadsProcess->UniqueProcessId,
113 current->Cid.UniqueThread, current->ThreadsProcess->ImageFileName);
114 if (current->Tcb.State == THREAD_STATE_RUNNABLE ||
115 current->Tcb.State == THREAD_STATE_SUSPENDED ||
116 current->Tcb.State == THREAD_STATE_BLOCKED)
117 {
118 Esp = (PULONG)current->Tcb.KernelStack;
119 Ebp = (PULONG)Esp[3];
120 DbgPrint("Ebp 0x%.8X\n", Ebp);
121 i = 0;
122 while (Ebp != 0 && Ebp >= (PULONG)current->Tcb.StackLimit)
123 {
124 DbgPrint("Frame: 0x%.8X Eip: 0x%.8X%s", Ebp[0], Ebp[1],
125 (i % 2) == 1 ? "\n" : "");
126 Ebp = (PULONG)Ebp[0];
127 }
128 if ((i % 2) == 0)
129 {
130 DbgPrint("\n");
131 }
132 }
133 }
134 current_entry = current_entry->Flink;
135 }
136 }
137
138 static PETHREAD PsScanThreadList (KPRIORITY Priority, ULONG Affinity)
139 {
140 #if 0
141 PLIST_ENTRY current_entry;
142 PETHREAD current;
143
144 current_entry = RemoveHeadList(&PriorityListHead[Priority]);
145 if (current_entry != &PriorityListHead[Priority])
146 {
147 current = CONTAINING_RECORD(current_entry, ETHREAD,
148 Tcb.QueueListEntry);
149 }
150 else
151 {
152 current = NULL;
153 }
154
155 return(current);
156 #else
157 PLIST_ENTRY current_entry;
158 PETHREAD current;
159
160 current_entry = PriorityListHead[Priority].Flink;
161 while (current_entry != &PriorityListHead[Priority])
162 {
163 current = CONTAINING_RECORD(current_entry, ETHREAD,
164 Tcb.QueueListEntry);
165 assert(current->Tcb.State == THREAD_STATE_RUNNABLE);
166 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
167 current->Tcb.UserAffinity, Affinity, current->Cid.UniqueThread,
168 Priority);
169 if (current->Tcb.UserAffinity & Affinity)
170 {
171 RemoveEntryList(&current->Tcb.QueueListEntry);
172 return(current);
173 }
174 current_entry = current_entry->Flink;
175 }
176 return(NULL);
177 #endif
178 }
179
180
181 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
182 {
183 KPRIORITY CurrentPriority;
184 PETHREAD Candidate;
185 ULONG Affinity;
186 PKTHREAD KCurrentThread = KeGetCurrentKPCR()->CurrentThread;
187 PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb);
188
189 DPRINT("PsDispatchThread() %d/%d\n", KeGetCurrentProcessorNumber(),
190 CurrentThread->Cid.UniqueThread);
191
192 CurrentThread->Tcb.State = NewThreadStatus;
193 if (CurrentThread->Tcb.State == THREAD_STATE_RUNNABLE)
194 {
195 PiNrRunnableThreads++;
196 PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
197 CurrentThread);
198 }
199
200 Affinity = 1 << KeGetCurrentProcessorNumber();
201 for (CurrentPriority = HIGH_PRIORITY;
202 CurrentPriority >= LOW_PRIORITY;
203 CurrentPriority--)
204 {
205 Candidate = PsScanThreadList(CurrentPriority, Affinity);
206 if (Candidate == CurrentThread)
207 {
208 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
209 return;
210 }
211 if (Candidate != NULL)
212 {
213 PETHREAD OldThread;
214
215 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
216
217 Candidate->Tcb.State = THREAD_STATE_RUNNING;
218
219 OldThread = CurrentThread;
220 CurrentThread = Candidate;
221
222 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
223 Ki386ContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb);
224 PsReapThreads();
225 return;
226 }
227 }
228 DbgPrint("CRITICAL: No threads are runnable\n");
229 KeBugCheck(0);
230 }
231
232 VOID STDCALL
233 PsDispatchThread(ULONG NewThreadStatus)
234 {
235 KIRQL oldIrql;
236
237 if (!DoneInitYet)
238 {
239 return;
240 }
241
242 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
243 /*
244 * Save wait IRQL
245 */
246 KeGetCurrentKPCR()->CurrentThread->WaitIrql = oldIrql;
247 PsDispatchThreadNoLock(NewThreadStatus);
248 KeLowerIrql(oldIrql);
249 }
250
251 VOID
252 PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus)
253 {
254 KIRQL oldIrql;
255
256 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
257 if (WaitStatus != NULL)
258 {
259 Thread->Tcb.WaitStatus = *WaitStatus;
260 }
261 Thread->Tcb.State = THREAD_STATE_RUNNABLE;
262 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
263 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
264 }
265
266 VOID
267 PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode,
268 BOOLEAN DispatcherLock, KIRQL WaitIrql)
269 {
270 KIRQL oldIrql;
271 PETHREAD Thread = PsGetCurrentThread();
272
273 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
274
275 if (DispatcherLock)
276 {
277 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
278 }
279
280 Thread->Tcb.Alertable = Alertable;
281 Thread->Tcb.WaitMode = WaitMode;
282 Thread->Tcb.WaitIrql = WaitIrql;
283 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
284
285 if (Status != NULL)
286 {
287 *Status = Thread->Tcb.WaitStatus;
288 }
289 KeLowerIrql(WaitIrql);
290 }
291
292 VOID
293 PsFreezeAllThreads(PEPROCESS Process)
294 /*
295 * Used by the debugging code to freeze all the process's threads
296 * while the debugger is examining their state.
297 */
298 {
299 KIRQL oldIrql;
300 PLIST_ENTRY current_entry;
301 PETHREAD current;
302
303 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
304
305 current_entry = Process->ThreadListHead.Flink;
306 while (current_entry != &Process->ThreadListHead)
307 {
308 current = CONTAINING_RECORD(current_entry, ETHREAD,
309 Tcb.ProcessThreadListEntry);
310
311 /*
312 * We have to be careful here, we can't just set the freeze the
313 * thread inside kernel mode since it may be holding a lock.
314 */
315
316 current_entry = current_entry->Flink;
317 }
318
319 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
320 }
321
322 VOID
323 PsApplicationProcessorInit(VOID)
324 {
325 KeGetCurrentKPCR()->CurrentThread =
326 (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
327 }
328
329 VOID
330 PsPrepareForApplicationProcessorInit(ULONG Id)
331 {
332 PETHREAD IdleThread;
333 HANDLE IdleThreadHandle;
334
335 PsInitializeThread(NULL,
336 &IdleThread,
337 &IdleThreadHandle,
338 THREAD_ALL_ACCESS,
339 NULL,
340 TRUE);
341 IdleThread->Tcb.State = THREAD_STATE_RUNNING;
342 IdleThread->Tcb.FreezeCount = 0;
343 IdleThread->Tcb.UserAffinity = 1 << Id;
344 IdleThread->Tcb.Priority = LOW_PRIORITY;
345 IdleThreads[Id] = IdleThread;
346
347 NtClose(IdleThreadHandle);
348 DbgPrint("IdleThread for Processor %d has PID %d\n",
349 Id, IdleThread->Cid.UniqueThread);
350 }
351
352 VOID
353 PsInitThreadManagment(VOID)
354 /*
355 * FUNCTION: Initialize thread managment
356 */
357 {
358 PETHREAD FirstThread;
359 ULONG i;
360 HANDLE FirstThreadHandle;
361
362 KeInitializeSpinLock(&PiThreadListLock);
363 for (i=0; i < MAXIMUM_PRIORITY; i++)
364 {
365 InitializeListHead(&PriorityListHead[i]);
366 }
367
368 InitializeListHead(&PiThreadListHead);
369
370 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
371
372 RtlInitUnicodeString(&PsThreadType->TypeName, L"Thread");
373
374 PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
375 PsThreadType->TotalObjects = 0;
376 PsThreadType->TotalHandles = 0;
377 PsThreadType->MaxObjects = 0;
378 PsThreadType->MaxHandles = 0;
379 PsThreadType->PagedPoolCharge = 0;
380 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
381 PsThreadType->Mapping = &PiThreadMapping;
382 PsThreadType->Dump = NULL;
383 PsThreadType->Open = NULL;
384 PsThreadType->Close = PiCloseThread;
385 PsThreadType->Delete = PiDeleteThread;
386 PsThreadType->Parse = NULL;
387 PsThreadType->Security = NULL;
388 PsThreadType->QueryName = NULL;
389 PsThreadType->OkayToClose = NULL;
390 PsThreadType->Create = NULL;
391 PsThreadType->DuplicationNotify = NULL;
392
393 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
394 THREAD_ALL_ACCESS,NULL, TRUE);
395 HalInitFirstTask(FirstThread);
396 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
397 FirstThread->Tcb.FreezeCount = 0;
398 KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
399 NtClose(FirstThreadHandle);
400
401 DPRINT("FirstThread %x\n",FirstThread);
402
403 DoneInitYet = TRUE;
404 }
405
406
407 /*
408 * Sets thread's base priority relative to the process' base priority
409 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
410 */
411 LONG STDCALL
412 KeSetBasePriorityThread (PKTHREAD Thread,
413 LONG Increment)
414 {
415 Thread->BasePriority =
416 ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
417 if (Thread->BasePriority < LOW_PRIORITY)
418 Thread->BasePriority = LOW_PRIORITY;
419 else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
420 Thread->BasePriority = HIGH_PRIORITY;
421 Thread->Priority = Thread->BasePriority;
422 return 1;
423 }
424
425
426 KPRIORITY STDCALL
427 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
428 {
429 KPRIORITY OldPriority;
430 KIRQL oldIrql;
431
432 if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
433 {
434 KeBugCheck(0);
435 }
436
437 OldPriority = Thread->Priority;
438 Thread->Priority = (CHAR)Priority;
439
440 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
441 if (Thread->State == THREAD_STATE_RUNNABLE)
442 {
443 RemoveEntryList(&Thread->QueueListEntry);
444 PsInsertIntoThreadList(Thread->BasePriority,
445 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
446 }
447 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
448 return(OldPriority);
449 }
450
451
452 NTSTATUS STDCALL
453 NtAlertResumeThread(IN HANDLE ThreadHandle,
454 OUT PULONG SuspendCount)
455 {
456 UNIMPLEMENTED;
457 }
458
459
460 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
461 {
462 #if 0
463 PETHREAD Thread;
464 NTSTATUS Status;
465 NTSTATUS ThreadStatus;
466
467 Status = ObReferenceObjectByHandle(ThreadHandle,
468 THREAD_SUSPEND_RESUME,
469 PsThreadType,
470 UserMode,
471 (PVOID*)&Thread,
472 NULL);
473 if (Status != STATUS_SUCCESS)
474 {
475 return(Status);
476 }
477
478 ThreadStatus = STATUS_ALERTED;
479 (VOID)PsUnblockThread(Thread, &ThreadStatus);
480
481 ObDereferenceObject(Thread);
482 return(STATUS_SUCCESS);
483 #endif
484 UNIMPLEMENTED;
485 }
486
487 NTSTATUS STDCALL
488 NtOpenThread(OUT PHANDLE ThreadHandle,
489 IN ACCESS_MASK DesiredAccess,
490 IN POBJECT_ATTRIBUTES ObjectAttributes,
491 IN PCLIENT_ID ClientId)
492 {
493 UNIMPLEMENTED;
494 }
495
496 NTSTATUS STDCALL
497 NtCallbackReturn (PVOID Result,
498 ULONG ResultLength,
499 NTSTATUS Status)
500 {
501 UNIMPLEMENTED;
502 }
503
504
505 NTSTATUS STDCALL
506 NtW32Call (IN ULONG RoutineIndex,
507 IN PVOID Argument,
508 IN ULONG ArgumentLength,
509 OUT PVOID* Result OPTIONAL,
510 OUT PULONG ResultLength OPTIONAL)
511 {
512 UNIMPLEMENTED;
513 }
514
515 NTSTATUS STDCALL
516 NtContinue(IN PCONTEXT Context,
517 IN BOOLEAN TestAlert)
518 {
519 PKTRAP_FRAME TrapFrame;
520
521 /*
522 * Copy the supplied context over the register information that was saved
523 * on entry to kernel mode, it will then be restored on exit
524 * FIXME: Validate the context
525 */
526 TrapFrame = KeGetCurrentThread()->TrapFrame;
527 if (TrapFrame == NULL)
528 {
529 DbgPrint("NtContinue called but TrapFrame was NULL\n");
530 KeBugCheck(0);
531 }
532 KeContextToTrapFrame(Context, TrapFrame);
533 return(STATUS_SUCCESS);
534 }
535
536
537 NTSTATUS STDCALL
538 NtYieldExecution(VOID)
539 {
540 PsDispatchThread(THREAD_STATE_RUNNABLE);
541 return(STATUS_SUCCESS);
542 }
543
544
545 /* EOF */