branch update for HEAD-2003050101
[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 HANDLE STDCALL PsGetCurrentThreadId(VOID)
74 {
75    return(PsGetCurrentThread()->Cid.UniqueThread);
76 }
77
78 #ifndef LIBCAPTIVE
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, UCHAR WaitReason)
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       Thread->Tcb.WaitReason = WaitReason;
328       PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
329
330       if (Status != NULL)
331         {
332           *Status = Thread->Tcb.WaitStatus;
333         }
334     }
335   KeLowerIrql(WaitIrql);
336 }
337
338 VOID
339 PsFreezeAllThreads(PEPROCESS Process)
340      /*
341       * Used by the debugging code to freeze all the process's threads 
342       * while the debugger is examining their state. 
343       */
344 {
345   KIRQL oldIrql;
346   PLIST_ENTRY current_entry;
347   PETHREAD current;
348
349   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
350
351   current_entry = Process->ThreadListHead.Flink;
352   while (current_entry != &Process->ThreadListHead)
353     {
354       current = CONTAINING_RECORD(current_entry, ETHREAD, 
355                                   Tcb.ProcessThreadListEntry);
356
357       /*
358        * We have to be careful here, we can't just set the freeze the
359        * thread inside kernel mode since it may be holding a lock.
360        */
361
362       current_entry = current_entry->Flink;
363     }
364   
365   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
366 }
367
368 VOID
369 PsApplicationProcessorInit(VOID)
370 {
371   KeGetCurrentKPCR()->CurrentThread = 
372     (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
373 }
374
375 VOID
376 PsPrepareForApplicationProcessorInit(ULONG Id)
377 {
378   PETHREAD IdleThread;
379   HANDLE IdleThreadHandle;
380
381   PsInitializeThread(NULL,
382                      &IdleThread,
383                      &IdleThreadHandle,
384                      THREAD_ALL_ACCESS,
385                      NULL, 
386                      TRUE);
387   IdleThread->Tcb.State = THREAD_STATE_RUNNING;
388   IdleThread->Tcb.FreezeCount = 0;
389   IdleThread->Tcb.UserAffinity = 1 << Id;
390   IdleThread->Tcb.Priority = LOW_PRIORITY;
391   IdleThreads[Id] = IdleThread;
392
393   NtClose(IdleThreadHandle);
394   DPRINT("IdleThread for Processor %d has PID %d\n",
395            Id, IdleThread->Cid.UniqueThread);
396 }
397
398 #endif /* LIBCAPTIVE */
399
400 VOID 
401 PsInitThreadManagment(VOID)
402 /*
403  * FUNCTION: Initialize thread managment
404  */
405 {
406    PETHREAD FirstThread;
407    ULONG i;
408    HANDLE FirstThreadHandle;
409 #ifndef LIBCAPTIVE
410    NTSTATUS Status;
411 #endif /* LIBCAPTIVE */
412    
413    KeInitializeSpinLock(&PiThreadListLock);
414    for (i=0; i < MAXIMUM_PRIORITY; i++)
415      {
416         InitializeListHead(&PriorityListHead[i]);
417      }
418
419    InitializeListHead(&PiThreadListHead);
420    
421    PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
422    
423    RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, REACTOS_UCS2(L"Thread"));
424    
425    PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
426    PsThreadType->TotalObjects = 0;
427    PsThreadType->TotalHandles = 0;
428    PsThreadType->MaxObjects = 0;
429    PsThreadType->MaxHandles = 0;
430    PsThreadType->PagedPoolCharge = 0;
431    PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
432    PsThreadType->Mapping = &PiThreadMapping;
433    PsThreadType->Dump = NULL;
434    PsThreadType->Open = NULL;
435    PsThreadType->Close = NULL;
436 #ifndef LIBCAPTIVE
437    PsThreadType->Delete = PiDeleteThread;
438 #else /* !LIBCAPTIVE */
439    PsThreadType->Delete = NULL;
440 #endif /* !LIBCAPTIVE */
441    PsThreadType->Parse = NULL;
442    PsThreadType->Security = NULL;
443    PsThreadType->QueryName = NULL;
444    PsThreadType->OkayToClose = NULL;
445    PsThreadType->Create = NULL;
446    PsThreadType->DuplicationNotify = NULL;
447    
448    PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
449                       THREAD_ALL_ACCESS,NULL, TRUE);
450    FirstThread->Tcb.State = THREAD_STATE_RUNNING;
451    FirstThread->Tcb.FreezeCount = 0;
452    FirstThread->Tcb.Queue = ExAllocatePool(NonPagedPool, sizeof(*FirstThread->Tcb.Queue));
453    KeInitializeQueue(FirstThread->Tcb.Queue, 0);
454    KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
455    NtClose(FirstThreadHandle);
456    
457    DPRINT("FirstThread %x\n",FirstThread);
458       
459    DoneInitYet = TRUE;
460
461 #ifndef LIBCAPTIVE
462    /*
463     * Create the reaper thread
464     */
465    KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
466    Status = PsCreateSystemThread(&PiReaperThreadHandle,
467                                  THREAD_ALL_ACCESS,
468                                  NULL,
469                                  NULL,
470                                  NULL,
471                                  PiReaperThreadMain,
472                                  NULL);
473    if (!NT_SUCCESS(Status))
474      {
475        DPRINT1("PS: Failed to create reaper thread.\n");
476        KeBugCheck(0);
477      }
478 #endif /* LIBCAPTIVE */
479 }
480
481 #ifndef LIBCAPTIVE
482
483 LONG STDCALL
484 KeSetBasePriorityThread (PKTHREAD       Thread,
485                          LONG           Increment)
486 /*
487  * Sets thread's base priority relative to the process' base priority
488  * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
489  */
490 {
491    Thread->BasePriority = 
492      ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
493    if (Thread->BasePriority < LOW_PRIORITY)
494      Thread->BasePriority = LOW_PRIORITY;
495    else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
496            Thread->BasePriority = HIGH_PRIORITY;
497    Thread->Priority = Thread->BasePriority;
498    return 1;
499 }
500
501
502 KPRIORITY STDCALL
503 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
504 {
505    KPRIORITY OldPriority;
506    KIRQL oldIrql;
507    
508    if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
509      {
510         KeBugCheck(0);
511      }
512    
513    OldPriority = Thread->Priority;
514    Thread->Priority = (CHAR)Priority;
515
516    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
517    if (Thread->State == THREAD_STATE_READY)
518     {
519         RemoveEntryList(&Thread->QueueListEntry);
520         PsInsertIntoThreadList(Thread->BasePriority, 
521                                CONTAINING_RECORD(Thread,ETHREAD,Tcb));
522      }
523    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
524    return(OldPriority);
525 }
526
527 NTSTATUS STDCALL
528 KeSetAffinityThread(PKTHREAD    Thread,
529                                         PVOID           AfMask)
530 /*
531  * Sets thread's affinity
532  */
533 {
534         DPRINT1("KeSetAffinityThread() is a stub returning STATUS_SUCCESS");
535         return STATUS_SUCCESS; // FIXME: Use function below
536         //return ZwSetInformationThread(handle, ThreadAffinityMask,<pointer to affinity mask>,sizeof(KAFFINITY));
537 }
538
539
540 NTSTATUS STDCALL 
541 NtAlertResumeThread(IN  HANDLE ThreadHandle,
542                     OUT PULONG  SuspendCount)
543 {
544    UNIMPLEMENTED;
545 }
546
547
548 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
549 {
550    PETHREAD Thread;
551    NTSTATUS Status;
552    NTSTATUS ThreadStatus;
553    
554    Status = ObReferenceObjectByHandle(ThreadHandle,
555                                       THREAD_SUSPEND_RESUME,
556                                       PsThreadType,
557                                       UserMode,
558                                       (PVOID*)&Thread,
559                                       NULL);
560    if (Status != STATUS_SUCCESS)
561      {
562         return(Status);
563      }
564    
565    ThreadStatus = STATUS_ALERTED;
566    (VOID)PsUnblockThread(Thread, &ThreadStatus);
567    
568    ObDereferenceObject(Thread);
569    return(STATUS_SUCCESS);
570 }
571
572 NTSTATUS STDCALL 
573 NtOpenThread(OUT PHANDLE ThreadHandle,
574              IN ACCESS_MASK DesiredAccess,
575              IN POBJECT_ATTRIBUTES ObjectAttributes,
576              IN PCLIENT_ID ClientId)
577 {
578         UNIMPLEMENTED;
579 }
580
581 NTSTATUS STDCALL 
582 NtContinue(IN PCONTEXT  Context,
583            IN BOOLEAN TestAlert)
584 {
585    PKTRAP_FRAME TrapFrame;
586    
587    /*
588     * Copy the supplied context over the register information that was saved
589     * on entry to kernel mode, it will then be restored on exit
590     * FIXME: Validate the context
591     */
592    TrapFrame = KeGetCurrentThread()->TrapFrame;
593    if (TrapFrame == NULL)
594      {
595         CPRINT("NtContinue called but TrapFrame was NULL\n");
596         KeBugCheck(0);
597      }
598    KeContextToTrapFrame(Context, TrapFrame);
599    return(STATUS_SUCCESS);
600 }
601
602
603 NTSTATUS STDCALL
604 NtYieldExecution(VOID)
605 {
606   PsDispatchThread(THREAD_STATE_READY);
607   return(STATUS_SUCCESS);
608 }
609
610
611 NTSTATUS STDCALL
612 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
613                            OUT PEPROCESS *Process OPTIONAL,
614                            OUT PETHREAD *Thread)
615 {
616   KIRQL oldIrql;
617   PLIST_ENTRY current_entry;
618   PETHREAD current;
619
620   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
621
622   current_entry = PiThreadListHead.Flink;
623   while (current_entry != &PiThreadListHead)
624     {
625       current = CONTAINING_RECORD(current_entry,
626                                   ETHREAD,
627                                   Tcb.ThreadListEntry);
628       if (current->Cid.UniqueThread == Cid->UniqueThread &&
629           current->Cid.UniqueProcess == Cid->UniqueProcess)
630         {
631           if (Process != NULL)
632           {
633             *Process = current->ThreadsProcess;
634             ObReferenceObject(current->ThreadsProcess);
635           }
636
637           *Thread = current;
638           ObReferenceObject(current);
639
640           KeReleaseSpinLock(&PiThreadListLock, oldIrql);
641           return(STATUS_SUCCESS);
642         }
643
644       current_entry = current_entry->Flink;
645     }
646
647   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
648
649   return(STATUS_INVALID_PARAMETER);
650 }
651
652
653 NTSTATUS STDCALL
654 PsLookupThreadByThreadId(IN PVOID ThreadId,
655                          OUT PETHREAD *Thread)
656 {
657   KIRQL oldIrql;
658   PLIST_ENTRY current_entry;
659   PETHREAD current;
660
661   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
662
663   current_entry = PiThreadListHead.Flink;
664   while (current_entry != &PiThreadListHead)
665     {
666       current = CONTAINING_RECORD(current_entry,
667                                   ETHREAD,
668                                   Tcb.ThreadListEntry);
669       if (current->Cid.UniqueThread == (HANDLE)ThreadId)
670         {
671           *Thread = current;
672           ObReferenceObject(current);
673           KeReleaseSpinLock(&PiThreadListLock, oldIrql);
674           return(STATUS_SUCCESS);
675         }
676
677       current_entry = current_entry->Flink;
678     }
679
680   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
681
682   return(STATUS_INVALID_PARAMETER);
683 }
684
685 #endif /* LIBCAPTIVE */
686
687 /* EOF */