xref: /reactos/subsystems/csr/csrsrv/thredsup.c (revision d2aeaba5)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Client/Server Runtime SubSystem
4  * FILE:            subsystems/win32/csrsrv/thredsup.c
5  * PURPOSE:         CSR Server DLL Thread Management
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  *                  Alex Ionescu (alex@relsoft.net)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <srv.h>
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #define CsrHashThread(t) (HandleToUlong(t) % NUMBER_THREAD_HASH_BUCKETS)
18 
19 /* GLOBALS ********************************************************************/
20 
21 LIST_ENTRY CsrThreadHashTable[NUMBER_THREAD_HASH_BUCKETS];
22 
23 
24 /* PRIVATE FUNCTIONS **********************************************************/
25 
26 /*++
27  * @name ProtectHandle
28  * @implemented NT5.2
29  *
30  * The ProtectHandle routine protects an object handle against closure.
31  *
32  * @return TRUE or FALSE.
33  *
34  * @remarks None.
35  *
36  *--*/
37 BOOLEAN
38 NTAPI
ProtectHandle(IN HANDLE ObjectHandle)39 ProtectHandle(IN HANDLE ObjectHandle)
40 {
41     NTSTATUS Status;
42     OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo;
43 
44     /* Query current state */
45     Status = NtQueryObject(ObjectHandle,
46                            ObjectHandleFlagInformation,
47                            &HandleInfo,
48                            sizeof(HandleInfo),
49                            NULL);
50     if (NT_SUCCESS(Status))
51     {
52         /* Enable protect from close */
53         HandleInfo.ProtectFromClose = TRUE;
54         Status = NtSetInformationObject(ObjectHandle,
55                                         ObjectHandleFlagInformation,
56                                         &HandleInfo,
57                                         sizeof(HandleInfo));
58         if (NT_SUCCESS(Status)) return TRUE;
59     }
60 
61     /* We failed to or set the state */
62     return FALSE;
63 }
64 
65 /*++
66  * @name UnProtectHandle
67  * @implemented NT5.2
68  *
69  * The UnProtectHandle routine unprotects an object handle against closure.
70  *
71  * @return TRUE or FALSE.
72  *
73  * @remarks None.
74  *
75  *--*/
76 BOOLEAN
77 NTAPI
UnProtectHandle(IN HANDLE ObjectHandle)78 UnProtectHandle(IN HANDLE ObjectHandle)
79 {
80     NTSTATUS Status;
81     OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo;
82 
83     /* Query current state */
84     Status = NtQueryObject(ObjectHandle,
85                            ObjectHandleFlagInformation,
86                            &HandleInfo,
87                            sizeof(HandleInfo),
88                            NULL);
89     if (NT_SUCCESS(Status))
90     {
91         /* Disable protect from close */
92         HandleInfo.ProtectFromClose = FALSE;
93         Status = NtSetInformationObject(ObjectHandle,
94                                         ObjectHandleFlagInformation,
95                                         &HandleInfo,
96                                         sizeof(HandleInfo));
97         if (NT_SUCCESS(Status)) return TRUE;
98     }
99 
100     /* We failed to or set the state */
101     return FALSE;
102 }
103 
104 /*++
105  * @name CsrAllocateThread
106  *
107  * The CsrAllocateThread routine allocates a new CSR Thread object.
108  *
109  * @param CsrProcess
110  *        Pointer to the CSR Process which will contain this CSR Thread.
111  *
112  * @return Pointer to the newly allocated CSR Thread.
113  *
114  * @remarks None.
115  *
116  *--*/
117 PCSR_THREAD
118 NTAPI
CsrAllocateThread(IN PCSR_PROCESS CsrProcess)119 CsrAllocateThread(IN PCSR_PROCESS CsrProcess)
120 {
121     PCSR_THREAD CsrThread;
122 
123     /* Allocate the structure */
124     CsrThread = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, sizeof(CSR_THREAD));
125     if (!CsrThread) return NULL;
126 
127     /* Reference the Thread and Process */
128     CsrLockedReferenceThread(CsrThread);
129     CsrLockedReferenceProcess(CsrProcess);
130 
131     /* Set the Parent Process */
132     CsrThread->Process = CsrProcess;
133 
134     /* Return Thread */
135     return CsrThread;
136 }
137 
138 /*++
139  * @name CsrLockedReferenceThread
140  *
141  * The CsrLockedReferenceThread references a CSR Thread while the
142  * Process Lock is already being held.
143  *
144  * @param CsrThread
145  *        Pointer to the CSR Thread to be referenced.
146  *
147  * @return None.
148  *
149  * @remarks This routine will return with the Process Lock held.
150  *
151  *--*/
152 VOID
153 NTAPI
CsrLockedReferenceThread(IN PCSR_THREAD CsrThread)154 CsrLockedReferenceThread(IN PCSR_THREAD CsrThread)
155 {
156     /* Increment the reference count */
157     ++CsrThread->ReferenceCount;
158 }
159 
160 /*++
161  * @name CsrLocateThreadByClientId
162  *
163  * The CsrLocateThreadByClientId routine locates the CSR Thread and,
164  * optionally, its parent CSR Process, corresponding to a Client ID.
165  *
166  * @param Process
167  *        Optional pointer to a CSR Process pointer which will contain
168  *        the CSR Thread's parent.
169  *
170  * @param ClientId
171  *        Pointer to a Client ID structure containing the Unique Thread ID
172  *        to look up.
173  *
174  * @return Pointer to the CSR Thread corresponding to this CID, or NULL if
175  *         none was found.
176  *
177  * @remarks None.
178  *
179  *--*/
180 PCSR_THREAD
181 NTAPI
CsrLocateThreadByClientId(OUT PCSR_PROCESS * Process OPTIONAL,IN PCLIENT_ID ClientId)182 CsrLocateThreadByClientId(OUT PCSR_PROCESS *Process OPTIONAL,
183                           IN PCLIENT_ID ClientId)
184 {
185     ULONG i;
186     PLIST_ENTRY ListHead, NextEntry;
187     PCSR_THREAD FoundThread;
188     // ASSERT(ProcessStructureListLocked());
189 
190     if (Process) *Process = NULL;
191 
192     /* Hash the Thread */
193     i = CsrHashThread(ClientId->UniqueThread);
194 
195     /* Set the list pointers */
196     ListHead = &CsrThreadHashTable[i];
197     NextEntry = ListHead->Flink;
198 
199     /* Star the loop */
200     while (NextEntry != ListHead)
201     {
202         /* Get the thread */
203         FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, HashLinks);
204 
205         /* Move to the next entry */
206         NextEntry = NextEntry->Flink;
207 
208         /* Compare the CID */
209         // if (*(PULONGLONG)&FoundThread->ClientId == *(PULONGLONG)ClientId)
210         if ( FoundThread->ClientId.UniqueProcess == ClientId->UniqueProcess &&
211              FoundThread->ClientId.UniqueThread  == ClientId->UniqueThread )
212         {
213             /* Match found, return the process */
214             if (Process) *Process = FoundThread->Process;
215 
216             /* Return thread too */
217             return FoundThread;
218         }
219     }
220 
221     /* Nothing found */
222     return NULL;
223 }
224 
225 /*++
226  * @name CsrLocateThreadInProcess
227  *
228  * The CsrLocateThreadInProcess routine locates the CSR Thread
229  * corresponding to a Client ID inside a specific CSR Process.
230  *
231  * @param Process
232  *        Optional pointer to the CSR Process which contains the CSR Thread
233  *        that will be looked up.
234  *
235  * @param ClientId
236  *        Pointer to a Client ID structure containing the Unique Thread ID
237  *        to look up.
238  *
239  * @return Pointer to the CSR Thread corresponding to this CID, or NULL if
240  *         none was found.
241  *
242  * @remarks If the CsrProcess argument is NULL, the lookup will be done inside
243  *          CsrRootProcess.
244  *
245  *--*/
246 PCSR_THREAD
247 NTAPI
CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL,IN PCLIENT_ID Cid)248 CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL,
249                          IN PCLIENT_ID Cid)
250 {
251     PLIST_ENTRY ListHead, NextEntry;
252     PCSR_THREAD FoundThread = NULL;
253 
254     /* Use the Root Process if none was specified */
255     if (!CsrProcess) CsrProcess = CsrRootProcess;
256 
257     /* Save the List pointers */
258     ListHead = &CsrProcess->ThreadList;
259     NextEntry = ListHead->Flink;
260 
261     /* Start the Loop */
262     while (NextEntry != ListHead)
263     {
264         /* Get Thread Entry */
265         FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
266 
267         /* Check for TID Match */
268         if (FoundThread->ClientId.UniqueThread == Cid->UniqueThread) break;
269 
270         /* Move to the next entry */
271         NextEntry = NextEntry->Flink;
272     }
273 
274     /* Return what we found */
275     return FoundThread;
276 }
277 
278 /*++
279  * @name CsrInsertThread
280  *
281  * The CsrInsertThread routine inserts a CSR Thread into its parent's
282  * Thread List and into the Thread Hash Table.
283  *
284  * @param Process
285  *        Pointer to the CSR Process containing this CSR Thread.
286  *
287  * @param Thread
288  *        Pointer to the CSR Thread to be inserted.
289  *
290  * @return None.
291  *
292  * @remarks None.
293  *
294  *--*/
295 NTSTATUS
296 NTAPI
CsrInsertThread(IN PCSR_PROCESS Process,IN PCSR_THREAD Thread)297 CsrInsertThread(IN PCSR_PROCESS Process,
298                 IN PCSR_THREAD Thread)
299 {
300     ULONG i;
301     NTSTATUS Status;
302     ULONG ThreadInfo;
303     // ASSERT(ProcessStructureListLocked());
304 
305     /* Make sure the thread isn't already dead by the time we got this */
306     Status = NtQueryInformationThread(Thread->ThreadHandle,
307                                       ThreadIsTerminated,
308                                       &ThreadInfo,
309                                       sizeof(ThreadInfo),
310                                       NULL);
311     if (!NT_SUCCESS(Status)) return Status;
312     if (ThreadInfo) return STATUS_THREAD_IS_TERMINATING;
313 
314     /* Insert it into the Regular List */
315     InsertTailList(&Process->ThreadList, &Thread->Link);
316 
317     /* Increase Thread Count */
318     Process->ThreadCount++;
319 
320     /* Hash the Thread */
321     i = CsrHashThread(Thread->ClientId.UniqueThread);
322 
323     /* Insert it there too */
324     InsertHeadList(&CsrThreadHashTable[i], &Thread->HashLinks);
325     return STATUS_SUCCESS;
326 }
327 
328 /*++
329  * @name CsrDeallocateThread
330  *
331  * The CsrDeallocateThread frees the memory associated with a CSR Thread.
332  *
333  * @param CsrThread
334  *        Pointer to the CSR Thread to be freed.
335  *
336  * @return None.
337  *
338  * @remarks Do not call this routine. It is reserved for the internal
339  *          thread management routines when a CSR Thread has been cleanly
340  *          dereferenced and killed.
341  *
342  *--*/
343 VOID
344 NTAPI
CsrDeallocateThread(IN PCSR_THREAD CsrThread)345 CsrDeallocateThread(IN PCSR_THREAD CsrThread)
346 {
347     /* Free the process object from the heap */
348     // ASSERT(CsrThread->WaitBlock == NULL);
349     RtlFreeHeap(CsrHeap, 0, CsrThread);
350 }
351 
352 /*++
353  * @name CsrRemoveThread
354  *
355  * The CsrRemoveThread function undoes a CsrInsertThread operation and
356  * removes the CSR Thread from the the Hash Table and Thread List.
357  *
358  * @param CsrThread
359  *        Pointer to the CSR Thread to remove.
360  *
361  * @return None.
362  *
363  * @remarks If this CSR Thread is the last one inside a CSR Process, the
364  *          parent will be dereferenced and the CsrProcessLastThreadTerminated
365  *          flag will be set.
366  *
367  *          After executing this routine, the CSR Thread will have the
368  *          CsrThreadInTermination flag set.
369  *
370  *--*/
371 VOID
372 NTAPI
CsrRemoveThread(IN PCSR_THREAD CsrThread)373 CsrRemoveThread(IN PCSR_THREAD CsrThread)
374 {
375     ASSERT(ProcessStructureListLocked());
376 
377     /* Remove it from the List */
378     RemoveEntryList(&CsrThread->Link);
379 
380     /* Decreate the thread count of the process */
381     CsrThread->Process->ThreadCount--;
382 
383     /* Remove it from the Hash List as well */
384     if (CsrThread->HashLinks.Flink) RemoveEntryList(&CsrThread->HashLinks);
385 
386     /* Check if this is the last Thread */
387     if (CsrThread->Process->ThreadCount == 0)
388     {
389         /* Check if it's not already been marked for deletion */
390         if ((CsrThread->Process->Flags & CsrProcessLastThreadTerminated) == 0)
391         {
392             /* Let everyone know this process is about to lose the thread */
393             CsrThread->Process->Flags |= CsrProcessLastThreadTerminated;
394 
395             /* Reference the Process */
396             CsrLockedDereferenceProcess(CsrThread->Process);
397         }
398     }
399 
400     /* Mark the thread for deletion */
401     CsrThread->Flags |= CsrThreadInTermination;
402 }
403 
404 /*++
405  * @name CsrThreadRefcountZero
406  *
407  * The CsrThreadRefcountZero routine is executed when a CSR Thread has lost
408  * all its active references. It removes and de-allocates the CSR Thread.
409  *
410  * @param CsrThread
411  *        Pointer to the CSR Thread that is to be deleted.
412  *
413  * @return None.
414  *
415  * @remarks Do not call this routine. It is reserved for the internal
416  *          thread management routines when a CSR Thread has lost all
417  *          its references.
418  *
419  *          This routine is called with the Process Lock held.
420  *
421  *--*/
422 VOID
423 NTAPI
CsrThreadRefcountZero(IN PCSR_THREAD CsrThread)424 CsrThreadRefcountZero(IN PCSR_THREAD CsrThread)
425 {
426     PCSR_PROCESS CsrProcess = CsrThread->Process;
427     NTSTATUS Status;
428     ASSERT(ProcessStructureListLocked());
429 
430     /* Remove this thread */
431     CsrRemoveThread(CsrThread);
432 
433     /* Release the Process Lock */
434     CsrReleaseProcessLock();
435 
436     /* Close the NT Thread Handle */
437     if (CsrThread->ThreadHandle)
438     {
439         UnProtectHandle(CsrThread->ThreadHandle);
440         Status = NtClose(CsrThread->ThreadHandle);
441         ASSERT(NT_SUCCESS(Status));
442     }
443 
444     /* De-allocate the CSR Thread Object */
445     CsrDeallocateThread(CsrThread);
446 
447     /* Remove a reference from the process */
448     CsrDereferenceProcess(CsrProcess);
449 }
450 
451 /*++
452  * @name CsrLockedDereferenceThread
453  *
454  * The CsrLockedDereferenceThread dereferences a CSR Thread while the
455  * Process Lock is already being held.
456  *
457  * @param CsrThread
458  *        Pointer to the CSR Thread to be dereferenced.
459  *
460  * @return None.
461  *
462  * @remarks This routine will return with the Process Lock held.
463  *
464  *--*/
465 VOID
466 NTAPI
CsrLockedDereferenceThread(IN PCSR_THREAD CsrThread)467 CsrLockedDereferenceThread(IN PCSR_THREAD CsrThread)
468 {
469     LONG LockCount;
470 
471     /* Decrease reference count */
472     LockCount = --CsrThread->ReferenceCount;
473     ASSERT(LockCount >= 0);
474     if (LockCount == 0)
475     {
476         /* Call the generic cleanup code */
477         CsrThreadRefcountZero(CsrThread);
478         /* Acquire the lock again, it was released by CsrThreadRefcountZero */
479         CsrAcquireProcessLock();
480     }
481 }
482 
483 
484 /* PUBLIC FUNCTIONS ***********************************************************/
485 
486 /*++
487  * @name CsrAddStaticServerThread
488  * @implemented NT4
489  *
490  * The CsrAddStaticServerThread routine adds a new CSR Thread to the
491  * CSR Server Process (CsrRootProcess).
492  *
493  * @param hThread
494  *        Handle to an existing NT Thread to which to associate this
495  *        CSR Thread.
496  *
497  * @param ClientId
498  *        Pointer to the Client ID structure of the NT Thread to associate
499  *        with this CSR Thread.
500  *
501  * @param ThreadFlags
502  *        Initial CSR Thread Flags to associate to this CSR Thread. Usually
503  *        CsrThreadIsServerThread.
504  *
505  * @return Pointer to the newly allocated CSR Thread.
506  *
507  * @remarks None.
508  *
509  *--*/
510 PCSR_THREAD
511 NTAPI
CsrAddStaticServerThread(IN HANDLE hThread,IN PCLIENT_ID ClientId,IN ULONG ThreadFlags)512 CsrAddStaticServerThread(IN HANDLE hThread,
513                          IN PCLIENT_ID ClientId,
514                          IN ULONG ThreadFlags)
515 {
516     PCSR_THREAD CsrThread;
517 
518     /* Get the Lock */
519     CsrAcquireProcessLock();
520 
521     /* Allocate the Server Thread */
522     CsrThread = CsrAllocateThread(CsrRootProcess);
523     if (CsrThread)
524     {
525         /* Setup the Object */
526         CsrThread->ThreadHandle = hThread;
527         ProtectHandle(hThread);
528         CsrThread->ClientId = *ClientId;
529         CsrThread->Flags = ThreadFlags;
530 
531         /* Insert it into the Thread List */
532         InsertTailList(&CsrRootProcess->ThreadList, &CsrThread->Link);
533 
534         /* Increment the thread count */
535         CsrRootProcess->ThreadCount++;
536     }
537     else
538     {
539         DPRINT1("CsrAddStaticServerThread: alloc failed for thread 0x%x\n", hThread);
540     }
541 
542     /* Release the Process Lock and return */
543     CsrReleaseProcessLock();
544     return CsrThread;
545 }
546 
547 /*++
548  * @name CsrCreateRemoteThread
549  * @implemented NT4
550  *
551  * The CsrCreateRemoteThread routine creates a CSR Thread object for
552  * an NT Thread which is not part of the current NT Process.
553  *
554  * @param hThread
555  *        Handle to an existing NT Thread to which to associate this
556  *        CSR Thread.
557  *
558  * @param ClientId
559  *        Pointer to the Client ID structure of the NT Thread to associate
560  *        with this CSR Thread.
561  *
562  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
563  *
564  * @remarks None.
565  *
566  *--*/
567 NTSTATUS
568 NTAPI
CsrCreateRemoteThread(IN HANDLE hThread,IN PCLIENT_ID ClientId)569 CsrCreateRemoteThread(IN HANDLE hThread,
570                       IN PCLIENT_ID ClientId)
571 {
572     NTSTATUS Status;
573     HANDLE ThreadHandle;
574     PCSR_THREAD CsrThread;
575     PCSR_PROCESS CsrProcess;
576     KERNEL_USER_TIMES KernelTimes;
577 
578     /* Get the Thread Create Time */
579     Status = NtQueryInformationThread(hThread,
580                                       ThreadTimes,
581                                       &KernelTimes,
582                                       sizeof(KernelTimes),
583                                       NULL);
584     if (!NT_SUCCESS(Status))
585     {
586         DPRINT1("Failed to query thread times: %lx\n", Status);
587         return Status;
588     }
589 
590     /* Lock the Owner Process */
591     Status = CsrLockProcessByClientId(ClientId->UniqueProcess, &CsrProcess);
592     if (!NT_SUCCESS(Status))
593     {
594         DPRINT1("No known process for %lx\n", ClientId->UniqueProcess);
595         return Status;
596     }
597 
598     /* Make sure the thread didn't terminate */
599     if (KernelTimes.ExitTime.QuadPart)
600     {
601         /* Unlock the process and return */
602         CsrUnlockProcess(CsrProcess);
603         DPRINT1("Dead thread: %I64x\n", KernelTimes.ExitTime.QuadPart);
604         return STATUS_THREAD_IS_TERMINATING;
605     }
606 
607     /* Allocate a CSR Thread Structure */
608     CsrThread = CsrAllocateThread(CsrProcess);
609     if (!CsrThread)
610     {
611         DPRINT1("CSRSRV: %s: out of memory!\n", __FUNCTION__);
612         CsrUnlockProcess(CsrProcess);
613         return STATUS_NO_MEMORY;
614     }
615 
616     /* Duplicate the Thread Handle */
617     Status = NtDuplicateObject(NtCurrentProcess(),
618                                hThread,
619                                NtCurrentProcess(),
620                                &ThreadHandle,
621                                0,
622                                0,
623                                DUPLICATE_SAME_ACCESS);
624     /* Allow failure */
625     if (!NT_SUCCESS(Status))
626     {
627         DPRINT1("Thread duplication failed: %lx\n", Status);
628         ThreadHandle = hThread;
629     }
630 
631     /* Save the data we have */
632     CsrThread->CreateTime = KernelTimes.CreateTime;
633     CsrThread->ClientId = *ClientId;
634     CsrThread->ThreadHandle = ThreadHandle;
635     ProtectHandle(ThreadHandle);
636     CsrThread->Flags = 0;
637 
638     /* Insert the Thread into the Process */
639     Status = CsrInsertThread(CsrProcess, CsrThread);
640     if (!NT_SUCCESS(Status))
641     {
642         /* Bail out */
643         if (CsrThread->ThreadHandle != hThread) NtClose(CsrThread->ThreadHandle);
644         CsrUnlockProcess(CsrProcess);
645         CsrDeallocateThread(CsrThread);
646         return Status;
647     }
648 
649     /* Release the lock and return */
650     CsrUnlockProcess(CsrProcess);
651     return STATUS_SUCCESS;
652 }
653 
654 /*++
655  * @name CsrCreateThread
656  * @implemented NT4
657  *
658  * The CsrCreateThread routine creates a CSR Thread object for an NT Thread.
659  *
660  * @param CsrProcess
661  *        Pointer to the CSR Process which will contain the CSR Thread.
662  *
663  * @param hThread
664  *        Handle to an existing NT Thread to which to associate this
665  *        CSR Thread.
666  *
667  * @param ClientId
668  *        Pointer to the Client ID structure of the NT Thread to associate
669  *        with this CSR Thread.
670  *
671  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
672  *
673  * @remarks None.
674  *
675  *--*/
676 NTSTATUS
677 NTAPI
CsrCreateThread(IN PCSR_PROCESS CsrProcess,IN HANDLE hThread,IN PCLIENT_ID ClientId,IN BOOLEAN HaveClient)678 CsrCreateThread(IN PCSR_PROCESS CsrProcess,
679                 IN HANDLE hThread,
680                 IN PCLIENT_ID ClientId,
681                 IN BOOLEAN HaveClient)
682 {
683     NTSTATUS Status;
684     PCSR_THREAD CsrThread, CurrentThread;
685     PCSR_PROCESS CurrentProcess;
686     CLIENT_ID CurrentCid;
687     KERNEL_USER_TIMES KernelTimes;
688 
689     if (HaveClient)
690     {
691         /* Get the current thread and CID */
692         CurrentThread = CsrGetClientThread();
693         CurrentCid = CurrentThread->ClientId;
694 
695         /* Acquire the Process Lock */
696         CsrAcquireProcessLock();
697 
698         /* Get the current Process and make sure the Thread is valid with this CID */
699         CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, &CurrentCid);
700 
701         /* Something is wrong if we get an empty thread back */
702         if (!CurrentThread)
703         {
704             DPRINT1("CSRSRV: %s: invalid thread!\n", __FUNCTION__);
705             CsrReleaseProcessLock();
706             return STATUS_THREAD_IS_TERMINATING;
707         }
708     }
709     else
710     {
711         /* Acquire the Process Lock */
712         CsrAcquireProcessLock();
713     }
714 
715     /* Get the Thread Create Time */
716     Status = NtQueryInformationThread(hThread,
717                                       ThreadTimes,
718                                       &KernelTimes,
719                                       sizeof(KernelTimes),
720                                       NULL);
721     if (!NT_SUCCESS(Status))
722     {
723         CsrReleaseProcessLock();
724         return Status;
725     }
726 
727     /* Allocate a CSR Thread Structure */
728     CsrThread = CsrAllocateThread(CsrProcess);
729     if (!CsrThread)
730     {
731         DPRINT1("CSRSRV: %s: out of memory!\n", __FUNCTION__);
732         CsrReleaseProcessLock();
733         return STATUS_NO_MEMORY;
734     }
735 
736     /* Save the data we have */
737     CsrThread->CreateTime = KernelTimes.CreateTime;
738     CsrThread->ClientId = *ClientId;
739     CsrThread->ThreadHandle = hThread;
740     ProtectHandle(hThread);
741     CsrThread->Flags = 0;
742 
743     /* Insert the Thread into the Process */
744     Status = CsrInsertThread(CsrProcess, CsrThread);
745     if (!NT_SUCCESS(Status))
746     {
747         /* Bail out */
748         CsrUnlockProcess(CsrProcess);
749         CsrDeallocateThread(CsrThread);
750         return Status;
751     }
752 
753     /* Release the lock and return */
754     CsrReleaseProcessLock();
755 
756     return STATUS_SUCCESS;
757 }
758 
759 /*++
760  * @name CsrDereferenceThread
761  * @implemented NT4
762  *
763  * The CsrDereferenceThread routine removes a reference from a CSR Thread.
764  *
765  * @param CsrThread
766  *        Pointer to the CSR Thread to dereference.
767  *
768  * @return None.
769  *
770  * @remarks If the reference count has reached zero (ie: the CSR Thread has
771  *          no more active references), it will be deleted.
772  *
773  *--*/
774 VOID
775 NTAPI
CsrDereferenceThread(IN PCSR_THREAD CsrThread)776 CsrDereferenceThread(IN PCSR_THREAD CsrThread)
777 {
778     /* Acquire process lock */
779     CsrAcquireProcessLock();
780 
781     /* Decrease reference count */
782     ASSERT(CsrThread->ReferenceCount > 0);
783     if ((--CsrThread->ReferenceCount) == 0)
784     {
785         /* Call the generic cleanup code */
786         CsrThreadRefcountZero(CsrThread);
787     }
788     else
789     {
790         /* Just release the lock */
791         CsrReleaseProcessLock();
792     }
793 }
794 
795 /*++
796  * @name CsrDestroyThread
797  * @implemented NT4
798  *
799  * The CsrDestroyThread routine destroys the CSR Thread corresponding to
800  * a given Thread ID.
801  *
802  * @param Cid
803  *        Pointer to the Client ID Structure corresponding to the CSR
804  *        Thread which is about to be destroyed.
805  *
806  * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING
807  *         if the CSR Thread is already terminating.
808  *
809  * @remarks None.
810  *
811  *--*/
812 NTSTATUS
813 NTAPI
CsrDestroyThread(IN PCLIENT_ID Cid)814 CsrDestroyThread(IN PCLIENT_ID Cid)
815 {
816     CLIENT_ID ClientId = *Cid;
817     PCSR_THREAD CsrThread;
818     PCSR_PROCESS CsrProcess;
819 
820     /* Acquire lock */
821     CsrAcquireProcessLock();
822 
823     /* Find the thread */
824     CsrThread = CsrLocateThreadByClientId(&CsrProcess,
825                                           &ClientId);
826 
827     /* Make sure we got one back, and that it's not already gone */
828     if (!CsrThread || (CsrThread->Flags & CsrThreadTerminated))
829     {
830         /* Release the lock and return failure */
831         CsrReleaseProcessLock();
832         return STATUS_THREAD_IS_TERMINATING;
833     }
834 
835     /* Set the terminated flag */
836     CsrThread->Flags |= CsrThreadTerminated;
837 
838     /* Acquire the Wait Lock */
839     CsrAcquireWaitLock();
840 
841     /* Do we have an active wait block? */
842     if (CsrThread->WaitBlock)
843     {
844         /* Notify waiters of termination */
845         CsrNotifyWaitBlock(CsrThread->WaitBlock,
846                            NULL,
847                            NULL,
848                            NULL,
849                            CsrProcessTerminating,
850                            TRUE);
851     }
852 
853     /* Release the Wait Lock */
854     CsrReleaseWaitLock();
855 
856     /* Dereference the thread */
857     CsrLockedDereferenceThread(CsrThread);
858 
859     /* Release the Process Lock and return success */
860     CsrReleaseProcessLock();
861     return STATUS_SUCCESS;
862 }
863 
864 /*++
865  * @name CsrExecServerThread
866  * @implemented NT4
867  *
868  * The CsrExecServerThread routine creates an NT Thread and then
869  * initializes a CSR Thread for it.
870  *
871  * @param ThreadHandler
872  *        Pointer to the thread's startup routine.
873  *
874  * @param Flags
875  *        Initial CSR Thread Flags to set to the CSR Thread.
876  *
877  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
878  *
879  * @remarks This routine is similar to CsrAddStaticServerThread, but it
880  *          also creates an NT Thread instead of expecting one to already
881  *          exist.
882  *
883  *--*/
884 NTSTATUS
885 NTAPI
CsrExecServerThread(IN PVOID ThreadHandler,IN ULONG Flags)886 CsrExecServerThread(IN PVOID ThreadHandler,
887                     IN ULONG Flags)
888 {
889     PCSR_THREAD CsrThread;
890     HANDLE hThread;
891     CLIENT_ID ClientId;
892     NTSTATUS Status;
893 
894     /* Acquire process lock */
895     CsrAcquireProcessLock();
896 
897     /* Allocate a CSR Thread in the Root Process */
898     ASSERT(CsrRootProcess != NULL);
899     CsrThread = CsrAllocateThread(CsrRootProcess);
900     if (!CsrThread)
901     {
902         /* Fail */
903         CsrReleaseProcessLock();
904         return STATUS_NO_MEMORY;
905     }
906 
907     /* Create the Thread */
908     Status = RtlCreateUserThread(NtCurrentProcess(),
909                                  NULL,
910                                  FALSE,
911                                  0,
912                                  0,
913                                  0,
914                                  ThreadHandler,
915                                  NULL,
916                                  &hThread,
917                                  &ClientId);
918     if (!NT_SUCCESS(Status))
919     {
920         /* Fail */
921         CsrDeallocateThread(CsrThread);
922         CsrReleaseProcessLock();
923         return Status;
924     }
925 
926     /* Setup the Thread Object */
927     CsrThread->ThreadHandle = hThread;
928     ProtectHandle(hThread);
929     CsrThread->ClientId = ClientId;
930     CsrThread->Flags = Flags;
931 
932     /* Insert it into the Thread List */
933     InsertHeadList(&CsrRootProcess->ThreadList, &CsrThread->Link);
934 
935     /* Increase the thread count */
936     CsrRootProcess->ThreadCount++;
937 
938     /* Return */
939     CsrReleaseProcessLock();
940     return Status;
941 }
942 
943 /*++
944  * @name CsrLockThreadByClientId
945  * @implemented NT4
946  *
947  * The CsrLockThreadByClientId routine locks the CSR Thread corresponding
948  * to the given Thread ID and optionally returns it.
949  *
950  * @param Tid
951  *        Thread ID corresponding to the CSR Thread which will be locked.
952  *
953  * @param CsrThread
954  *        Optional pointer to a CSR Thread pointer which will hold the
955  *        CSR Thread corresponding to the given Thread ID.
956  *
957  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
958  *
959  * @remarks Locking a CSR Thread is defined as acquiring an extra
960  *          reference to it and returning with the Process Lock held.
961  *
962  *--*/
963 NTSTATUS
964 NTAPI
CsrLockThreadByClientId(IN HANDLE Tid,OUT PCSR_THREAD * CsrThread)965 CsrLockThreadByClientId(IN HANDLE Tid,
966                         OUT PCSR_THREAD *CsrThread)
967 {
968     PLIST_ENTRY NextEntry;
969     PCSR_THREAD CurrentThread = NULL;
970     NTSTATUS Status = STATUS_UNSUCCESSFUL;
971     ULONG i;
972 
973     /* Acquire the lock */
974     CsrAcquireProcessLock();
975 
976     /* Assume failure */
977     ASSERT(CsrThread != NULL);
978     *CsrThread = NULL;
979 
980     /* Convert to Hash */
981     i = CsrHashThread(Tid);
982 
983     /* Setup the List Pointers */
984     NextEntry = CsrThreadHashTable[i].Flink;
985 
986     /* Start Loop */
987     while (NextEntry != &CsrThreadHashTable[i])
988     {
989         /* Get the Thread */
990         CurrentThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, HashLinks);
991 
992         /* Check for TID Match */
993         if ((CurrentThread->ClientId.UniqueThread == Tid) &&
994             (CurrentThread->Flags & CsrThreadTerminated) == 0)
995         {
996             /* Get out of here */
997             break;
998         }
999 
1000         /* Move to the next entry */
1001         NextEntry = NextEntry->Flink;
1002     }
1003 
1004     /* Nothing found if we got back to the list */
1005     if (NextEntry == &CsrThreadHashTable[i]) CurrentThread = NULL;
1006 
1007     /* Did the loop find something? */
1008     if (CurrentThread)
1009     {
1010         /* Reference the found thread */
1011         Status = STATUS_SUCCESS;
1012         CsrLockedReferenceThread(CurrentThread);
1013         *CsrThread = CurrentThread;
1014     }
1015     else
1016     {
1017         /* Nothing found, release the lock */
1018         Status = STATUS_UNSUCCESSFUL;
1019         CsrReleaseProcessLock();
1020     }
1021 
1022     /* Return the status */
1023     return Status;
1024 }
1025 
1026 /*++
1027  * @name CsrReferenceThread
1028  * @implemented NT4
1029  *
1030  * The CsrReferenceThread routine increases the active reference count of
1031  * a CSR Thread.
1032  *
1033  * @param CsrThread
1034  *        Pointer to the CSR Thread whose reference count will be increased.
1035  *
1036  * @return None.
1037  *
1038  * @remarks Do not use this routine if the Process Lock is already held.
1039  *
1040  *--*/
1041 VOID
1042 NTAPI
CsrReferenceThread(IN PCSR_THREAD CsrThread)1043 CsrReferenceThread(IN PCSR_THREAD CsrThread)
1044 {
1045     /* Acquire process lock */
1046     CsrAcquireProcessLock();
1047 
1048     /* Sanity checks */
1049     ASSERT((CsrThread->Flags & CsrThreadTerminated) == 0);
1050     ASSERT(CsrThread->ReferenceCount != 0);
1051 
1052     /* Increment reference count */
1053     CsrThread->ReferenceCount++;
1054 
1055     /* Release the lock */
1056     CsrReleaseProcessLock();
1057 }
1058 
1059 /*++
1060  * @name CsrUnlockThread
1061  * @implemented NT4
1062  *
1063  * The CsrUnlockThread undoes a previous CsrLockThreadByClientId operation.
1064  *
1065  * @param CsrThread
1066  *        Pointer to a previously locked CSR Thread.
1067  *
1068  * @return STATUS_SUCCESS.
1069  *
1070  * @remarks This routine must be called with the Process Lock held.
1071  *
1072  *--*/
1073 NTSTATUS
1074 NTAPI
CsrUnlockThread(IN PCSR_THREAD CsrThread)1075 CsrUnlockThread(IN PCSR_THREAD CsrThread)
1076 {
1077     /* Dereference the Thread */
1078     ASSERT(ProcessStructureListLocked());
1079     CsrLockedDereferenceThread(CsrThread);
1080 
1081     /* Release the lock and return */
1082     CsrReleaseProcessLock();
1083     return STATUS_SUCCESS;
1084 }
1085 
1086 /* EOF */
1087