2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS CSR Sub System
4 * FILE: subsys/csr/csrsrv/thread.c
5 * PURPOSE: CSR Server DLL Thread Implementation
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES ******************************************************************/
16 /* DATA **********************************************************************/
18 LIST_ENTRY CsrThreadHashTable
[256];
19 SECURITY_QUALITY_OF_SERVICE CsrSecurityQos
=
21 sizeof(SECURITY_QUALITY_OF_SERVICE
),
22 SecurityImpersonation
,
23 SECURITY_STATIC_TRACKING
,
27 /* PRIVATE FUNCTIONS *********************************************************/
30 * @name CsrAllocateThread
32 * The CsrAllocateThread routine allocates a new CSR Thread object.
35 * Pointer to the CSR Process which will contain this CSR Thread.
37 * @return Pointer to the newly allocated CSR Thread.
44 CsrAllocateThread(IN PCSR_PROCESS CsrProcess
)
46 PCSR_THREAD CsrThread
;
48 /* Allocate the structure */
49 CsrThread
= RtlAllocateHeap(CsrHeap
, HEAP_ZERO_MEMORY
, sizeof(CSR_THREAD
));
50 if (!CsrThread
) return(NULL
);
52 /* Reference the Thread and Process */
53 CsrThread
->ReferenceCount
++;
54 CsrProcess
->ReferenceCount
++;
56 /* Set the Parent Process */
57 CsrThread
->Process
= CsrProcess
;
64 * @name CsrLocateThreadByClientId
66 * The CsrLocateThreadByClientId routine locates the CSR Thread and,
67 * optionally, its parent CSR Process, corresponding to a Client ID.
70 * Optional pointer to a CSR Process pointer which will contain
71 * the CSR Thread's parent.
74 * Pointer to a Client ID structure containing the Unique Thread ID
77 * @return Pointer to the CSR Thread corresponding to this CID, or NULL if
85 CsrLocateThreadByClientId(OUT PCSR_PROCESS
*Process OPTIONAL
,
86 IN PCLIENT_ID ClientId
)
89 PLIST_ENTRY ListHead
, NextEntry
;
90 PCSR_THREAD FoundThread
;
93 i
= CsrHashThread(ClientId
->UniqueThread
);
95 /* Set the list pointers */
96 ListHead
= &CsrThreadHashTable
[i
];
97 NextEntry
= ListHead
->Flink
;
100 while (NextEntry
!= ListHead
)
103 FoundThread
= CONTAINING_RECORD(NextEntry
, CSR_THREAD
, HashLinks
);
105 /* Compare the CID */
106 if (FoundThread
->ClientId
.UniqueThread
== ClientId
->UniqueThread
)
108 /* Match found, return the process */
109 *Process
= FoundThread
->Process
;
111 /* Return thread too */
116 NextEntry
= NextEntry
->Flink
;
124 * @name CsrLocateThreadInProcess
126 * The CsrLocateThreadInProcess routine locates the CSR Thread
127 * corresponding to a Client ID inside a specific CSR Process.
130 * Optional pointer to the CSR Process which contains the CSR Thread
131 * that will be looked up.
134 * Pointer to a Client ID structure containing the Unique Thread ID
137 * @return Pointer to the CSR Thread corresponding to this CID, or NULL if
140 * @remarks If the CsrProcess argument is NULL, the lookup will be done inside
146 CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL
,
149 PLIST_ENTRY ListHead
, NextEntry
;
150 PCSR_THREAD FoundThread
= NULL
;
152 /* Use the Root Process if none was specified */
153 if (!CsrProcess
) CsrProcess
= CsrRootProcess
;
155 /* Save the List pointers */
156 ListHead
= &CsrProcess
->ThreadList
;
157 NextEntry
= ListHead
->Flink
;
160 while (NextEntry
!= ListHead
)
162 /* Get Thread Entry */
163 FoundThread
= CONTAINING_RECORD(NextEntry
, CSR_THREAD
, Link
);
165 /* Check for TID Match */
166 if (FoundThread
->ClientId
.UniqueThread
== Cid
->UniqueThread
) break;
169 NextEntry
= NextEntry
->Flink
;
172 /* Return what we found */
177 * @name CsrInsertThread
179 * The CsrInsertThread routine inserts a CSR Thread into its parent's
180 * Thread List and into the Thread Hash Table.
183 * Pointer to the CSR Process containing this CSR Thread.
186 * Pointer to the CSR Thread to be inserted.
195 CsrInsertThread(IN PCSR_PROCESS Process
,
196 IN PCSR_THREAD Thread
)
200 /* Insert it into the Regular List */
201 InsertTailList(&Process
->ThreadList
, &Thread
->Link
);
203 /* Increase Thread Count */
204 Process
->ThreadCount
++;
206 /* Hash the Thread */
207 i
= CsrHashThread(Thread
->ClientId
.UniqueThread
);
209 /* Insert it there too */
210 InsertHeadList(&CsrThreadHashTable
[i
], &Thread
->HashLinks
);
214 * @name CsrDeallocateThread
216 * The CsrDeallocateThread frees the memory associated with a CSR Thread.
219 * Pointer to the CSR Thread to be freed.
223 * @remarks Do not call this routine. It is reserved for the internal
224 * thread management routines when a CSR Thread has been cleanly
225 * dereferenced and killed.
230 CsrDeallocateThread(IN PCSR_THREAD CsrThread
)
232 /* Free the process object from the heap */
233 RtlFreeHeap(CsrHeap
, 0, CsrThread
);
237 * @name CsrLockedDereferenceThread
239 * The CsrLockedDereferenceThread derefences a CSR Thread while the
240 * Process Lock is already being held.
243 * Pointer to the CSR Thread to be dereferenced.
247 * @remarks This routine will return with the Process Lock held.
252 CsrLockedDereferenceThread(PCSR_THREAD CsrThread
)
254 /* Decrease reference count */
255 if (!(--CsrThread
->ReferenceCount
))
257 /* Call the generic cleanup code */
258 CsrThreadRefcountZero(CsrThread
);
259 CsrAcquireProcessLock();
264 * @name CsrRemoveThread
266 * The CsrRemoveThread function undoes a CsrInsertThread operation and
267 * removes the CSR Thread from the the Hash Table and Thread List.
270 * Pointer to the CSR Thread to remove.
274 * @remarks If this CSR Thread is the last one inside a CSR Process, the
275 * parent will be dereferenced and the CsrProcessLastThreadTerminated
278 * After executing this routine, the CSR Thread will have the
279 * CsrThreadInTermination flag set.
284 CsrRemoveThread(IN PCSR_THREAD CsrThread
)
286 /* Remove it from the List */
287 RemoveEntryList(&CsrThread
->Link
);
289 /* Decreate the thread count of the process */
290 CsrThread
->Process
->ThreadCount
--;
292 /* Remove it from the Hash List as well */
293 if (CsrThread
->HashLinks
.Flink
) RemoveEntryList(&CsrThread
->HashLinks
);
295 /* Check if this is the last Thread */
296 if (!CsrThread
->Process
->ThreadCount
)
298 /* Check if it's not already been marked for deletion */
299 if (!(CsrThread
->Process
->Flags
& CsrProcessLastThreadTerminated
))
301 /* Let everyone know this process is about to lose the thread */
302 CsrThread
->Process
->Flags
|= CsrProcessLastThreadTerminated
;
304 /* Reference the Process */
305 CsrLockedDereferenceProcess(CsrThread
->Process
);
309 /* Mark the thread for deletion */
310 CsrThread
->Flags
|= CsrThreadInTermination
;
314 * @name CsrThreadRefcountZero
316 * The CsrThreadRefcountZero routine is executed when a CSR Thread has lost
317 * all its active references. It removes and de-allocates the CSR Thread.
320 * Pointer to the CSR Thread that is to be deleted.
324 * @remarks Do not call this routine. It is reserved for the internal
325 * thread management routines when a CSR Thread has lost all
328 * This routine is called with the Process Lock held.
333 CsrThreadRefcountZero(IN PCSR_THREAD CsrThread
)
335 PCSR_PROCESS CsrProcess
= CsrThread
->Process
;
337 /* Remove this thread */
338 CsrRemoveThread(CsrThread
);
340 /* Release the Process Lock */
341 CsrReleaseProcessLock();
343 /* Close the NT Thread Handle */
344 NtClose(CsrThread
->ThreadHandle
);
346 /* De-allocate the CSR Thread Object */
347 CsrDeallocateThread(CsrThread
);
349 /* Remove a reference from the process */
350 CsrDereferenceProcess(CsrProcess
);
353 /* PUBLIC FUNCTIONS ***********************************************************/
356 * @name CsrAddStaticServerThread
359 * The CsrAddStaticServerThread routine adds a new CSR Thread to the
360 * CSR Server Process (CsrRootProcess).
363 * Handle to an existing NT Thread to which to associate this
367 * Pointer to the Client ID structure of the NT Thread to associate
368 * with this CSR Thread.
371 * Initial CSR Thread Flags to associate to this CSR Thread. Usually
372 * CsrThreadIsServerThread.
374 * @return Pointer to the newly allocated CSR Thread.
381 CsrAddStaticServerThread(IN HANDLE hThread
,
382 IN PCLIENT_ID ClientId
,
383 IN ULONG ThreadFlags
)
385 PCSR_THREAD CsrThread
;
388 CsrAcquireProcessLock();
390 /* Allocate the Server Thread */
391 if ((CsrThread
= CsrAllocateThread(CsrRootProcess
)))
393 /* Setup the Object */
394 CsrThread
->ThreadHandle
= hThread
;
395 CsrThread
->ClientId
= *ClientId
;
396 CsrThread
->Flags
= ThreadFlags
;
398 /* Insert it into the Thread List */
399 InsertTailList(&CsrRootProcess
->ThreadList
, &CsrThread
->Link
);
401 /* Increment the thread count */
402 CsrRootProcess
->ThreadCount
++;
405 /* Release the Process Lock and return */
406 CsrReleaseProcessLock();
411 * @name CsrCreateRemoteThread
414 * The CsrCreateRemoteThread routine creates a CSR Thread object for
415 * an NT Thread which is not part of the current NT Process.
418 * Handle to an existing NT Thread to which to associate this
422 * Pointer to the Client ID structure of the NT Thread to associate
423 * with this CSR Thread.
425 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
433 CsrCreateRemoteThread(IN HANDLE hThread
,
434 IN PCLIENT_ID ClientId
)
438 PCSR_THREAD CsrThread
;
439 PCSR_PROCESS CsrProcess
;
440 KERNEL_USER_TIMES KernelTimes
;
442 DPRINT("CSRSRV: %s called\n", __FUNCTION__
);
444 /* Get the Thread Create Time */
445 Status
= NtQueryInformationThread(hThread
,
451 /* Lock the Owner Process */
452 Status
= CsrLockProcessByClientId(&ClientId
->UniqueProcess
,
455 /* Make sure the thread didn't terminate */
456 if (KernelTimes
.ExitTime
.QuadPart
)
458 /* Unlock the process and return */
459 CsrUnlockProcess(CsrProcess
);
460 return STATUS_THREAD_IS_TERMINATING
;
463 /* Allocate a CSR Thread Structure */
464 if (!(CsrThread
= CsrAllocateThread(CsrProcess
)))
466 DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__
);
467 CsrUnlockProcess(CsrProcess
);
468 return STATUS_NO_MEMORY
;
471 /* Duplicate the Thread Handle */
472 Status
= NtDuplicateObject(NtCurrentProcess(),
478 DUPLICATE_SAME_ACCESS
);
480 if (!NT_SUCCESS(Status
)) ThreadHandle
= hThread
;
482 /* Save the data we have */
483 CsrThread
->CreateTime
= KernelTimes
.CreateTime
;
484 CsrThread
->ClientId
= *ClientId
;
485 CsrThread
->ThreadHandle
= ThreadHandle
;
486 CsrThread
->Flags
= 0;
488 /* Insert the Thread into the Process */
489 CsrInsertThread(CsrProcess
, CsrThread
);
491 /* Release the lock and return */
492 CsrUnlockProcess(CsrProcess
);
493 return STATUS_SUCCESS
;
497 * @name CsrCreateThread
500 * The CsrCreateThread routine creates a CSR Thread object for an NT Thread.
503 * Pointer to the CSR Process which will contain the CSR Thread.
506 * Handle to an existing NT Thread to which to associate this
510 * Pointer to the Client ID structure of the NT Thread to associate
511 * with this CSR Thread.
513 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
521 CsrCreateThread(IN PCSR_PROCESS CsrProcess
,
523 IN PCLIENT_ID ClientId
)
526 PCSR_THREAD CsrThread
;
527 PCSR_PROCESS CurrentProcess
;
528 PCSR_THREAD CurrentThread
= NtCurrentTeb()->CsrClientThread
;
529 CLIENT_ID CurrentCid
;
530 KERNEL_USER_TIMES KernelTimes
;
532 DPRINT("CSRSRV: %s called\n", __FUNCTION__
);
534 /* Get the current thread and CID */
535 CurrentCid
= CurrentThread
->ClientId
;
537 /* Acquire the Process Lock */
538 CsrAcquireProcessLock();
540 /* Get the current Process and make sure the Thread is valid with this CID */
541 CurrentThread
= CsrLocateThreadByClientId(&CurrentProcess
,
544 /* Something is wrong if we get an empty thread back */
547 DPRINT1("CSRSRV:%s: invalid thread!\n", __FUNCTION__
);
548 CsrReleaseProcessLock();
549 return STATUS_THREAD_IS_TERMINATING
;
552 /* Get the Thread Create Time */
553 Status
= NtQueryInformationThread(hThread
,
559 /* Allocate a CSR Thread Structure */
560 if (!(CsrThread
= CsrAllocateThread(CsrProcess
)))
562 DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__
);
563 CsrReleaseProcessLock();
564 return STATUS_NO_MEMORY
;
567 /* Save the data we have */
568 CsrThread
->CreateTime
= KernelTimes
.CreateTime
;
569 CsrThread
->ClientId
= *ClientId
;
570 CsrThread
->ThreadHandle
= hThread
;
571 CsrThread
->Flags
= 0;
573 /* Insert the Thread into the Process */
574 CsrInsertThread(CsrProcess
, CsrThread
);
576 /* Release the lock and return */
577 CsrReleaseProcessLock();
578 return STATUS_SUCCESS
;
582 * @name CsrDereferenceThread
585 * The CsrDereferenceThread routine removes a reference from a CSR Thread.
588 * Pointer to the CSR Thread to dereference.
592 * @remarks If the reference count has reached zero (ie: the CSR Thread has
593 * no more active references), it will be deleted.
598 CsrDereferenceThread(PCSR_THREAD CsrThread
)
600 /* Acquire process lock */
601 CsrAcquireProcessLock();
603 /* Decrease reference count */
604 if (!(--CsrThread
->ReferenceCount
))
606 /* Call the generic cleanup code */
607 CsrThreadRefcountZero(CsrThread
);
611 /* Just release the lock */
612 CsrReleaseProcessLock();
617 * @name CsrExecServerThread
620 * The CsrExecServerThread routine creates an NT Thread and then
621 * initializes a CSR Thread for it.
623 * @param ThreadHandler
624 * Pointer to the thread's startup routine.
627 * Initial CSR Thread Flags to set to the CSR Thread.
629 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
632 * @remarks This routine is similar to CsrAddStaticServerThread, but it
633 * also creates an NT Thread instead of expecting one to already
639 CsrExecServerThread(IN PVOID ThreadHandler
,
642 PCSR_THREAD CsrThread
;
647 /* Acquire process lock */
648 CsrAcquireProcessLock();
650 /* Allocate a CSR Thread in the Root Process */
651 if (!(CsrThread
= CsrAllocateThread(CsrRootProcess
)))
654 CsrReleaseProcessLock();
655 return STATUS_NO_MEMORY
;
658 /* Create the Thread */
659 Status
= RtlCreateUserThread(NtCurrentProcess(),
669 if (!NT_SUCCESS(Status
))
672 CsrDeallocateThread(CsrThread
);
673 CsrReleaseProcessLock();
677 /* Setup the Thread Object */
678 CsrThread
->ThreadHandle
= hThread
;
679 CsrThread
->ClientId
= ClientId
;
680 CsrThread
->Flags
= Flags
;
682 /* Insert it into the Thread List */
683 InsertHeadList(&CsrRootProcess
->ThreadList
, &CsrThread
->Link
);
685 /* Increase the thread count */
686 CsrRootProcess
->ThreadCount
++;
689 CsrReleaseProcessLock();
694 * @name CsrDestroyThread
697 * The CsrDestroyThread routine destroys the CSR Thread corresponding to
701 * Pointer to the Client ID Structure corresponding to the CSR
702 * Thread which is about to be destroyed.
704 * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING
705 * if the CSR Thread is already terminating.
712 CsrDestroyThread(IN PCLIENT_ID Cid
)
714 CLIENT_ID ClientId
= *Cid
;
715 PCSR_THREAD CsrThread
;
716 PCSR_PROCESS CsrProcess
;
719 CsrAcquireProcessLock();
721 /* Find the thread */
722 CsrThread
= CsrLocateThreadByClientId(&CsrProcess
,
725 /* Make sure we got one back, and that it's not already gone */
726 if (!CsrThread
|| CsrThread
->Flags
& CsrThreadTerminated
)
728 /* Release the lock and return failure */
729 CsrReleaseProcessLock();
730 return STATUS_THREAD_IS_TERMINATING
;
733 /* Set the terminated flag */
734 CsrThread
->Flags
|= CsrThreadTerminated
;
736 /* Acquire the Wait Lock */
737 CsrAcquireWaitLock();
739 /* Do we have an active wait block? */
740 if (CsrThread
->WaitBlock
)
742 /* Notify waiters of termination */
743 CsrNotifyWaitBlock(CsrThread
->WaitBlock
,
747 CsrProcessTerminating
,
751 /* Release the Wait Lock */
752 CsrReleaseWaitLock();
754 /* Dereference the thread */
755 CsrLockedDereferenceThread(CsrThread
);
757 /* Release the Process Lock and return success */
758 CsrReleaseProcessLock();
759 return STATUS_SUCCESS
;
763 * @name CsrImpersonateClient
766 * The CsrImpersonateClient will impersonate the given CSR Thread.
769 * Pointer to the CSR Thread to impersonate.
771 * @return TRUE if impersionation suceeded, false otherwise.
773 * @remarks Impersonation can be recursive.
778 CsrImpersonateClient(IN PCSR_THREAD CsrThread
)
781 PCSR_THREAD CurrentThread
= NtCurrentTeb()->CsrClientThread
;
783 /* Use the current thread if none given */
784 if (!CsrThread
) CsrThread
= CurrentThread
;
786 /* Still no thread, something is wrong */
794 Status
= NtImpersonateThread(NtCurrentThread(),
795 CsrThread
->ThreadHandle
,
798 if (!NT_SUCCESS(Status
))
804 /* Increase the impersonation count for the current thread */
805 if (CurrentThread
) ++CurrentThread
->ImpersonationCount
;
812 * @name CsrRevertToSelf
815 * The CsrRevertToSelf routine will attempt to remove an active impersonation.
819 * @return TRUE if the reversion was succesful, false otherwise.
821 * @remarks Impersonation can be recursive; as such, the impersonation token
822 * will only be deleted once the CSR Thread's impersonaton count
828 CsrRevertToSelf(VOID
)
831 PCSR_THREAD CurrentThread
= NtCurrentTeb()->CsrClientThread
;
832 HANDLE ImpersonationToken
= NULL
;
834 /* Check if we have a Current Thread */
837 /* Make sure impersonation is on */
838 if (!CurrentThread
->ImpersonationCount
)
842 else if (--CurrentThread
->ImpersonationCount
> 0)
844 /* Success; impersonation count decreased but still not zero */
849 /* Impersonation has been totally removed, revert to ourselves */
850 Status
= NtSetInformationThread(NtCurrentThread(),
851 ThreadImpersonationToken
,
855 /* Return TRUE or FALSE */
856 return NT_SUCCESS(Status
);
860 * @name CsrLockThreadByClientId
863 * The CsrLockThreadByClientId routine locks the CSR Thread corresponding
864 * to the given Thread ID and optionally returns it.
867 * Thread ID corresponding to the CSR Thread which will be locked.
870 * Optional pointer to a CSR Thread pointer which will hold the
871 * CSR Thread corresponding to the given Thread ID.
873 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
876 * @remarks Locking a CSR Thread is defined as acquiring an extra
877 * reference to it and returning with the Process Lock held.
882 CsrLockThreadByClientId(IN HANDLE Tid
,
883 OUT PCSR_THREAD
*CsrThread OPTIONAL
)
885 PLIST_ENTRY ListHead
, NextEntry
;
886 PCSR_THREAD CurrentThread
= NULL
;
887 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
890 /* Acquire the lock */
891 CsrAcquireProcessLock();
893 /* Convert to Hash */
894 i
= CsrHashThread(Tid
);
896 /* Setup the List Pointers */
897 ListHead
= &CsrThreadHashTable
[i
];
898 NextEntry
= ListHead
;
901 while (NextEntry
!= ListHead
)
903 /* Get the Process */
904 CurrentThread
= CONTAINING_RECORD(NextEntry
, CSR_THREAD
, HashLinks
);
906 /* Check for PID Match */
907 if ((CurrentThread
->ClientId
.UniqueThread
== Tid
) &&
908 !(CurrentThread
->Flags
& CsrThreadTerminated
))
910 /* Get out of here with success */
911 Status
= STATUS_SUCCESS
;
916 NextEntry
= NextEntry
->Flink
;
919 /* Did the loop find something? */
920 if (NT_SUCCESS(Status
))
922 /* Reference the found thread */
923 CurrentThread
->ReferenceCount
++;
927 /* Nothing found, release the lock */
928 CsrReleaseProcessLock();
931 /* Return the status and thread */
932 if (CsrThread
) *CsrThread
= CurrentThread
;
937 * @name CsrReferenceThread
940 * The CsrReferenceThread routine increases the active reference count of
944 * Pointer to the CSR Thread whose reference count will be increased.
948 * @remarks Do not use this routine if the Process Lock is already held.
953 CsrReferenceThread(PCSR_THREAD CsrThread
)
955 /* Acquire process lock */
956 CsrAcquireProcessLock();
958 /* Increment reference count */
959 CsrThread
->ReferenceCount
++;
961 /* Release the lock */
962 CsrReleaseProcessLock();
966 * @name CsrUnlockThread
969 * The CsrUnlockThread undoes a previous CsrLockThreadByClientId operation.
972 * Pointer to a previously locked CSR Thread.
974 * @return STATUS_SUCCESS.
976 * @remarks This routine must be called with the Process Lock held.
981 CsrUnlockThread(PCSR_THREAD CsrThread
)
983 /* Dereference the Thread */
984 CsrLockedDereferenceThread(CsrThread
);
986 /* Release the lock and return */
987 CsrReleaseProcessLock();
988 return STATUS_SUCCESS
;