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