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