Began improvements to memory managment, changed method of
[reactos.git] / reactos / ntoskrnl / ps / thread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Thread managment
6 * PROGRAMMER: David Welch (welch@mcmail.com)
7 * REVISION HISTORY:
8 * 23/06/98: Created
9 */
10
11 /*
12 * NOTE:
13 *
14 * All of the routines that manipulate the thread queue synchronize on
15 * a single spinlock
16 *
17 */
18
19 /* INCLUDES ****************************************************************/
20
21 #include <windows.h>
22 #include <ddk/ntddk.h>
23 #include <internal/ke.h>
24 #include <internal/ob.h>
25 #include <string.h>
26 #include <internal/string.h>
27 #include <internal/hal.h>
28 #include <internal/ps.h>
29
30 #define NDEBUG
31 #include <internal/debug.h>
32
33 /* TYPES *******************************************************************/
34
35 /* GLOBALS ******************************************************************/
36
37 POBJECT_TYPE PsThreadType = NULL;
38
39 #define NR_THREAD_PRIORITY_LEVELS (31)
40 #define THREAD_PRIORITY_MAX (15)
41
42 static KSPIN_LOCK ThreadListLock = {0,};
43
44 /*
45 * PURPOSE: List of threads associated with each priority level
46 */
47 static LIST_ENTRY PriorityListHead[NR_THREAD_PRIORITY_LEVELS]={{NULL,NULL},};
48 static BOOLEAN DoneInitYet = FALSE;
49 ULONG PiNrThreads = 0;
50 ULONG PiNrRunnableThreads = 0;
51
52 static PETHREAD CurrentThread = NULL;
53
54 static ULONG NextThreadUniqueId = 0;
55 //static ULONG NextProcessUniqueId = 0;
56
57 /* FUNCTIONS ***************************************************************/
58
59 PKTHREAD KeGetCurrentThread(VOID)
60 {
61 return(&(CurrentThread->Tcb));
62 }
63
64 PETHREAD PsGetCurrentThread(VOID)
65 {
66 return((PETHREAD)KeGetCurrentThread());
67 }
68
69 VOID PiTerminateProcessThreads(PEPROCESS Process, NTSTATUS ExitStatus)
70 {
71 KIRQL oldlvl;
72 PLIST_ENTRY current_entry;
73 PETHREAD current;
74 ULONG i;
75
76 KeAcquireSpinLock(&ThreadListLock, &oldlvl);
77
78 for (i=0; i<NR_THREAD_PRIORITY_LEVELS; i++)
79 {
80 current_entry = PriorityListHead[i].Flink;
81 while (current_entry != &PriorityListHead[i])
82 {
83 current = CONTAINING_RECORD(current_entry,ETHREAD,Tcb.Entry);
84 if (current->ThreadsProcess == Process &&
85 current != PsGetCurrentThread())
86 {
87 PsTerminateOtherThread(current, ExitStatus);
88 }
89 current_entry = current_entry->Flink;
90 }
91 }
92
93 KeReleaseSpinLock(&ThreadListLock, oldlvl);
94 }
95
96 static VOID PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
97 {
98 KIRQL oldlvl;
99
100 DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority,
101 Thread);
102
103 KeAcquireSpinLock(&ThreadListLock,&oldlvl);
104 InsertTailList(&PriorityListHead[THREAD_PRIORITY_MAX+Priority],
105 &Thread->Tcb.Entry);
106 KeReleaseSpinLock(&ThreadListLock,oldlvl);
107 }
108
109 VOID PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
110 {
111 NTSTATUS Ret;
112
113 KeReleaseSpinLock(&ThreadListLock,PASSIVE_LEVEL);
114 Ret = StartRoutine(StartContext);
115 PsTerminateSystemThread(Ret);
116 for(;;);
117 }
118
119 static PETHREAD PsScanThreadList(KPRIORITY Priority)
120 {
121 PLIST_ENTRY current_entry;
122 PETHREAD current;
123 PETHREAD oldest = NULL;
124 ULONG oldest_time = 0;
125
126 // DPRINT("PsScanThreadList(Priority %d)\n",Priority);
127
128 current_entry = PriorityListHead[THREAD_PRIORITY_MAX+Priority].Flink;
129 while (current_entry != &PriorityListHead[THREAD_PRIORITY_MAX+Priority])
130 {
131 current = CONTAINING_RECORD(current_entry,ETHREAD,Tcb.Entry);
132
133 if (current->Tcb.ThreadState == THREAD_STATE_TERMINATED &&
134 current != CurrentThread)
135 {
136 PsReleaseThread(current);
137 }
138
139 if (current->Tcb.ThreadState == THREAD_STATE_RUNNABLE)
140 {
141 if (oldest == NULL || oldest_time > current->Tcb.LastTick)
142 {
143 oldest = current;
144 oldest_time = current->Tcb.LastTick;
145 }
146 }
147 current_entry = current_entry->Flink;
148 }
149 // DPRINT("PsScanThreadList() = %x\n",oldest);
150 return(oldest);
151 }
152
153 VOID PsDispatchThread(VOID)
154 {
155 KPRIORITY CurrentPriority;
156 PETHREAD Candidate;
157 KIRQL irql;
158 LARGE_INTEGER TickCount;
159
160 KeAcquireSpinLock(&ThreadListLock,&irql);
161
162 if (!DoneInitYet)
163 {
164 return;
165 }
166
167 DPRINT("PsDispatchThread() Current %x\n",CurrentThread);
168
169 if (CurrentThread->Tcb.ThreadState==THREAD_STATE_RUNNING)
170 {
171 CurrentThread->Tcb.ThreadState=THREAD_STATE_RUNNABLE;
172 }
173
174 for (CurrentPriority=THREAD_PRIORITY_TIME_CRITICAL;
175 CurrentPriority>=THREAD_PRIORITY_IDLE;
176 CurrentPriority--)
177 {
178 Candidate = PsScanThreadList(CurrentPriority);
179 if (Candidate == CurrentThread)
180 {
181 DPRINT("Scheduling current thread\n");
182 KeQueryTickCount(&TickCount);
183 CurrentThread->Tcb.LastTick = GET_LARGE_INTEGER_LOW_PART(TickCount);
184 CurrentThread->Tcb.ThreadState = THREAD_STATE_RUNNING;
185 KeReleaseSpinLock(&ThreadListLock,irql);
186 return;
187 }
188 if (Candidate != NULL)
189 {
190 DPRINT("Scheduling %x\n",Candidate);
191
192 Candidate->Tcb.ThreadState = THREAD_STATE_RUNNING;
193
194 KeQueryTickCount(&TickCount);
195 CurrentThread->Tcb.LastTick = GET_LARGE_INTEGER_LOW_PART(TickCount);
196
197 CurrentThread = Candidate;
198
199 HalTaskSwitch(&CurrentThread->Tcb);
200 KeReleaseSpinLock(&ThreadListLock,irql);
201 return;
202 }
203 }
204 DbgPrint("CRITICAL: No threads are runnable\n");
205 KeBugCheck(0);
206 }
207
208 NTSTATUS PsInitializeThread(HANDLE ProcessHandle,
209 PETHREAD* ThreadPtr,
210 PHANDLE ThreadHandle,
211 ACCESS_MASK DesiredAccess,
212 POBJECT_ATTRIBUTES ThreadAttributes)
213 {
214 PETHREAD Thread;
215 NTSTATUS Status;
216
217 PiNrThreads++;
218
219 Thread = ObCreateObject(ThreadHandle,
220 DesiredAccess,
221 ThreadAttributes,
222 PsThreadType);
223 DPRINT("Thread = %x\n",Thread);
224 Thread->Tcb.LastTick = 0;
225 Thread->Tcb.ThreadState=THREAD_STATE_SUSPENDED;
226 Thread->Tcb.BasePriority=THREAD_PRIORITY_NORMAL;
227 Thread->Tcb.CurrentPriority=THREAD_PRIORITY_NORMAL;
228 Thread->Tcb.ApcList=ExAllocatePool(NonPagedPool,sizeof(LIST_ENTRY));
229 Thread->Tcb.SuspendCount = 1;
230 if (ProcessHandle!=NULL)
231 {
232 Status = ObReferenceObjectByHandle(ProcessHandle,
233 PROCESS_CREATE_THREAD,
234 PsProcessType,
235 UserMode,
236 (PVOID*)&Thread->ThreadsProcess,
237 NULL);
238 if (Status != STATUS_SUCCESS)
239 {
240 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
241 return(Status);
242 }
243 }
244 else
245 {
246 Thread->ThreadsProcess=SystemProcess;
247 ObReferenceObjectByPointer(Thread->ThreadsProcess,
248 PROCESS_CREATE_THREAD,
249 PsProcessType,
250 UserMode);
251 }
252 ObReferenceObjectByPointer(Thread->ThreadsProcess,
253 PROCESS_CREATE_THREAD,
254 PsProcessType,
255 UserMode);
256 InitializeListHead(Thread->Tcb.ApcList);
257 InitializeListHead(&(Thread->IrpList));
258 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
259 &NextThreadUniqueId);
260 DbgPrint("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
261 ObReferenceObjectByPointer(Thread,
262 THREAD_ALL_ACCESS,
263 PsThreadType,
264 UserMode);
265 PsInsertIntoThreadList(Thread->Tcb.CurrentPriority,Thread);
266
267 *ThreadPtr = Thread;
268
269 ObDereferenceObject(Thread->ThreadsProcess);
270 return(STATUS_SUCCESS);
271 }
272
273 VOID PsResumeThread(PETHREAD Thread)
274 {
275 DPRINT("PsResumeThread(Thread %x)\n",Thread);
276 Thread->Tcb.SuspendCount--;
277 if (Thread->Tcb.SuspendCount <= 0 &&
278 Thread->Tcb.ThreadState != THREAD_STATE_RUNNING)
279 {
280 DPRINT("Setting thread to runnable\n");
281 Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;
282 }
283 DPRINT("Finished PsResumeThread()\n");
284 }
285
286 VOID PsSuspendThread(PETHREAD Thread)
287 {
288 DPRINT("PsSuspendThread(Thread %x)\n",Thread);
289 Thread->Tcb.SuspendCount++;
290 if (Thread->Tcb.SuspendCount > 0)
291 {
292 Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;
293 if (Thread == CurrentThread)
294 {
295 PsDispatchThread();
296 }
297 }
298 }
299
300 void PsInitThreadManagment(void)
301 /*
302 * FUNCTION: Initialize thread managment
303 */
304 {
305 PETHREAD FirstThread;
306 ULONG i;
307 ANSI_STRING AnsiString;
308 HANDLE FirstThreadHandle;
309
310 KeInitializeSpinLock(&ThreadListLock);
311 for (i=0; i<NR_THREAD_PRIORITY_LEVELS; i++)
312 {
313 InitializeListHead(&PriorityListHead[i]);
314 }
315
316 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
317
318 RtlInitAnsiString(&AnsiString,"Thread");
319 RtlAnsiStringToUnicodeString(&PsThreadType->TypeName,&AnsiString,TRUE);
320
321 PsThreadType->TotalObjects = 0;
322 PsThreadType->TotalHandles = 0;
323 PsThreadType->MaxObjects = 0;
324 PsThreadType->MaxHandles = 0;
325 PsThreadType->PagedPoolCharge = 0;
326 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
327 PsThreadType->Dump = NULL;
328 PsThreadType->Open = NULL;
329 PsThreadType->Close = NULL;
330 PsThreadType->Delete = NULL;
331 PsThreadType->Parse = NULL;
332 PsThreadType->Security = NULL;
333 PsThreadType->QueryName = NULL;
334 PsThreadType->OkayToClose = NULL;
335
336 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
337 THREAD_ALL_ACCESS,NULL);
338 HalInitFirstTask(FirstThread);
339 FirstThread->Tcb.ThreadState = THREAD_STATE_RUNNING;
340 FirstThread->Tcb.SuspendCount = 0;
341
342 DPRINT("FirstThread %x\n",FirstThread);
343
344 CurrentThread = FirstThread;
345
346 DoneInitYet = TRUE;
347 }
348
349 NTSTATUS NtCreateThread(PHANDLE ThreadHandle,
350 ACCESS_MASK DesiredAccess,
351 POBJECT_ATTRIBUTES ObjectAttributes,
352 HANDLE ProcessHandle,
353 PCLIENT_ID Client,
354 PCONTEXT ThreadContext,
355 PINITIAL_TEB InitialTeb,
356 BOOLEAN CreateSuspended)
357 {
358 return(ZwCreateThread(ThreadHandle,
359 DesiredAccess,
360 ObjectAttributes,
361 ProcessHandle,
362 Client,
363 ThreadContext,
364 InitialTeb,
365 CreateSuspended));
366 }
367
368 NTSTATUS ZwCreateThread(PHANDLE ThreadHandle,
369 ACCESS_MASK DesiredAccess,
370 POBJECT_ATTRIBUTES ObjectAttributes,
371 HANDLE ProcessHandle,
372 PCLIENT_ID Client,
373 PCONTEXT ThreadContext,
374 PINITIAL_TEB InitialTeb,
375 BOOLEAN CreateSuspended)
376 {
377 PETHREAD Thread;
378 NTSTATUS Status;
379
380 DPRINT("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
381 ThreadHandle,ThreadContext);
382
383 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
384 DesiredAccess,ObjectAttributes);
385 if (!NT_SUCCESS(Status))
386 {
387 return(Status);
388 }
389
390 Status = HalInitTaskWithContext(Thread,ThreadContext);
391 if (!NT_SUCCESS(Status))
392 {
393 return(Status);
394 }
395 Thread->StartAddress=NULL;
396
397 if (Client!=NULL)
398 {
399 *Client=Thread->Cid;
400 }
401
402 if (!CreateSuspended)
403 {
404 DPRINT("Not creating suspended\n");
405 PsResumeThread(Thread);
406 }
407 DPRINT("Finished PsCreateThread()\n");
408 return(STATUS_SUCCESS);
409 }
410
411 NTSTATUS PsCreateSystemThread(PHANDLE ThreadHandle,
412 ACCESS_MASK DesiredAccess,
413 POBJECT_ATTRIBUTES ObjectAttributes,
414 HANDLE ProcessHandle,
415 PCLIENT_ID ClientId,
416 PKSTART_ROUTINE StartRoutine,
417 PVOID StartContext)
418 /*
419 * FUNCTION: Creates a thread which executes in kernel mode
420 * ARGUMENTS:
421 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
422 * handle
423 * DesiredAccess = Requested access to the thread
424 * ObjectAttributes = Object attributes (optional)
425 * ProcessHandle = Handle of process thread will run in
426 * NULL to use system process
427 * ClientId (OUT) = Caller supplied storage for the returned client id
428 * of the thread (optional)
429 * StartRoutine = Entry point for the thread
430 * StartContext = Argument supplied to the thread when it begins
431 * execution
432 * RETURNS: Success or failure status
433 */
434 {
435 PETHREAD Thread;
436 NTSTATUS Status;
437
438 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
439 ThreadHandle,ProcessHandle);
440
441 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
442 DesiredAccess,ObjectAttributes);
443 if (!NT_SUCCESS(Status))
444 {
445 return(Status);
446 }
447
448 Thread->StartAddress=StartRoutine;
449 Status = HalInitTask(Thread,StartRoutine,StartContext);
450 if (!NT_SUCCESS(Status))
451 {
452 return(Status);
453 }
454
455 if (ClientId!=NULL)
456 {
457 *ClientId=Thread->Cid;
458 }
459
460 PsResumeThread(Thread);
461
462 return(STATUS_SUCCESS);
463 }
464
465 LONG KeSetBasePriorityThread(PKTHREAD Thread, LONG Increment)
466 {
467 UNIMPLEMENTED;
468 }
469
470 KPRIORITY KeSetPriorityThread(PKTHREAD Thread, KPRIORITY Priority)
471 {
472 KPRIORITY OldPriority;
473 OldPriority = Thread->CurrentPriority;
474 Thread->CurrentPriority = Priority;
475
476 RemoveEntryList(&Thread->Entry);
477 PsInsertIntoThreadList(Thread->CurrentPriority,
478 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
479
480 return(OldPriority);
481 }
482
483 NTSTATUS STDCALL NtAlertResumeThread(IN HANDLE ThreadHandle,
484 OUT PULONG SuspendCount)
485 {
486 return(ZwAlertResumeThread(ThreadHandle,SuspendCount));
487 }
488
489 NTSTATUS STDCALL ZwAlertResumeThread(IN HANDLE ThreadHandle,
490 OUT PULONG SuspendCount)
491 {
492 UNIMPLEMENTED;
493 }
494
495 NTSTATUS STDCALL NtAlertThread(IN HANDLE ThreadHandle)
496 {
497 return(ZwAlertThread(ThreadHandle));
498 }
499
500 NTSTATUS STDCALL ZwAlertThread(IN HANDLE ThreadHandle)
501 {
502 UNIMPLEMENTED;
503 }
504
505 NTSTATUS STDCALL NtGetContextThread(IN HANDLE ThreadHandle,
506 OUT PCONTEXT Context)
507 {
508 return(ZwGetContextThread(ThreadHandle,Context));
509 }
510
511 NTSTATUS STDCALL ZwGetContextThread(IN HANDLE ThreadHandle,
512 OUT PCONTEXT Context)
513 {
514 UNIMPLEMENTED;
515 }
516
517 NTSTATUS STDCALL NtOpenThread(OUT PHANDLE ThreadHandle,
518 IN ACCESS_MASK DesiredAccess,
519 IN POBJECT_ATTRIBUTES ObjectAttributes,
520 IN PCLIENT_ID ClientId)
521 {
522 return(ZwOpenThread(ThreadHandle,
523 DesiredAccess,
524 ObjectAttributes,
525 ClientId));
526 }
527
528 NTSTATUS STDCALL ZwOpenThread(OUT PHANDLE ThreadHandle,
529 IN ACCESS_MASK DesiredAccess,
530 IN POBJECT_ATTRIBUTES ObjectAttributes,
531 IN PCLIENT_ID ClientId)
532 {
533 UNIMPLEMENTED;
534 }
535
536 NTSTATUS STDCALL NtResumeThread(IN HANDLE ThreadHandle,
537 IN PULONG SuspendCount)
538 {
539 return(ZwResumeThread(ThreadHandle,SuspendCount));
540 }
541
542 NTSTATUS STDCALL ZwResumeThread(IN HANDLE ThreadHandle,
543 IN PULONG SuspendCount)
544 /*
545 * FUNCTION: Decrements a thread's resume count
546 * ARGUMENTS:
547 * ThreadHandle = Handle to the thread that should be resumed
548 * ResumeCount = The resulting resume count.
549 * REMARK:
550 * A thread is resumed if its suspend count is 0. This procedure maps to
551 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
552 * RETURNS: Status
553 */
554 {
555 PETHREAD Thread;
556 NTSTATUS Status;
557
558 Status = ObReferenceObjectByHandle(ThreadHandle,
559 THREAD_SUSPEND_RESUME,
560 PsThreadType,
561 UserMode,
562 (PVOID*)&Thread,
563 NULL);
564 if (Status != STATUS_SUCCESS)
565 {
566 return(Status);
567 }
568
569 (*SuspendCount) = InterlockedDecrement(&Thread->Tcb.SuspendCount);
570 if (Thread->Tcb.SuspendCount <= 0)
571 {
572 Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;
573 }
574
575 ObDereferenceObject(Thread);
576 return(STATUS_SUCCESS);
577 }
578
579 NTSTATUS STDCALL NtSetContextThread(IN HANDLE ThreadHandle,
580 IN PCONTEXT Context)
581 {
582 return(ZwSetContextThread(ThreadHandle,Context));
583 }
584
585 NTSTATUS STDCALL ZwSetContextThread(IN HANDLE ThreadHandle,
586 IN PCONTEXT Context)
587 {
588 UNIMPLEMENTED;
589 }
590
591 NTSTATUS STDCALL NtSuspendThread(IN HANDLE ThreadHandle,
592 IN PULONG PreviousSuspendCount)
593 {
594 return(ZwSuspendThread(ThreadHandle,PreviousSuspendCount));
595 }
596
597 NTSTATUS STDCALL ZwSuspendThread(IN HANDLE ThreadHandle,
598 IN PULONG PreviousSuspendCount)
599 /*
600 * FUNCTION: Increments a thread's suspend count
601 * ARGUMENTS:
602 * ThreadHandle = Handle to the thread that should be resumed
603 * PreviousSuspendCount = The resulting/previous suspend count.
604 * REMARK:
605 * A thread will be suspended if its suspend count is greater than 0.
606 * This procedure maps to the win32 SuspendThread function. (
607 * documentation about the the suspend count can be found here aswell )
608 * The suspend count is not increased if it is greater than
609 * MAXIMUM_SUSPEND_COUNT.
610 * RETURNS: Status
611 */
612 {
613 PETHREAD Thread;
614 NTSTATUS Status;
615
616 Status = ObReferenceObjectByHandle(ThreadHandle,
617 THREAD_SUSPEND_RESUME,
618 PsThreadType,
619 UserMode,
620 (PVOID*)&Thread,
621 NULL);
622 if (Status != STATUS_SUCCESS)
623 {
624 return(Status);
625 }
626
627 (*PreviousSuspendCount) = InterlockedIncrement(&Thread->Tcb.SuspendCount);
628 if (Thread->Tcb.SuspendCount > 0)
629 {
630 Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;
631 if (Thread == PsGetCurrentThread())
632 {
633 PsDispatchThread();
634 }
635 }
636
637 ObDereferenceObject(Thread);
638 return(STATUS_SUCCESS);
639 }
640
641 NTSTATUS STDCALL NtContinue(IN PCONTEXT Context, IN CINT IrqLevel)
642 {
643 return(ZwContinue(Context,IrqLevel));
644 }
645
646 NTSTATUS STDCALL ZwContinue(IN PCONTEXT Context, IN CINT IrqLevel)
647 {
648 UNIMPLEMENTED;
649 }
650
651 NTSTATUS STDCALL NtYieldExecution(VOID)
652 {
653 return(ZwYieldExecution());
654 }
655
656 NTSTATUS STDCALL ZwYieldExecution(VOID)
657 {
658 PsDispatchThread();
659 return(STATUS_SUCCESS);
660 }