bootstrap
[reactos.git] / ntoskrnl / ps / thread.c
1 /* $Id$
2  *
3  * COPYRIGHT:              See COPYING in the top level directory
4  * PROJECT:                ReactOS kernel
5  * FILE:                   ntoskrnl/ps/thread.c
6  * PURPOSE:                Thread managment
7  * PROGRAMMER:             David Welch (welch@mcmail.com)
8  * REVISION HISTORY: 
9  *               23/06/98: Created
10  *               12/10/99: Phillip Susi:  Thread priorities, and APC work
11  */
12
13 /*
14  * NOTE:
15  * 
16  * All of the routines that manipulate the thread queue synchronize on
17  * a single spinlock
18  * 
19  */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/ob.h>
28 #include <internal/pool.h>
29 #include <ntos/minmax.h>
30 #include <internal/ldr.h>
31
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* TYPES *******************************************************************/
36
37 /* GLOBALS ******************************************************************/
38
39 POBJECT_TYPE EXPORTED PsThreadType = NULL;
40
41 KSPIN_LOCK PiThreadListLock;
42
43 /*
44  * PURPOSE: List of threads associated with each priority level
45  */
46 static LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
47 static BOOLEAN DoneInitYet = FALSE;
48 LIST_ENTRY PiThreadListHead;
49 #ifndef LIBCAPTIVE
50 static PETHREAD IdleThreads[MAXIMUM_PROCESSORS];
51 #endif /* LIBCAPTIVE */
52 ULONG PiNrThreads = 0;
53 #ifndef LIBCAPTIVE
54 ULONG PiNrReadyThreads = 0;
55 static HANDLE PiReaperThreadHandle;
56 static KEVENT PiReaperThreadEvent;
57 static BOOL PiReaperThreadShouldTerminate = FALSE;
58 ULONG PiNrThreadsAwaitingReaping = 0;
59 #endif /* LIBCAPTIVE */
60
61 static GENERIC_MAPPING PiThreadMapping = {THREAD_READ,
62                                           THREAD_WRITE,
63                                           THREAD_EXECUTE,
64                                           THREAD_ALL_ACCESS};
65
66 /* FUNCTIONS ***************************************************************/
67
68 #ifndef LIBCAPTIVE
69
70 PKTHREAD STDCALL KeGetCurrentThread(VOID)
71 {
72    return(KeGetCurrentKPCR()->CurrentThread);
73 }
74
75 #endif /* LIBCAPTIVE */
76
77 PETHREAD STDCALL PsGetCurrentThread(VOID)
78 {
79   PKTHREAD CurrentThread = KeGetCurrentKPCR()->CurrentThread;
80   return(CONTAINING_RECORD(CurrentThread, ETHREAD, Tcb));
81 }
82
83 #ifndef LIBCAPTIVE
84
85 HANDLE STDCALL PsGetCurrentThreadId(VOID)
86 {
87    return(PsGetCurrentThread()->Cid.UniqueThread);
88 }
89
90 VOID 
91 PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
92 {
93    if (Priority >= MAXIMUM_PRIORITY || Priority < 0)
94      {
95         DPRINT1("Invalid thread priority\n");
96         KeBugCheck(0);
97      }
98    InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry);
99    PiNrReadyThreads++;
100 }
101
102 VOID PsDumpThreads(BOOLEAN IncludeSystem)
103 {
104    PLIST_ENTRY current_entry;
105    PETHREAD current;
106    ULONG t;
107    ULONG i;
108    
109    current_entry = PiThreadListHead.Flink;
110    t = 0;
111    
112    while (current_entry != &PiThreadListHead)
113      {
114        PULONG Ebp;
115        PULONG Esp;
116
117        current = CONTAINING_RECORD(current_entry, ETHREAD, 
118                                    Tcb.ThreadListEntry);
119        t++;
120        if (t > PiNrThreads)
121          {
122            DbgPrint("Too many threads on list\n");
123            return;
124          }
125        if (IncludeSystem || current->ThreadsProcess->UniqueProcessId >= 6)
126          {
127            DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s Stack: \n",
128                     current->Tcb.State, 
129                     current->ThreadsProcess->UniqueProcessId,
130                     current->Cid.UniqueThread, 
131                     current->ThreadsProcess->ImageFileName);
132            if (current->Tcb.State == THREAD_STATE_READY ||
133                current->Tcb.State == THREAD_STATE_SUSPENDED ||
134                current->Tcb.State == THREAD_STATE_BLOCKED)
135              {
136                Esp = (PULONG)current->Tcb.KernelStack;
137                Ebp = (PULONG)Esp[3];
138                DbgPrint("Ebp 0x%.8X\n", Ebp);
139                i = 0;
140                while (Ebp != 0 && Ebp >= (PULONG)current->Tcb.StackLimit)
141                  {
142                    DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1],
143                             (i % 8) == 7 ? "\n" : "  ");
144                    Ebp = (PULONG)Ebp[0];
145                    i++;
146                  }
147                if ((i % 8) != 7)
148                  {
149                    DbgPrint("\n");
150                  }
151              }
152          }
153        current_entry = current_entry->Flink;
154      }
155 }
156
157 static PETHREAD PsScanThreadList (KPRIORITY Priority, ULONG Affinity)
158 {
159    PLIST_ENTRY current_entry;
160    PETHREAD current;
161
162    current_entry = PriorityListHead[Priority].Flink;
163    while (current_entry != &PriorityListHead[Priority])
164      {
165        current = CONTAINING_RECORD(current_entry, ETHREAD,
166                                    Tcb.QueueListEntry);
167        assert(current->Tcb.State == THREAD_STATE_READY);
168        DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
169                current->Tcb.UserAffinity, Affinity, current->Cid.UniqueThread,
170                Priority);
171        if (current->Tcb.UserAffinity & Affinity)
172          {
173            RemoveEntryList(&current->Tcb.QueueListEntry);
174            return(current);
175          }
176        current_entry = current_entry->Flink;
177      }
178    return(NULL);
179 }
180
181 VOID STDCALL
182 PiWakeupReaperThread(VOID)
183 {
184   KeSetEvent(&PiReaperThreadEvent, 0, FALSE);
185 }
186
187 NTSTATUS STDCALL
188 PiReaperThreadMain(PVOID Ignored)
189 {
190   while (1)
191     {
192       KeWaitForSingleObject(&PiReaperThreadEvent,
193                             Executive,
194                             KernelMode,
195                             FALSE,
196                             NULL);
197       if (PiReaperThreadShouldTerminate)
198         {
199           PsTerminateSystemThread(0);
200         }
201       PsReapThreads();
202     }
203 }
204
205 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
206 {
207    KPRIORITY CurrentPriority;
208    PETHREAD Candidate;
209    ULONG Affinity;
210    PKTHREAD KCurrentThread = KeGetCurrentKPCR()->CurrentThread;
211    PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb);
212
213    DPRINT("PsDispatchThread() %d/%d\n", KeGetCurrentProcessorNumber(),
214            CurrentThread->Cid.UniqueThread);
215    
216    CurrentThread->Tcb.State = NewThreadStatus;
217    if (CurrentThread->Tcb.State == THREAD_STATE_READY)
218      {
219         PiNrReadyThreads++;
220         PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
221                                CurrentThread);
222      }
223    if (CurrentThread->Tcb.State == THREAD_STATE_TERMINATED_1)
224      {
225        PiNrThreadsAwaitingReaping++;
226      }
227    
228    Affinity = 1 << KeGetCurrentProcessorNumber();
229    for (CurrentPriority = HIGH_PRIORITY;
230         CurrentPriority >= LOW_PRIORITY;
231         CurrentPriority--)
232      {
233         Candidate = PsScanThreadList(CurrentPriority, Affinity);
234         if (Candidate == CurrentThread)
235           {
236              KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
237              return;
238           }
239         if (Candidate != NULL)
240           {
241             PETHREAD OldThread;
242
243             DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
244             
245             Candidate->Tcb.State = THREAD_STATE_RUNNING;
246             
247             OldThread = CurrentThread;
248             CurrentThread = Candidate;
249              
250             KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
251             if (PiNrThreadsAwaitingReaping > 0)
252               {
253                 PiWakeupReaperThread();
254               }
255             KiArchContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb);
256             return;
257           }
258      }
259    CPRINT("CRITICAL: No threads are ready\n");
260    KeBugCheck(0);
261 }
262
263 VOID STDCALL
264 PsDispatchThread(ULONG NewThreadStatus)
265 {
266    KIRQL oldIrql;
267    
268    if (!DoneInitYet)
269      {
270         return;
271      }
272    
273    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
274    /*
275     * Save wait IRQL
276     */
277    KeGetCurrentKPCR()->CurrentThread->WaitIrql = oldIrql;   
278    PsDispatchThreadNoLock(NewThreadStatus);
279    KeLowerIrql(oldIrql);
280 }
281
282 VOID
283 PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus)
284 {
285   KIRQL oldIrql;
286
287   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
288   if (WaitStatus != NULL)
289     {
290       Thread->Tcb.WaitStatus = *WaitStatus;
291     }
292   Thread->Tcb.State = THREAD_STATE_READY;
293   PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
294   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
295 }
296
297 VOID
298 PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode, 
299               BOOLEAN DispatcherLock, KIRQL WaitIrql)
300 {
301   KIRQL oldIrql;
302   PKTHREAD KThread = KeGetCurrentKPCR()->CurrentThread;
303   PETHREAD Thread = CONTAINING_RECORD (KThread, ETHREAD, Tcb);
304   PKWAIT_BLOCK WaitBlock;
305
306   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
307
308   if (KThread->ApcState.KernelApcPending)
309   {
310     if (!DispatcherLock)
311       {
312         KeAcquireDispatcherDatabaseLock(FALSE);
313       }
314     WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
315     while (WaitBlock)
316       {
317         RemoveEntryList (&WaitBlock->WaitListEntry);
318         WaitBlock = WaitBlock->NextWaitBlock;
319       }
320     Thread->Tcb.WaitBlockList = NULL;
321     KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
322     PsDispatchThreadNoLock (THREAD_STATE_READY);
323     if (Status != NULL)
324       {
325         *Status = STATUS_KERNEL_APC;
326       }
327   }
328   else
329     {
330       if (DispatcherLock)
331         {
332           KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
333         }
334       Thread->Tcb.Alertable = Alertable;
335       Thread->Tcb.WaitMode = WaitMode;
336       Thread->Tcb.WaitIrql = WaitIrql;
337       PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
338       if (Status != NULL)
339         {
340           *Status = Thread->Tcb.WaitStatus;
341         }
342     }
343   KeLowerIrql(WaitIrql);
344 }
345
346 VOID
347 PsFreezeAllThreads(PEPROCESS Process)
348      /*
349       * Used by the debugging code to freeze all the process's threads 
350       * while the debugger is examining their state. 
351       */
352 {
353   KIRQL oldIrql;
354   PLIST_ENTRY current_entry;
355   PETHREAD current;
356
357   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
358
359   current_entry = Process->ThreadListHead.Flink;
360   while (current_entry != &Process->ThreadListHead)
361     {
362       current = CONTAINING_RECORD(current_entry, ETHREAD, 
363                                   Tcb.ProcessThreadListEntry);
364
365       /*
366        * We have to be careful here, we can't just set the freeze the
367        * thread inside kernel mode since it may be holding a lock.
368        */
369
370       current_entry = current_entry->Flink;
371     }
372   
373   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
374 }
375
376 VOID
377 PsApplicationProcessorInit(VOID)
378 {
379   KeGetCurrentKPCR()->CurrentThread = 
380     (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
381 }
382
383 VOID
384 PsPrepareForApplicationProcessorInit(ULONG Id)
385 {
386   PETHREAD IdleThread;
387   HANDLE IdleThreadHandle;
388
389   PsInitializeThread(NULL,
390                      &IdleThread,
391                      &IdleThreadHandle,
392                      THREAD_ALL_ACCESS,
393                      NULL, 
394                      TRUE);
395   IdleThread->Tcb.State = THREAD_STATE_RUNNING;
396   IdleThread->Tcb.FreezeCount = 0;
397   IdleThread->Tcb.UserAffinity = 1 << Id;
398   IdleThread->Tcb.Priority = LOW_PRIORITY;
399   IdleThreads[Id] = IdleThread;
400
401   NtClose(IdleThreadHandle);
402   DPRINT("IdleThread for Processor %d has PID %d\n",
403            Id, IdleThread->Cid.UniqueThread);
404 }
405
406 #endif /* LIBCAPTIVE */
407
408 VOID 
409 PsInitThreadManagment(VOID)
410 /*
411  * FUNCTION: Initialize thread managment
412  */
413 {
414    PETHREAD FirstThread;
415    ULONG i;
416    HANDLE FirstThreadHandle;
417 #ifndef LIBCAPTIVE
418    NTSTATUS Status;
419 #endif /* LIBCAPTIVE */
420    
421    KeInitializeSpinLock(&PiThreadListLock);
422    for (i=0; i < MAXIMUM_PRIORITY; i++)
423      {
424         InitializeListHead(&PriorityListHead[i]);
425      }
426
427    InitializeListHead(&PiThreadListHead);
428    
429    PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
430    
431    RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, REACTOS_UCS2(L"Thread"));
432    
433    PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
434    PsThreadType->TotalObjects = 0;
435    PsThreadType->TotalHandles = 0;
436    PsThreadType->MaxObjects = 0;
437    PsThreadType->MaxHandles = 0;
438    PsThreadType->PagedPoolCharge = 0;
439    PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
440    PsThreadType->Mapping = &PiThreadMapping;
441    PsThreadType->Dump = NULL;
442    PsThreadType->Open = NULL;
443    PsThreadType->Close = NULL;
444 #ifndef LIBCAPTIVE
445    PsThreadType->Delete = PiDeleteThread;
446 #else /* !LIBCAPTIVE */
447    PsThreadType->Delete = NULL;
448 #endif /* !LIBCAPTIVE */
449    PsThreadType->Parse = NULL;
450    PsThreadType->Security = NULL;
451    PsThreadType->QueryName = NULL;
452    PsThreadType->OkayToClose = NULL;
453    PsThreadType->Create = NULL;
454    PsThreadType->DuplicationNotify = NULL;
455    
456    PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
457                       THREAD_ALL_ACCESS,NULL, TRUE);
458    FirstThread->Tcb.State = THREAD_STATE_RUNNING;
459    FirstThread->Tcb.FreezeCount = 0;
460    KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
461    NtClose(FirstThreadHandle);
462    
463    DPRINT("FirstThread %x\n",FirstThread);
464       
465    DoneInitYet = TRUE;
466
467 #ifndef LIBCAPTIVE
468    /*
469     * Create the reaper thread
470     */
471    KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
472    Status = PsCreateSystemThread(&PiReaperThreadHandle,
473                                  THREAD_ALL_ACCESS,
474                                  NULL,
475                                  NULL,
476                                  NULL,
477                                  PiReaperThreadMain,
478                                  NULL);
479    if (!NT_SUCCESS(Status))
480      {
481        DPRINT1("PS: Failed to create reaper thread.\n");
482        KeBugCheck(0);
483      }
484 #endif /* LIBCAPTIVE */
485 }
486
487 #ifndef LIBCAPTIVE
488
489 LONG STDCALL
490 KeSetBasePriorityThread (PKTHREAD       Thread,
491                          LONG           Increment)
492 /*
493  * Sets thread's base priority relative to the process' base priority
494  * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
495  */
496 {
497    Thread->BasePriority = 
498      ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
499    if (Thread->BasePriority < LOW_PRIORITY)
500      Thread->BasePriority = LOW_PRIORITY;
501    else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
502            Thread->BasePriority = HIGH_PRIORITY;
503    Thread->Priority = Thread->BasePriority;
504    return 1;
505 }
506
507
508 KPRIORITY STDCALL
509 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
510 {
511    KPRIORITY OldPriority;
512    KIRQL oldIrql;
513    
514    if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
515      {
516         KeBugCheck(0);
517      }
518    
519    OldPriority = Thread->Priority;
520    Thread->Priority = (CHAR)Priority;
521
522    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
523    if (Thread->State == THREAD_STATE_READY)
524     {
525         RemoveEntryList(&Thread->QueueListEntry);
526         PsInsertIntoThreadList(Thread->BasePriority, 
527                                CONTAINING_RECORD(Thread,ETHREAD,Tcb));
528      }
529    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
530    return(OldPriority);
531 }
532
533
534 NTSTATUS STDCALL 
535 NtAlertResumeThread(IN  HANDLE ThreadHandle,
536                     OUT PULONG  SuspendCount)
537 {
538    UNIMPLEMENTED;
539 }
540
541
542 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
543 {
544    PETHREAD Thread;
545    NTSTATUS Status;
546    NTSTATUS ThreadStatus;
547    
548    Status = ObReferenceObjectByHandle(ThreadHandle,
549                                       THREAD_SUSPEND_RESUME,
550                                       PsThreadType,
551                                       UserMode,
552                                       (PVOID*)&Thread,
553                                       NULL);
554    if (Status != STATUS_SUCCESS)
555      {
556         return(Status);
557      }
558    
559    ThreadStatus = STATUS_ALERTED;
560    (VOID)PsUnblockThread(Thread, &ThreadStatus);
561    
562    ObDereferenceObject(Thread);
563    return(STATUS_SUCCESS);
564 }
565
566 NTSTATUS STDCALL 
567 NtOpenThread(OUT PHANDLE ThreadHandle,
568              IN ACCESS_MASK DesiredAccess,
569              IN POBJECT_ATTRIBUTES ObjectAttributes,
570              IN PCLIENT_ID ClientId)
571 {
572         UNIMPLEMENTED;
573 }
574
575 NTSTATUS STDCALL 
576 NtContinue(IN PCONTEXT  Context,
577            IN BOOLEAN TestAlert)
578 {
579    PKTRAP_FRAME TrapFrame;
580    
581    /*
582     * Copy the supplied context over the register information that was saved
583     * on entry to kernel mode, it will then be restored on exit
584     * FIXME: Validate the context
585     */
586    TrapFrame = KeGetCurrentThread()->TrapFrame;
587    if (TrapFrame == NULL)
588      {
589         CPRINT("NtContinue called but TrapFrame was NULL\n");
590         KeBugCheck(0);
591      }
592    KeContextToTrapFrame(Context, TrapFrame);
593    return(STATUS_SUCCESS);
594 }
595
596
597 NTSTATUS STDCALL
598 NtYieldExecution(VOID)
599 {
600   PsDispatchThread(THREAD_STATE_READY);
601   return(STATUS_SUCCESS);
602 }
603
604
605 NTSTATUS STDCALL
606 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
607                            OUT PEPROCESS *Process OPTIONAL,
608                            OUT PETHREAD *Thread)
609 {
610   KIRQL oldIrql;
611   PLIST_ENTRY current_entry;
612   PETHREAD current;
613
614   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
615
616   current_entry = PiThreadListHead.Flink;
617   while (current_entry != &PiThreadListHead)
618     {
619       current = CONTAINING_RECORD(current_entry,
620                                   ETHREAD,
621                                   Tcb.ThreadListEntry);
622       if (current->Cid.UniqueThread == Cid->UniqueThread &&
623           current->Cid.UniqueProcess == Cid->UniqueProcess)
624         {
625           if (Process != NULL)
626             *Process = current->ThreadsProcess;
627           *Thread = current;
628           KeReleaseSpinLock(&PiThreadListLock, oldIrql);
629           return(STATUS_SUCCESS);
630         }
631
632       current_entry = current_entry->Flink;
633     }
634
635   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
636
637   return(STATUS_INVALID_PARAMETER);
638 }
639
640
641 NTSTATUS STDCALL
642 PsLookupThreadByThreadId(IN PVOID ThreadId,
643                          OUT PETHREAD *Thread)
644 {
645   KIRQL oldIrql;
646   PLIST_ENTRY current_entry;
647   PETHREAD current;
648
649   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
650
651   current_entry = PiThreadListHead.Flink;
652   while (current_entry != &PiThreadListHead)
653     {
654       current = CONTAINING_RECORD(current_entry,
655                                   ETHREAD,
656                                   Tcb.ThreadListEntry);
657       if (current->Cid.UniqueThread == (HANDLE)ThreadId)
658         {
659           *Thread = current;
660           KeReleaseSpinLock(&PiThreadListLock, oldIrql);
661           return(STATUS_SUCCESS);
662         }
663
664       current_entry = current_entry->Flink;
665     }
666
667   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
668
669   return(STATUS_INVALID_PARAMETER);
670 }
671
672 #endif /* LIBCAPTIVE */
673
674 /* EOF */