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