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