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