Added process ids.
[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 #include <internal/ob.h>
30
31 #define NDEBUG
32 #include <internal/debug.h>
33
34 /* TYPES *******************************************************************/
35
36 /* GLOBALS ******************************************************************/
37
38 POBJECT_TYPE PsThreadType = NULL;
39
40 #define NR_THREAD_PRIORITY_LEVELS (31)
41 #define THREAD_PRIORITY_MAX (15)
42
43 static KSPIN_LOCK ThreadListLock = {0,};
44
45 /*
46 * PURPOSE: List of threads associated with each priority level
47 */
48 static LIST_ENTRY PriorityListHead[NR_THREAD_PRIORITY_LEVELS]={{NULL,NULL},};
49 static BOOLEAN DoneInitYet = FALSE;
50 ULONG PiNrThreads = 0;
51 ULONG PiNrRunnableThreads = 0;
52
53 static PETHREAD CurrentThread = NULL;
54
55 static ULONG NextUniqueThreadId = 0;
56
57 /* FUNCTIONS ***************************************************************/
58
59 PKTHREAD KeGetCurrentThread(VOID)
60 {
61 return(&(CurrentThread->Tcb));
62 }
63
64 PETHREAD PsGetCurrentThread(VOID)
65 {
66 return(CurrentThread);
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 KeBugCheck(0);
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 = TickCount.LowPart;
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 = TickCount.LowPart;
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 &NextUniqueThreadId);
260 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
261 DbgPrint("Thread->Cid.UniqueThread %d\nThread->Cid.UniqueProcess %d\n",
262 Thread->Cid.UniqueThread, Thread->Cid.UniqueThread);
263 ObReferenceObjectByPointer(Thread,
264 THREAD_ALL_ACCESS,
265 PsThreadType,
266 UserMode);
267 PsInsertIntoThreadList(Thread->Tcb.CurrentPriority,Thread);
268
269 *ThreadPtr = Thread;
270
271 ObDereferenceObject(Thread->ThreadsProcess);
272 return(STATUS_SUCCESS);
273 }
274
275 VOID PsResumeThread(PETHREAD Thread)
276 {
277 DPRINT("PsResumeThread(Thread %x)\n",Thread);
278 Thread->Tcb.SuspendCount--;
279 if (Thread->Tcb.SuspendCount <= 0 &&
280 Thread->Tcb.ThreadState != THREAD_STATE_RUNNING)
281 {
282 DPRINT("Setting thread to runnable\n");
283 Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;
284 }
285 DPRINT("Finished PsResumeThread()\n");
286 }
287
288 VOID PsSuspendThread(PETHREAD Thread)
289 {
290 DPRINT("PsSuspendThread(Thread %x)\n",Thread);
291 Thread->Tcb.SuspendCount++;
292 if (Thread->Tcb.SuspendCount > 0)
293 {
294 Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;
295 if (Thread == CurrentThread)
296 {
297 PsDispatchThread();
298 }
299 }
300 }
301
302 VOID PiDeleteThread(PVOID ObjectBody)
303 {
304 DbgPrint("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
305 }
306
307 VOID PsInitThreadManagment(VOID)
308 /*
309 * FUNCTION: Initialize thread managment
310 */
311 {
312 PETHREAD FirstThread;
313 ULONG i;
314 ANSI_STRING AnsiString;
315 HANDLE FirstThreadHandle;
316
317 KeInitializeSpinLock(&ThreadListLock);
318 for (i=0; i<NR_THREAD_PRIORITY_LEVELS; i++)
319 {
320 InitializeListHead(&PriorityListHead[i]);
321 }
322
323 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
324
325 RtlInitAnsiString(&AnsiString,"Thread");
326 RtlAnsiStringToUnicodeString(&PsThreadType->TypeName,&AnsiString,TRUE);
327
328 PsThreadType->TotalObjects = 0;
329 PsThreadType->TotalHandles = 0;
330 PsThreadType->MaxObjects = 0;
331 PsThreadType->MaxHandles = 0;
332 PsThreadType->PagedPoolCharge = 0;
333 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
334 PsThreadType->Dump = NULL;
335 PsThreadType->Open = NULL;
336 PsThreadType->Close = NULL;
337 PsThreadType->Delete = PiDeleteThread;
338 PsThreadType->Parse = NULL;
339 PsThreadType->Security = NULL;
340 PsThreadType->QueryName = NULL;
341 PsThreadType->OkayToClose = NULL;
342
343 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
344 THREAD_ALL_ACCESS,NULL);
345 HalInitFirstTask(FirstThread);
346 FirstThread->Tcb.ThreadState = THREAD_STATE_RUNNING;
347 FirstThread->Tcb.SuspendCount = 0;
348
349 DPRINT("FirstThread %x\n",FirstThread);
350
351 CurrentThread = FirstThread;
352
353 DoneInitYet = TRUE;
354 }
355
356 NTSTATUS NtCreateThread(PHANDLE ThreadHandle,
357 ACCESS_MASK DesiredAccess,
358 POBJECT_ATTRIBUTES ObjectAttributes,
359 HANDLE ProcessHandle,
360 PCLIENT_ID Client,
361 PCONTEXT ThreadContext,
362 PINITIAL_TEB InitialTeb,
363 BOOLEAN CreateSuspended)
364 {
365 return(ZwCreateThread(ThreadHandle,
366 DesiredAccess,
367 ObjectAttributes,
368 ProcessHandle,
369 Client,
370 ThreadContext,
371 InitialTeb,
372 CreateSuspended));
373 }
374
375 NTSTATUS ZwCreateThread(PHANDLE ThreadHandle,
376 ACCESS_MASK DesiredAccess,
377 POBJECT_ATTRIBUTES ObjectAttributes,
378 HANDLE ProcessHandle,
379 PCLIENT_ID Client,
380 PCONTEXT ThreadContext,
381 PINITIAL_TEB InitialTeb,
382 BOOLEAN CreateSuspended)
383 {
384 PETHREAD Thread;
385 NTSTATUS Status;
386
387 DPRINT("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
388 ThreadHandle,ThreadContext);
389
390 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
391 DesiredAccess,ObjectAttributes);
392 if (!NT_SUCCESS(Status))
393 {
394 return(Status);
395 }
396
397 Status = HalInitTaskWithContext(Thread,ThreadContext);
398 if (!NT_SUCCESS(Status))
399 {
400 return(Status);
401 }
402 Thread->StartAddress=NULL;
403
404 if (Client!=NULL)
405 {
406 *Client=Thread->Cid;
407 }
408
409 if (!CreateSuspended)
410 {
411 DPRINT("Not creating suspended\n");
412 PsResumeThread(Thread);
413 }
414 DPRINT("Finished PsCreateThread()\n");
415 return(STATUS_SUCCESS);
416 }
417
418 NTSTATUS PsCreateSystemThread(PHANDLE ThreadHandle,
419 ACCESS_MASK DesiredAccess,
420 POBJECT_ATTRIBUTES ObjectAttributes,
421 HANDLE ProcessHandle,
422 PCLIENT_ID ClientId,
423 PKSTART_ROUTINE StartRoutine,
424 PVOID StartContext)
425 /*
426 * FUNCTION: Creates a thread which executes in kernel mode
427 * ARGUMENTS:
428 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
429 * handle
430 * DesiredAccess = Requested access to the thread
431 * ObjectAttributes = Object attributes (optional)
432 * ProcessHandle = Handle of process thread will run in
433 * NULL to use system process
434 * ClientId (OUT) = Caller supplied storage for the returned client id
435 * of the thread (optional)
436 * StartRoutine = Entry point for the thread
437 * StartContext = Argument supplied to the thread when it begins
438 * execution
439 * RETURNS: Success or failure status
440 */
441 {
442 PETHREAD Thread;
443 NTSTATUS Status;
444
445 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
446 ThreadHandle,ProcessHandle);
447
448 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
449 DesiredAccess,ObjectAttributes);
450 if (!NT_SUCCESS(Status))
451 {
452 return(Status);
453 }
454
455 Thread->StartAddress=StartRoutine;
456 Status = HalInitTask(Thread,StartRoutine,StartContext);
457 if (!NT_SUCCESS(Status))
458 {
459 return(Status);
460 }
461
462 if (ClientId!=NULL)
463 {
464 *ClientId=Thread->Cid;
465 }
466
467 PsResumeThread(Thread);
468
469 return(STATUS_SUCCESS);
470 }
471
472 LONG KeSetBasePriorityThread(PKTHREAD Thread, LONG Increment)
473 {
474 UNIMPLEMENTED;
475 }
476
477 KPRIORITY KeSetPriorityThread(PKTHREAD Thread, KPRIORITY Priority)
478 {
479 KPRIORITY OldPriority;
480 OldPriority = Thread->CurrentPriority;
481 Thread->CurrentPriority = Priority;
482
483 RemoveEntryList(&Thread->Entry);
484 PsInsertIntoThreadList(Thread->CurrentPriority,
485 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
486
487 return(OldPriority);
488 }
489
490 NTSTATUS STDCALL NtAlertResumeThread(IN HANDLE ThreadHandle,
491 OUT PULONG SuspendCount)
492 {
493 return(ZwAlertResumeThread(ThreadHandle,SuspendCount));
494 }
495
496 NTSTATUS STDCALL ZwAlertResumeThread(IN HANDLE ThreadHandle,
497 OUT PULONG SuspendCount)
498 {
499 UNIMPLEMENTED;
500 }
501
502 NTSTATUS STDCALL NtAlertThread(IN HANDLE ThreadHandle)
503 {
504 return(ZwAlertThread(ThreadHandle));
505 }
506
507 NTSTATUS STDCALL ZwAlertThread(IN HANDLE ThreadHandle)
508 {
509 UNIMPLEMENTED;
510 }
511
512 NTSTATUS STDCALL NtGetContextThread(IN HANDLE ThreadHandle,
513 OUT PCONTEXT Context)
514 {
515 return(ZwGetContextThread(ThreadHandle,Context));
516 }
517
518 NTSTATUS STDCALL ZwGetContextThread(IN HANDLE ThreadHandle,
519 OUT PCONTEXT Context)
520 {
521 UNIMPLEMENTED;
522 }
523
524 NTSTATUS STDCALL NtOpenThread(OUT PHANDLE ThreadHandle,
525 IN ACCESS_MASK DesiredAccess,
526 IN POBJECT_ATTRIBUTES ObjectAttributes,
527 IN PCLIENT_ID ClientId)
528 {
529 return(ZwOpenThread(ThreadHandle,
530 DesiredAccess,
531 ObjectAttributes,
532 ClientId));
533 }
534
535 NTSTATUS STDCALL ZwOpenThread(OUT PHANDLE ThreadHandle,
536 IN ACCESS_MASK DesiredAccess,
537 IN POBJECT_ATTRIBUTES ObjectAttributes,
538 IN PCLIENT_ID ClientId)
539 {
540 UNIMPLEMENTED;
541 }
542
543 NTSTATUS STDCALL NtResumeThread(IN HANDLE ThreadHandle,
544 IN PULONG SuspendCount)
545 {
546 return(ZwResumeThread(ThreadHandle,SuspendCount));
547 }
548
549 NTSTATUS STDCALL ZwResumeThread(IN HANDLE ThreadHandle,
550 IN PULONG SuspendCount)
551 /*
552 * FUNCTION: Decrements a thread's resume count
553 * ARGUMENTS:
554 * ThreadHandle = Handle to the thread that should be resumed
555 * ResumeCount = The resulting resume count.
556 * REMARK:
557 * A thread is resumed if its suspend count is 0. This procedure maps to
558 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
559 * RETURNS: Status
560 */
561 {
562 PETHREAD Thread;
563 NTSTATUS Status;
564
565 Status = ObReferenceObjectByHandle(ThreadHandle,
566 THREAD_SUSPEND_RESUME,
567 PsThreadType,
568 UserMode,
569 (PVOID*)&Thread,
570 NULL);
571 if (Status != STATUS_SUCCESS)
572 {
573 return(Status);
574 }
575
576 (*SuspendCount) = InterlockedDecrement(&Thread->Tcb.SuspendCount);
577 if (Thread->Tcb.SuspendCount <= 0)
578 {
579 Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;
580 }
581
582 ObDereferenceObject(Thread);
583 return(STATUS_SUCCESS);
584 }
585
586 NTSTATUS STDCALL NtSetContextThread(IN HANDLE ThreadHandle,
587 IN PCONTEXT Context)
588 {
589 return(ZwSetContextThread(ThreadHandle,Context));
590 }
591
592 NTSTATUS STDCALL ZwSetContextThread(IN HANDLE ThreadHandle,
593 IN PCONTEXT Context)
594 {
595 UNIMPLEMENTED;
596 }
597
598 NTSTATUS STDCALL NtSuspendThread(IN HANDLE ThreadHandle,
599 IN PULONG PreviousSuspendCount)
600 {
601 return(ZwSuspendThread(ThreadHandle,PreviousSuspendCount));
602 }
603
604 NTSTATUS STDCALL ZwSuspendThread(IN HANDLE ThreadHandle,
605 IN PULONG PreviousSuspendCount)
606 /*
607 * FUNCTION: Increments a thread's suspend count
608 * ARGUMENTS:
609 * ThreadHandle = Handle to the thread that should be resumed
610 * PreviousSuspendCount = The resulting/previous suspend count.
611 * REMARK:
612 * A thread will be suspended if its suspend count is greater than 0.
613 * This procedure maps to the win32 SuspendThread function. (
614 * documentation about the the suspend count can be found here aswell )
615 * The suspend count is not increased if it is greater than
616 * MAXIMUM_SUSPEND_COUNT.
617 * RETURNS: Status
618 */
619 {
620 PETHREAD Thread;
621 NTSTATUS Status;
622
623 Status = ObReferenceObjectByHandle(ThreadHandle,
624 THREAD_SUSPEND_RESUME,
625 PsThreadType,
626 UserMode,
627 (PVOID*)&Thread,
628 NULL);
629 if (Status != STATUS_SUCCESS)
630 {
631 return(Status);
632 }
633
634 (*PreviousSuspendCount) = InterlockedIncrement(&Thread->Tcb.SuspendCount);
635 if (Thread->Tcb.SuspendCount > 0)
636 {
637 Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;
638 if (Thread == PsGetCurrentThread())
639 {
640 PsDispatchThread();
641 }
642 }
643
644 ObDereferenceObject(Thread);
645 return(STATUS_SUCCESS);
646 }
647
648 NTSTATUS STDCALL NtContinue(IN PCONTEXT Context, IN CINT IrqLevel)
649 {
650 return(ZwContinue(Context,IrqLevel));
651 }
652
653 NTSTATUS STDCALL ZwContinue(IN PCONTEXT Context, IN CINT IrqLevel)
654 {
655 UNIMPLEMENTED;
656 }
657
658 NTSTATUS STDCALL NtYieldExecution(VOID)
659 {
660 return(ZwYieldExecution());
661 }
662
663 NTSTATUS STDCALL ZwYieldExecution(VOID)
664 {
665 PsDispatchThread();
666 return(STATUS_SUCCESS);
667 }