Some fixes.
[reactos.git] / reactos / ntoskrnl / ps / thread.c
1 /* $Id: thread.c,v 1.46 2000/05/13 13:51:08 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 <string.h>
27 #include <internal/string.h>
28 #include <internal/hal.h>
29 #include <internal/ps.h>
30 #include <internal/ob.h>
31
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* TYPES *******************************************************************/
36
37 /* GLOBALS ******************************************************************/
38
39 POBJECT_TYPE PsThreadType = NULL;
40
41 #define NR_THREAD_PRIORITY_LEVELS (32)
42 #define THREAD_PRIORITY_MAX (15)
43
44 KSPIN_LOCK PiThreadListLock;
45
46 /*
47 * PURPOSE: List of threads associated with each priority level
48 */
49 LIST_ENTRY PiThreadListHead;
50 static LIST_ENTRY PriorityListHead[NR_THREAD_PRIORITY_LEVELS];
51 static BOOLEAN DoneInitYet = FALSE;
52 ULONG PiNrThreads = 0;
53 ULONG PiNrRunnableThreads = 0;
54
55 static PETHREAD CurrentThread = NULL;
56
57 /* FUNCTIONS ***************************************************************/
58
59 VOID PsFreezeProcessThreads(PEPROCESS Process)
60 {
61 KIRQL oldIrql;
62 PLIST_ENTRY current_entry;
63 PETHREAD current;
64
65 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
66 current_entry = PiThreadListHead.Flink;
67
68 while (current_entry != &PiThreadListHead)
69 {
70 current = CONTAINING_RECORD(current_entry, ETHREAD,
71 Tcb.ThreadListEntry);
72
73 current_entry = current_entry->Flink;
74
75 if (current->ThreadsProcess == Process &&
76 current != PsGetCurrentThread())
77 {
78 PsFreezeOtherThread(current);
79 }
80 }
81 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
82 }
83
84 VOID PsUnfreezeProcessThreads(PEPROCESS Process)
85 {
86 KIRQL oldIrql;
87 PLIST_ENTRY current_entry;
88 PETHREAD current;
89
90 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
91 current_entry = PiThreadListHead.Flink;
92
93 while (current_entry != &PiThreadListHead)
94 {
95 current = CONTAINING_RECORD(current_entry, ETHREAD,
96 Tcb.ThreadListEntry);
97
98 current_entry = current_entry->Flink;
99
100 if (current->ThreadsProcess == Process &&
101 current != PsGetCurrentThread())
102 {
103 PsUnfreezeOtherThread(current);
104 }
105 }
106 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
107 }
108
109 PKTHREAD KeGetCurrentThread(VOID)
110 {
111 return(&(CurrentThread->Tcb));
112 }
113
114 PETHREAD PsGetCurrentThread(VOID)
115 {
116 return(CurrentThread);
117 }
118
119 HANDLE PsGetCurrentThreadId(VOID)
120 {
121 return(CurrentThread->Cid.UniqueThread);
122 }
123
124 static VOID PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
125 {
126 // DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority,
127 // Thread);
128 // DPRINT("Offset %x\n", THREAD_PRIORITY_MAX + Priority);
129
130 InsertTailList(&PriorityListHead[THREAD_PRIORITY_MAX+Priority],
131 &Thread->Tcb.QueueListEntry);
132 PiNrRunnableThreads++;
133 }
134
135 VOID PsDumpThreads(VOID)
136 {
137 PLIST_ENTRY current_entry;
138 PETHREAD current;
139 ULONG t;
140
141 // return;
142
143 current_entry = PiThreadListHead.Flink;
144 t = 0;
145
146 while (current_entry != &PiThreadListHead)
147 {
148 current = CONTAINING_RECORD(current_entry, ETHREAD,
149 Tcb.ThreadListEntry);
150 t++;
151 if (t >= PiNrThreads)
152 {
153 DbgPrint("Too many threads on list\n");
154 return;
155 }
156 DbgPrint("current %x current->Tcb.State %d eip %x ",
157 current, current->Tcb.State,
158 current->Tcb.Context.eip);
159 // KeDumpStackFrames(0, 16);
160 DbgPrint("PID %d ", current->ThreadsProcess->UniqueProcessId);
161 DbgPrint("\n");
162
163 current_entry = current_entry->Flink;
164 }
165 }
166
167 static PETHREAD PsScanThreadList (KPRIORITY Priority)
168 {
169 PLIST_ENTRY current_entry;
170 PETHREAD current;
171
172 // DPRINT("PsScanThreadList(Priority %d)\n",Priority);
173
174 current_entry = RemoveHeadList(
175 &PriorityListHead[THREAD_PRIORITY_MAX + Priority]);
176 if (current_entry != &PriorityListHead[THREAD_PRIORITY_MAX + Priority])
177 {
178 current = CONTAINING_RECORD(current_entry, ETHREAD,
179 Tcb.QueueListEntry);
180 }
181 else
182 {
183 current = NULL;
184 }
185
186 return(current);
187 }
188
189
190 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
191 {
192 KPRIORITY CurrentPriority;
193 PETHREAD Candidate;
194
195 CurrentThread->Tcb.State = NewThreadStatus;
196 PiNrRunnableThreads--;
197 if (CurrentThread->Tcb.State == THREAD_STATE_RUNNABLE)
198 {
199 PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
200 CurrentThread);
201 }
202
203 for (CurrentPriority = THREAD_PRIORITY_TIME_CRITICAL;
204 (CurrentPriority >= THREAD_PRIORITY_IDLE);
205 CurrentPriority--)
206 {
207 Candidate = PsScanThreadList(CurrentPriority);
208 if (Candidate == CurrentThread)
209 {
210 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
211 return;
212 }
213 if (Candidate != NULL)
214 {
215 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
216
217 Candidate->Tcb.State = THREAD_STATE_RUNNING;
218
219 CurrentThread = Candidate;
220
221 KeReleaseSpinLockFromDpcLevel( &PiThreadListLock );
222 HalTaskSwitch(&CurrentThread->Tcb);
223 PsReapThreads();
224 return;
225 }
226 }
227 DbgPrint("CRITICAL: No threads are runnable\n");
228 KeBugCheck(0);
229 }
230
231 VOID PsDispatchThread(ULONG NewThreadStatus)
232 {
233 KIRQL oldIrql;
234
235 if (!DoneInitYet)
236 {
237 return;
238 }
239
240 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
241 CurrentThread->Tcb.WaitIrql = oldIrql; // save wait Irql
242 PsDispatchThreadNoLock(NewThreadStatus);
243 KeLowerIrql(oldIrql);
244 // DPRINT("oldIrql %d\n",oldIrql);
245 }
246
247 /*
248 * Suspend and resume may only be called to suspend the current thread, except by apc.c
249 */
250
251 ULONG PsUnfreezeThread(PETHREAD Thread, PNTSTATUS WaitStatus)
252 {
253 KIRQL oldIrql;
254 ULONG r;
255
256 DPRINT("PsUnfreezeThread(Thread %x, WaitStatus %x)\n", Thread, WaitStatus);
257
258 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
259
260 Thread->Tcb.FreezeCount--;
261 r = Thread->Tcb.FreezeCount;
262
263 if (WaitStatus != NULL)
264 {
265 Thread->Tcb.WaitStatus = *WaitStatus;
266 }
267
268 if (r <= 0)
269 {
270 DPRINT("Resuming thread\n");
271 Thread->Tcb.State = THREAD_STATE_RUNNABLE;
272 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
273 }
274
275 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
276 return(r);
277 }
278
279 VOID PsUnfreezeOtherThread(PETHREAD Thread)
280 {
281 ULONG r;
282
283 Thread->Tcb.FreezeCount--;
284 r = Thread->Tcb.FreezeCount;
285
286 if (r <= 0)
287 {
288 Thread->Tcb.State = THREAD_STATE_RUNNABLE;
289 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
290 }
291 }
292
293 VOID PsFreezeOtherThread(PETHREAD Thread)
294 {
295 ULONG r;
296
297 Thread->Tcb.FreezeCount++;
298 r = Thread->Tcb.FreezeCount;
299
300 if (r == 0)
301 {
302 return;
303 }
304
305 if (Thread->Tcb.State == THREAD_STATE_RUNNABLE)
306 {
307 RemoveEntryList(&Thread->Tcb.QueueListEntry);
308 }
309 Thread->Tcb.State = THREAD_STATE_FROZEN;
310 }
311
312 ULONG PsFreezeThread(PETHREAD Thread,
313 PNTSTATUS Status,
314 UCHAR Alertable,
315 ULONG WaitMode)
316 {
317 KIRQL oldIrql;
318 ULONG r;
319
320 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
321
322 Thread->Tcb.FreezeCount++;
323 r = Thread->Tcb.FreezeCount;
324
325 DPRINT("r %d\n", r);
326
327 if (r == 0)
328 {
329 DPRINT("Not suspending thread\n");
330 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
331 return(r);
332 }
333
334 Thread->Tcb.Alertable = Alertable;
335 Thread->Tcb.WaitMode = WaitMode;
336
337 if (PsGetCurrentThread() != Thread)
338 {
339 DPRINT("Suspending other\n");
340 if (Thread->Tcb.State == THREAD_STATE_RUNNABLE)
341 {
342 RemoveEntryList(&Thread->Tcb.QueueListEntry);
343 }
344 Thread->Tcb.State = THREAD_STATE_FROZEN;
345 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
346 }
347 else
348 {
349 DPRINT("Suspending self\n");
350 Thread->Tcb.WaitIrql = oldIrql;
351 PsDispatchThreadNoLock(THREAD_STATE_FROZEN);
352 if (Status != NULL)
353 {
354 *Status = Thread->Tcb.WaitStatus;
355 }
356 KeLowerIrql(oldIrql);
357 }
358 return(r);
359 }
360
361 VOID PsInitThreadManagment(VOID)
362 /*
363 * FUNCTION: Initialize thread managment
364 */
365 {
366 PETHREAD FirstThread;
367 ULONG i;
368 ANSI_STRING AnsiString;
369 HANDLE FirstThreadHandle;
370
371 KeInitializeSpinLock(&PiThreadListLock);
372 for (i=0; i<NR_THREAD_PRIORITY_LEVELS; i++)
373 {
374 InitializeListHead(&PriorityListHead[i]);
375 }
376 InitializeListHead(&PiThreadListHead);
377
378 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
379
380 RtlInitAnsiString(&AnsiString,"Thread");
381 RtlAnsiStringToUnicodeString(&PsThreadType->TypeName,&AnsiString,TRUE);
382
383 PsThreadType->TotalObjects = 0;
384 PsThreadType->TotalHandles = 0;
385 PsThreadType->MaxObjects = 0;
386 PsThreadType->MaxHandles = 0;
387 PsThreadType->PagedPoolCharge = 0;
388 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
389 PsThreadType->Dump = NULL;
390 PsThreadType->Open = NULL;
391 PsThreadType->Close = PiCloseThread;
392 PsThreadType->Delete = PiDeleteThread;
393 PsThreadType->Parse = NULL;
394 PsThreadType->Security = NULL;
395 PsThreadType->QueryName = NULL;
396 PsThreadType->OkayToClose = NULL;
397 PsThreadType->Create = NULL;
398
399 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
400 THREAD_ALL_ACCESS,NULL);
401 HalInitFirstTask(FirstThread);
402 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
403 FirstThread->Tcb.FreezeCount = 0;
404 ZwClose(FirstThreadHandle);
405
406 DPRINT("FirstThread %x\n",FirstThread);
407
408 CurrentThread = FirstThread;
409
410 DoneInitYet = TRUE;
411 }
412
413
414 /*
415 * Sets thread's base priority relative to the process' base priority
416 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
417 */
418 LONG KeSetBasePriorityThread(PKTHREAD Thread, LONG Increment)
419 {
420 Thread->BasePriority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
421 if( Thread->BasePriority < 0 )
422 Thread->BasePriority = 0;
423 else if( Thread->BasePriority >= NR_THREAD_PRIORITY_LEVELS )
424 Thread->BasePriority = NR_THREAD_PRIORITY_LEVELS - 1;
425 Thread->Priority = Thread->BasePriority;
426 return 1;
427 }
428
429
430 KPRIORITY KeSetPriorityThread(PKTHREAD Thread, KPRIORITY Priority)
431 {
432 KPRIORITY OldPriority;
433 KIRQL oldIrql;
434
435 OldPriority = Thread->Priority;
436 Thread->Priority = Priority;
437
438 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
439 RemoveEntryList(&Thread->QueueListEntry);
440 PsInsertIntoThreadList(Thread->BasePriority,
441 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
442 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
443 return(OldPriority);
444 }
445
446
447 NTSTATUS STDCALL NtAlertResumeThread(IN HANDLE ThreadHandle,
448 OUT PULONG SuspendCount)
449 {
450 UNIMPLEMENTED;
451 }
452
453
454 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
455 {
456 PETHREAD Thread;
457 NTSTATUS Status;
458 NTSTATUS ThreadStatus;
459
460 Status = ObReferenceObjectByHandle(ThreadHandle,
461 THREAD_SUSPEND_RESUME,
462 PsThreadType,
463 UserMode,
464 (PVOID*)&Thread,
465 NULL);
466 if (Status != STATUS_SUCCESS)
467 {
468 return(Status);
469 }
470
471 ThreadStatus = STATUS_ALERTED;
472 (VOID)PsUnfreezeThread(Thread, &ThreadStatus);
473
474 ObDereferenceObject(Thread);
475 return(STATUS_SUCCESS);
476 }
477
478
479 NTSTATUS STDCALL NtGetContextThread (IN HANDLE ThreadHandle,
480 OUT PCONTEXT Context)
481 {
482 UNIMPLEMENTED;
483 }
484
485
486 NTSTATUS STDCALL NtOpenThread(OUT PHANDLE ThreadHandle,
487 IN ACCESS_MASK DesiredAccess,
488 IN POBJECT_ATTRIBUTES ObjectAttributes,
489 IN PCLIENT_ID ClientId)
490 {
491 UNIMPLEMENTED;
492 }
493
494
495 NTSTATUS STDCALL NtResumeThread (IN HANDLE ThreadHandle,
496 IN PULONG SuspendCount)
497 /*
498 * FUNCTION: Decrements a thread's resume count
499 * ARGUMENTS:
500 * ThreadHandle = Handle to the thread that should be resumed
501 * ResumeCount = The resulting resume count.
502 * REMARK:
503 * A thread is resumed if its suspend count is 0. This procedure maps to
504 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
505 * RETURNS: Status
506 */
507 {
508 UNIMPLEMENTED;
509 return(STATUS_UNSUCCESSFUL);
510 }
511
512
513 NTSTATUS STDCALL NtSetContextThread (IN HANDLE ThreadHandle,
514 IN PCONTEXT Context)
515 {
516 UNIMPLEMENTED;
517 }
518
519
520 NTSTATUS STDCALL NtSuspendThread (IN HANDLE ThreadHandle,
521 IN PULONG PreviousSuspendCount)
522 /*
523 * FUNCTION: Increments a thread's suspend count
524 * ARGUMENTS:
525 * ThreadHandle = Handle to the thread that should be resumed
526 * PreviousSuspendCount = The resulting/previous suspend count.
527 * REMARK:
528 * A thread will be suspended if its suspend count is greater than 0.
529 * This procedure maps to the win32 SuspendThread function. (
530 * documentation about the the suspend count can be found here aswell )
531 * The suspend count is not increased if it is greater than
532 * MAXIMUM_SUSPEND_COUNT.
533 * RETURNS: Status
534 */
535 {
536 UNIMPLEMENTED;
537 return(STATUS_UNSUCCESSFUL);
538 }
539
540
541 NTSTATUS STDCALL NtContinue(IN PCONTEXT Context,
542 IN CINT IrqLevel)
543 {
544 PULONG StackTop;
545
546 StackTop = KeGetStackTopThread(PsGetCurrentThread());
547
548 memcpy(StackTop, Context, sizeof(CONTEXT));
549
550 return(STATUS_SUCCESS);
551 }
552
553
554 NTSTATUS STDCALL NtYieldExecution(VOID)
555 {
556 PsDispatchThread(THREAD_STATE_RUNNABLE);
557 return(STATUS_SUCCESS);
558 }
559
560
561 /* EOF */