b75e75041997870231da9da1472bec0ff527cd0e
[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    KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
457    NtClose(FirstThreadHandle);
458    
459    DPRINT("FirstThread %x\n",FirstThread);
460       
461    DoneInitYet = TRUE;
462
463 #ifndef LIBCAPTIVE
464    /*
465     * Create the reaper thread
466     */
467    KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
468    Status = PsCreateSystemThread(&PiReaperThreadHandle,
469                                  THREAD_ALL_ACCESS,
470                                  NULL,
471                                  NULL,
472                                  NULL,
473                                  PiReaperThreadMain,
474                                  NULL);
475    if (!NT_SUCCESS(Status))
476      {
477        DPRINT1("PS: Failed to create reaper thread.\n");
478        KeBugCheck(0);
479      }
480 #endif /* LIBCAPTIVE */
481 }
482
483 #ifndef LIBCAPTIVE
484
485 LONG STDCALL
486 KeSetBasePriorityThread (PKTHREAD       Thread,
487                          LONG           Increment)
488 /*
489  * Sets thread's base priority relative to the process' base priority
490  * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
491  */
492 {
493    Thread->BasePriority = 
494      ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
495    if (Thread->BasePriority < LOW_PRIORITY)
496      Thread->BasePriority = LOW_PRIORITY;
497    else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
498            Thread->BasePriority = HIGH_PRIORITY;
499    Thread->Priority = Thread->BasePriority;
500    return 1;
501 }
502
503
504 KPRIORITY STDCALL
505 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
506 {
507    KPRIORITY OldPriority;
508    KIRQL oldIrql;
509    
510    if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
511      {
512         KeBugCheck(0);
513      }
514    
515    OldPriority = Thread->Priority;
516    Thread->Priority = (CHAR)Priority;
517
518    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
519    if (Thread->State == THREAD_STATE_READY)
520     {
521         RemoveEntryList(&Thread->QueueListEntry);
522         PsInsertIntoThreadList(Thread->BasePriority, 
523                                CONTAINING_RECORD(Thread,ETHREAD,Tcb));
524      }
525    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
526    return(OldPriority);
527 }
528
529
530 NTSTATUS STDCALL 
531 NtAlertResumeThread(IN  HANDLE ThreadHandle,
532                     OUT PULONG  SuspendCount)
533 {
534    UNIMPLEMENTED;
535 }
536
537
538 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
539 {
540    PETHREAD Thread;
541    NTSTATUS Status;
542    NTSTATUS ThreadStatus;
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    ThreadStatus = STATUS_ALERTED;
556    (VOID)PsUnblockThread(Thread, &ThreadStatus);
557    
558    ObDereferenceObject(Thread);
559    return(STATUS_SUCCESS);
560 }
561
562 NTSTATUS STDCALL 
563 NtOpenThread(OUT PHANDLE ThreadHandle,
564              IN ACCESS_MASK DesiredAccess,
565              IN POBJECT_ATTRIBUTES ObjectAttributes,
566              IN PCLIENT_ID ClientId)
567 {
568         UNIMPLEMENTED;
569 }
570
571 NTSTATUS STDCALL 
572 NtContinue(IN PCONTEXT  Context,
573            IN BOOLEAN TestAlert)
574 {
575    PKTRAP_FRAME TrapFrame;
576    
577    /*
578     * Copy the supplied context over the register information that was saved
579     * on entry to kernel mode, it will then be restored on exit
580     * FIXME: Validate the context
581     */
582    TrapFrame = KeGetCurrentThread()->TrapFrame;
583    if (TrapFrame == NULL)
584      {
585         CPRINT("NtContinue called but TrapFrame was NULL\n");
586         KeBugCheck(0);
587      }
588    KeContextToTrapFrame(Context, TrapFrame);
589    return(STATUS_SUCCESS);
590 }
591
592
593 NTSTATUS STDCALL
594 NtYieldExecution(VOID)
595 {
596   PsDispatchThread(THREAD_STATE_READY);
597   return(STATUS_SUCCESS);
598 }
599
600
601 NTSTATUS STDCALL
602 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
603                            OUT PEPROCESS *Process OPTIONAL,
604                            OUT PETHREAD *Thread)
605 {
606   KIRQL oldIrql;
607   PLIST_ENTRY current_entry;
608   PETHREAD current;
609
610   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
611
612   current_entry = PiThreadListHead.Flink;
613   while (current_entry != &PiThreadListHead)
614     {
615       current = CONTAINING_RECORD(current_entry,
616                                   ETHREAD,
617                                   Tcb.ThreadListEntry);
618       if (current->Cid.UniqueThread == Cid->UniqueThread &&
619           current->Cid.UniqueProcess == Cid->UniqueProcess)
620         {
621           if (Process != NULL)
622             *Process = current->ThreadsProcess;
623           *Thread = current;
624           KeReleaseSpinLock(&PiThreadListLock, oldIrql);
625           return(STATUS_SUCCESS);
626         }
627
628       current_entry = current_entry->Flink;
629     }
630
631   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
632
633   return(STATUS_INVALID_PARAMETER);
634 }
635
636
637 NTSTATUS STDCALL
638 PsLookupThreadByThreadId(IN PVOID ThreadId,
639                          OUT PETHREAD *Thread)
640 {
641   KIRQL oldIrql;
642   PLIST_ENTRY current_entry;
643   PETHREAD current;
644
645   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
646
647   current_entry = PiThreadListHead.Flink;
648   while (current_entry != &PiThreadListHead)
649     {
650       current = CONTAINING_RECORD(current_entry,
651                                   ETHREAD,
652                                   Tcb.ThreadListEntry);
653       if (current->Cid.UniqueThread == (HANDLE)ThreadId)
654         {
655           *Thread = current;
656           KeReleaseSpinLock(&PiThreadListLock, oldIrql);
657           return(STATUS_SUCCESS);
658         }
659
660       current_entry = current_entry->Flink;
661     }
662
663   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
664
665   return(STATUS_INVALID_PARAMETER);
666 }
667
668 #endif /* LIBCAPTIVE */
669
670 /* EOF */