Fixed prototype for MmSetAddressRangeModified().
[reactos.git] / ntoskrnl / ps / create.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  *               09/08/03: Skywing:       ThreadEventPair support (delete)
12  */
13
14 /*
15  * NOTE:
16  * 
17  * All of the routines that manipulate the thread queue synchronize on
18  * a single spinlock
19  * 
20  */
21
22 /* INCLUDES ****************************************************************/
23
24 #define NTOS_MODE_KERNEL
25 #include <ntos.h>
26 #include <internal/ke.h>
27 #include <internal/ob.h>
28 #include <internal/ps.h>
29 #include <internal/ex.h>
30 #include <internal/se.h>
31 #include <internal/id.h>
32 #include <internal/dbg.h>
33 #include <internal/ldr.h>
34
35 #define NDEBUG
36 #include <internal/debug.h>
37
38 /* GLOBAL *******************************************************************/
39
40 static ULONG PiNextThreadUniqueId = 0;
41
42 extern KSPIN_LOCK PiThreadListLock;
43 extern ULONG PiNrThreads;
44
45 extern LIST_ENTRY PiThreadListHead;
46
47 #ifndef LIBCAPTIVE
48 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT    8
49
50 static ULONG PiThreadNotifyRoutineCount = 0;
51 static PCREATE_THREAD_NOTIFY_ROUTINE
52 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
53 #endif /* LIBCAPTIVE */
54
55 /* FUNCTIONS ***************************************************************/
56
57 #ifndef LIBCAPTIVE
58
59 NTSTATUS STDCALL
60 PsAssignImpersonationToken(PETHREAD Thread,
61                            HANDLE TokenHandle)
62 {
63    PACCESS_TOKEN Token;
64    SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
65    NTSTATUS Status;
66    
67    if (TokenHandle != NULL)
68      {
69         Status = ObReferenceObjectByHandle(TokenHandle,
70                                            0,
71                                            SepTokenObjectType,
72                                            UserMode,
73                                            (PVOID*)&Token,
74                                            NULL);
75         if (!NT_SUCCESS(Status))
76           {
77              return(Status);
78           }
79         ImpersonationLevel = Token->ImpersonationLevel;
80      }
81    else
82      {
83         Token = NULL;
84         ImpersonationLevel = 0;
85      }
86    
87    PsImpersonateClient(Thread,
88                        Token,
89                        0,
90                        0,
91                        ImpersonationLevel);
92    if (Token != NULL)
93      {
94         ObDereferenceObject(Token);
95      }
96    return(STATUS_SUCCESS);
97 }
98
99 /*
100  * @implemented
101  */
102 VOID STDCALL
103 PsRevertToSelf(VOID)
104 {
105    PETHREAD Thread;
106
107    Thread = PsGetCurrentThread();
108
109    if (Thread->ActiveImpersonationInfo != 0)
110      {
111         ObDereferenceObject(Thread->ImpersonationInfo->Token);
112         Thread->ActiveImpersonationInfo = 0;
113      }
114 }
115
116 /*
117  * @implemented
118  */
119 VOID STDCALL
120 PsImpersonateClient(PETHREAD Thread,
121                     PACCESS_TOKEN Token,
122                     UCHAR b,
123                     UCHAR c,
124                     SECURITY_IMPERSONATION_LEVEL Level)
125 {
126    if (Token == 0)
127      {
128         if (Thread->ActiveImpersonationInfo != 0)
129           {
130              Thread->ActiveImpersonationInfo = 0;
131              if (Thread->ImpersonationInfo->Token != NULL)
132                {
133                   ObDereferenceObject(Thread->ImpersonationInfo->Token);
134                }
135           }
136         return;
137      }
138    if (Thread->ActiveImpersonationInfo == 0 ||
139        Thread->ImpersonationInfo == NULL)
140      {
141         Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
142                                            sizeof(PS_IMPERSONATION_INFO));      
143      }
144    Thread->ImpersonationInfo->Level = Level;
145    Thread->ImpersonationInfo->Unknown2 = c;
146    Thread->ImpersonationInfo->Unknown1 = b;
147    Thread->ImpersonationInfo->Token = Token;
148    ObReferenceObjectByPointer(Token,
149                               0,
150                               SepTokenObjectType,
151                               KernelMode);
152    Thread->ActiveImpersonationInfo = 1;
153 }
154
155 PACCESS_TOKEN
156 PsReferenceEffectiveToken(PETHREAD Thread,
157                           PTOKEN_TYPE TokenType,
158                           PUCHAR b,
159                           PSECURITY_IMPERSONATION_LEVEL Level)
160 {
161    PEPROCESS Process;
162    PACCESS_TOKEN Token;
163    
164    if (Thread->ActiveImpersonationInfo == 0)
165      {
166         Process = Thread->ThreadsProcess;
167         *TokenType = TokenPrimary;
168         *b = 0;
169         Token = Process->Token;
170      }
171    else
172      {
173         Token = Thread->ImpersonationInfo->Token;
174         *TokenType = TokenImpersonation;
175         *b = Thread->ImpersonationInfo->Unknown2;
176         *Level = Thread->ImpersonationInfo->Level;      
177      }
178    return(Token);
179 }
180
181 NTSTATUS STDCALL
182 NtImpersonateThread(IN HANDLE ThreadHandle,
183                     IN HANDLE ThreadToImpersonateHandle,
184                     IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)
185 {
186    PETHREAD Thread;
187    PETHREAD ThreadToImpersonate;
188    NTSTATUS Status;
189    SECURITY_CLIENT_CONTEXT ClientContext;
190    
191    Status = ObReferenceObjectByHandle(ThreadHandle,
192                                       0,
193                                       PsThreadType,
194                                       UserMode,
195                                       (PVOID*)&Thread,
196                                       NULL);
197    if (!NT_SUCCESS(Status))
198      {
199         return(Status);
200      }
201    
202    Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
203                                       0,
204                                       PsThreadType,
205                                       UserMode,
206                                       (PVOID*)&ThreadToImpersonate,
207                                       NULL);
208    if (!NT_SUCCESS(Status))
209      {
210         ObDereferenceObject(Thread);
211         return(Status);
212      }
213    
214    Status = SeCreateClientSecurity(ThreadToImpersonate,
215                                    SecurityQualityOfService,
216                                    0,
217                                    &ClientContext);
218    if (!NT_SUCCESS(Status))
219      {
220         ObDereferenceObject(Thread);
221         ObDereferenceObject(ThreadToImpersonate);
222         return(Status);
223      }
224    
225    SeImpersonateClient(&ClientContext, Thread);
226    if (ClientContext.Token != NULL)
227      {
228         ObDereferenceObject(ClientContext.Token);
229      }
230    return(STATUS_SUCCESS);
231 }
232
233 NTSTATUS STDCALL
234 NtOpenThreadToken(IN    HANDLE          ThreadHandle,  
235                   IN    ACCESS_MASK     DesiredAccess,  
236                   IN    BOOLEAN         OpenAsSelf,     
237                   OUT   PHANDLE         TokenHandle)
238 {
239 #if 0
240    PETHREAD Thread;
241    NTSTATUS Status;
242    PACCESS_TOKEN Token;
243    
244    Status = ObReferenceObjectByHandle(ThreadHandle,
245                                       0,
246                                       PsThreadType,
247                                       UserMode,
248                                       (PVOID*)&Thread,
249                                       NULL);
250    if (!NT_SUCCESS(Status))
251      {
252         return(Status);
253      }
254    
255    Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
256    SepCreateImpersonationTokenDacl(Token);
257 #endif
258    return(STATUS_UNSUCCESSFUL);
259 }
260
261 #endif /* LIBCAPTIVE */
262
263 /*
264  * @implemented
265  */
266 PACCESS_TOKEN STDCALL 
267 PsReferenceImpersonationToken(PETHREAD Thread,
268                               PULONG Unknown1,
269                               PULONG Unknown2,
270                               SECURITY_IMPERSONATION_LEVEL* Level)
271 {
272    if (Thread->ActiveImpersonationInfo == 0)
273      {
274         return(NULL);
275      }
276    
277 #ifdef LIBCAPTIVE
278    KeBugCheck(0);
279          /* NOTREACHED */
280          return NULL;
281 #else /* !LIBCAPTIVE */
282    *Level = Thread->ImpersonationInfo->Level;
283    *Unknown1 = Thread->ImpersonationInfo->Unknown1;
284    *Unknown2 = Thread->ImpersonationInfo->Unknown2;
285    ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
286                               TOKEN_ALL_ACCESS,
287                               SepTokenObjectType,
288                               KernelMode);
289    return(Thread->ImpersonationInfo->Token);
290 #endif /* LIBCAPTIVE */
291 }
292
293 #ifndef LIBCAPTIVE
294
295 VOID
296 PiBeforeBeginThread(CONTEXT c)
297 {
298    KeLowerIrql(PASSIVE_LEVEL);
299 }
300
301 VOID STDCALL
302 PiDeleteThread(PVOID ObjectBody)
303 {
304   KIRQL oldIrql;
305   PETHREAD Thread;
306   ULONG i;
307   PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
308   ULONG NotifyRoutineCount;
309
310   Thread = (PETHREAD)ObjectBody;
311
312   DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
313
314   /* Terminate Win32 thread */
315   PsTerminateWin32Thread (Thread);
316
317   ObDereferenceObject(Thread->ThreadsProcess);
318   Thread->ThreadsProcess = NULL;
319
320   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
321   for (i = 0; i < PiThreadNotifyRoutineCount; i++)
322   {
323      NotifyRoutine[i] = PiThreadNotifyRoutine[i];
324   }
325   NotifyRoutineCount = PiThreadNotifyRoutineCount;
326   PiNrThreads--;
327   RemoveEntryList(&Thread->Tcb.ThreadListEntry);
328   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
329   ExpSwapThreadEventPair(Thread, NULL); /* Release the associated eventpair object, if there was one */
330
331   for (i = 0; i < NotifyRoutineCount; i++)
332   {
333      //must be called below DISPATCH_LVL
334      NotifyRoutine[i](Thread->Cid.UniqueProcess,
335                       Thread->Cid.UniqueThread,
336                       FALSE);
337   }
338
339   KeReleaseThread(Thread);
340   DPRINT("PiDeleteThread() finished\n");
341 }
342
343 #endif /* LIBCAPTIVE */
344
345 NTSTATUS
346 PsInitializeThread(HANDLE ProcessHandle,
347                    PETHREAD* ThreadPtr,
348                    PHANDLE ThreadHandle,
349                    ACCESS_MASK  DesiredAccess,
350                    POBJECT_ATTRIBUTES ThreadAttributes,
351                    BOOLEAN First)
352 {
353    PETHREAD Thread;
354    NTSTATUS Status;
355    KIRQL oldIrql;
356    PEPROCESS Process;
357 #ifndef LIBCAPTIVE
358    ULONG i;
359    ULONG NotifyRoutineCount;
360    PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
361 #endif /* LIBCAPTIVE */
362
363    /*
364     * Reference process
365     */
366    if (ProcessHandle != NULL)
367      {
368         Status = ObReferenceObjectByHandle(ProcessHandle,
369                                            PROCESS_CREATE_THREAD,
370                                            PsProcessType,
371                                            UserMode,
372                                            (PVOID*)&Process,
373                                            NULL);
374         if (Status != STATUS_SUCCESS)
375           {
376              DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
377              return(Status);
378           }
379         DPRINT( "Creating thread in process %x\n", Process );
380      }
381    else
382      {
383         Process = PsInitialSystemProcess;
384         ObReferenceObjectByPointer(Process,
385                                    PROCESS_CREATE_THREAD,
386                                    PsProcessType,
387                                    UserMode);
388      }
389    
390    /*
391     * Create and initialize thread
392     */
393    Status = ObRosCreateObject(ThreadHandle,
394                            DesiredAccess,
395                            ThreadAttributes,
396                            PsThreadType,
397                            (PVOID*)&Thread);
398    if (!NT_SUCCESS(Status))
399      {
400         return(Status);
401      }
402
403    DPRINT("Thread = %x\n",Thread);
404    
405    PiNrThreads++;
406    
407    KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
408    Thread->ThreadsProcess = Process;
409    /*
410     * FIXME: What lock protects this?
411     */
412    InsertTailList(&Thread->ThreadsProcess->ThreadListHead, 
413                   &Thread->Tcb.ProcessThreadListEntry);
414    InitializeListHead(&Thread->TerminationPortList);
415    KeInitializeSpinLock(&Thread->ActiveTimerListLock);
416    InitializeListHead(&Thread->IrpList);
417    Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
418                                               (LONG *)&PiNextThreadUniqueId);
419    Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
420    Thread->DeadThread = 0;
421    Thread->Win32Thread = 0;
422    DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
423    
424    *ThreadPtr = Thread;
425    
426    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
427    InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
428 #ifndef LIBCAPTIVE
429    for (i = 0; i < PiThreadNotifyRoutineCount; i++)
430    {
431       NotifyRoutine[i] = PiThreadNotifyRoutine[i];
432    }
433    NotifyRoutineCount = PiThreadNotifyRoutineCount;
434 #endif /* LIBCAPTIVE */
435    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
436
437    Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
438    Thread->Tcb.Priority = Thread->Tcb.BasePriority;
439
440 #ifndef LIBCAPTIVE
441   for (i = 0; i < NotifyRoutineCount; i++)
442   {
443       //must be called below DISPATCH_LVL
444       NotifyRoutine[i](Thread->Cid.UniqueProcess,
445                                Thread->Cid.UniqueThread,
446                                TRUE);
447   }
448 #endif /* LIBCAPTIVE */
449
450   return(STATUS_SUCCESS);
451 }
452
453 #ifndef LIBCAPTIVE
454
455 static NTSTATUS
456 PsCreateTeb(HANDLE ProcessHandle,
457             PTEB *TebPtr,
458             PETHREAD Thread,
459             PUSER_STACK UserStack)
460 {
461    MEMORY_BASIC_INFORMATION Info;
462    NTSTATUS Status;
463    ULONG ByteCount;
464    ULONG RegionSize;
465    ULONG TebSize;
466    PVOID TebBase;
467    TEB Teb;
468    ULONG ResultLength;
469
470    TebBase = (PVOID)0x7FFDE000;
471    TebSize = PAGE_SIZE;
472
473    while (TRUE)
474      {
475         Status = NtQueryVirtualMemory(ProcessHandle,
476                                       TebBase,
477                                       MemoryBasicInformation,
478                                       &Info,
479                                       sizeof(Info),
480                                       &ResultLength);
481         if (!NT_SUCCESS(Status))
482           {
483              CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
484              KEBUGCHECK(0);
485           }
486         /* FIXME: Race between this and the above check */
487         if (Info.State == MEM_FREE)
488           {
489              /* The TEB must reside in user space */
490              Status = NtAllocateVirtualMemory(ProcessHandle,
491                                               &TebBase,
492                                               0,
493                                               &TebSize,
494                                               MEM_RESERVE | MEM_COMMIT,
495                                               PAGE_READWRITE);
496              if (NT_SUCCESS(Status))
497                {
498                   break;
499                }
500           }
501              
502         TebBase = TebBase - TebSize;
503      }
504
505    DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
506
507    RtlZeroMemory(&Teb, sizeof(TEB));
508    /* set all pointers to and from the TEB */
509    Teb.Tib.Self = TebBase;
510    if (Thread->ThreadsProcess)
511      {
512         Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
513      }
514    DPRINT("Teb.Peb %x\n", Teb.Peb);
515    
516    /* store stack information from UserStack */
517    if(UserStack != NULL)
518    {
519     /* fixed-size stack */
520     if(UserStack->FixedStackBase && UserStack->FixedStackLimit)
521     {
522      Teb.Tib.StackBase = UserStack->FixedStackBase;
523      Teb.Tib.StackLimit = UserStack->FixedStackLimit;
524      Teb.DeallocationStack = UserStack->FixedStackLimit;
525     }
526     /* expandable stack */
527     else
528     {
529      Teb.Tib.StackBase = UserStack->ExpandableStackBase;
530      Teb.Tib.StackLimit = UserStack->ExpandableStackLimit;
531      Teb.DeallocationStack = UserStack->ExpandableStackBottom;
532     }
533    }
534
535    /* more initialization */
536    Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
537    Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
538    Teb.CurrentLocale = PsDefaultThreadLocaleId;
539
540    /* Terminate the exception handler list */
541    Teb.Tib.ExceptionList = (PVOID)-1;
542    
543    DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
544    
545    /* write TEB data into teb page */
546    Status = NtWriteVirtualMemory(ProcessHandle,
547                                  TebBase,
548                                  &Teb,
549                                  sizeof(TEB),
550                                  &ByteCount);
551
552    if (!NT_SUCCESS(Status))
553      {
554         /* free TEB */
555         DPRINT1 ("Writing TEB failed!\n");
556
557         RegionSize = 0;
558         NtFreeVirtualMemory(ProcessHandle,
559                             TebBase,
560                             &RegionSize,
561                             MEM_RELEASE);
562
563         return Status;
564      }
565
566    if (TebPtr != NULL)
567      {
568         *TebPtr = (PTEB)TebBase;
569      }
570
571    DPRINT("TEB allocated at %p\n", TebBase);
572
573    return Status;
574 }
575
576 VOID STDCALL
577 LdrInitApcRundownRoutine(PKAPC Apc)
578 {
579    ExFreePool(Apc);
580 }
581
582 VOID STDCALL
583 LdrInitApcKernelRoutine(PKAPC Apc,
584                         PKNORMAL_ROUTINE* NormalRoutine,
585                         PVOID* NormalContext,
586                         PVOID* SystemArgument1,
587                         PVOID* SystemArgument2)
588 {
589   ExFreePool(Apc);
590 }
591
592 NTSTATUS STDCALL
593 NtCreateThread(PHANDLE ThreadHandle,
594                ACCESS_MASK DesiredAccess,
595                POBJECT_ATTRIBUTES ObjectAttributes,
596                HANDLE ProcessHandle,
597                PCLIENT_ID Client,
598                PCONTEXT ThreadContext,
599                PUSER_STACK UserStack,
600                BOOLEAN CreateSuspended)
601 {
602   PETHREAD Thread;
603   PTEB TebBase;
604   NTSTATUS Status;
605   PKAPC LdrInitApc;
606
607   DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
608          ThreadHandle,ThreadContext);
609
610   Status = PsInitializeThread(ProcessHandle,
611                               &Thread,
612                               ThreadHandle,
613                               DesiredAccess,
614                               ObjectAttributes,
615                               FALSE);
616   if (!NT_SUCCESS(Status))
617     {
618       return(Status);
619     }
620
621   Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
622   if (!NT_SUCCESS(Status))
623     {
624       return(Status);
625     }
626
627   Status = PsCreateTeb(ProcessHandle,
628                        &TebBase,
629                        Thread,
630                        UserStack);
631   if (!NT_SUCCESS(Status))
632     {
633       return(Status);
634     }
635   Thread->Tcb.Teb = TebBase;
636
637   Thread->StartAddress = NULL;
638
639   if (Client != NULL)
640     {
641       *Client = Thread->Cid;
642     }
643
644   /*
645    * Maybe send a message to the process's debugger
646    */
647   DbgkCreateThread((PVOID)ThreadContext->Eip);
648
649   /*
650    * First, force the thread to be non-alertable for user-mode alerts.
651    */
652   Thread->Tcb.Alertable = FALSE;
653
654   /*
655    * If the thread is to be created suspended then queue an APC to
656    * do the suspend before we run any userspace code.
657    */
658   if (CreateSuspended)
659     {
660       PsSuspendThread(Thread, NULL);
661     }
662
663   /*
664    * Queue an APC to the thread that will execute the ntdll startup
665    * routine.
666    */
667   LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
668   KeInitializeApc(LdrInitApc, &Thread->Tcb, OriginalApcEnvironment, LdrInitApcKernelRoutine,
669                   LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(), 
670                   UserMode, NULL);
671   KeInsertQueueApc(LdrInitApc, NULL, NULL, IO_NO_INCREMENT);
672
673   /*
674    * Start the thread running and force it to execute the APC(s) we just
675    * queued before it runs anything else in user-mode.
676    */
677   Thread->Tcb.Alertable = TRUE;
678   Thread->Tcb.Alerted[0] = 1;
679   PsUnblockThread(Thread, NULL);
680
681   return(STATUS_SUCCESS);
682 }
683
684
685 /*
686  * @implemented
687  */
688 NTSTATUS STDCALL
689 PsCreateSystemThread(PHANDLE ThreadHandle,
690                      ACCESS_MASK DesiredAccess,
691                      POBJECT_ATTRIBUTES ObjectAttributes,
692                      HANDLE ProcessHandle,
693                      PCLIENT_ID ClientId,
694                      PKSTART_ROUTINE StartRoutine,
695                      PVOID StartContext)
696 /*
697  * FUNCTION: Creates a thread which executes in kernel mode
698  * ARGUMENTS:
699  *       ThreadHandle (OUT) = Caller supplied storage for the returned thread 
700  *                            handle
701  *       DesiredAccess = Requested access to the thread
702  *       ObjectAttributes = Object attributes (optional)
703  *       ProcessHandle = Handle of process thread will run in
704  *                       NULL to use system process
705  *       ClientId (OUT) = Caller supplied storage for the returned client id
706  *                        of the thread (optional)
707  *       StartRoutine = Entry point for the thread
708  *       StartContext = Argument supplied to the thread when it begins
709  *                     execution
710  * RETURNS: Success or failure status
711  */
712 {
713    PETHREAD Thread;
714    NTSTATUS Status;
715    
716    DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
717             ThreadHandle,ProcessHandle);
718    
719    Status = PsInitializeThread(ProcessHandle,
720                                &Thread,
721                                ThreadHandle,
722                                DesiredAccess,
723                                ObjectAttributes,
724                                FALSE);
725    if (!NT_SUCCESS(Status))
726      {
727         return(Status);
728      }
729    
730    Thread->StartAddress = StartRoutine;
731    Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
732    if (!NT_SUCCESS(Status))
733      {
734         return(Status);
735      }
736
737    if (ClientId != NULL)
738      {
739         *ClientId=Thread->Cid;
740      }
741
742    PsUnblockThread(Thread, NULL);
743    
744    return(STATUS_SUCCESS);
745 }
746
747
748 /*
749  * @implemented
750  */
751 NTSTATUS STDCALL
752 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
753 {
754   KIRQL oldIrql;
755
756   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
757   if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
758   {
759     KeReleaseSpinLock(&PiThreadListLock, oldIrql);
760     return(STATUS_INSUFFICIENT_RESOURCES);
761   }
762
763   PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
764   PiThreadNotifyRoutineCount++;
765   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
766
767   return(STATUS_SUCCESS);
768 }
769
770 #endif /* LIBCAPTIVE */
771
772 /* EOF */