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