2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS CSR Sub System
4 * FILE: subsystems/win32/csrss/csrsrv/thredsup.c
5 * PURPOSE: CSR Server DLL Thread Implementation
6 * PROGRAMMERS: ReactOS Portable Systems Group
10 /* INCLUDES *******************************************************************/
17 #define CsrHashThread(t) \
18 (HandleToUlong(t)&(256 - 1))
20 /* GLOBALS ********************************************************************/
22 LIST_ENTRY CsrThreadHashTable
[256];
24 /* FUNCTIONS ******************************************************************/
30 * The ProtectHandle routine protects an object handle against closure.
32 * @return TRUE or FALSE.
39 ProtectHandle(IN HANDLE ObjectHandle
)
42 OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo
;
44 /* Query current state */
45 Status
= NtQueryObject(ObjectHandle
,
46 ObjectHandleFlagInformation
,
50 if (NT_SUCCESS(Status
))
52 /* Enable protect from close */
53 HandleInfo
.ProtectFromClose
= TRUE
;
54 Status
= NtSetInformationObject(ObjectHandle
,
55 ObjectHandleFlagInformation
,
58 if (NT_SUCCESS(Status
)) return TRUE
;
61 /* We failed to or set the state */
66 * @name UnProtectHandle
69 * The UnProtectHandle routine unprotects an object handle against closure.
71 * @return TRUE or FALSE.
78 UnProtectHandle(IN HANDLE ObjectHandle
)
81 OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo
;
83 /* Query current state */
84 Status
= NtQueryObject(ObjectHandle
,
85 ObjectHandleFlagInformation
,
89 if (NT_SUCCESS(Status
))
91 /* Disable protect from close */
92 HandleInfo
.ProtectFromClose
= FALSE
;
93 Status
= NtSetInformationObject(ObjectHandle
,
94 ObjectHandleFlagInformation
,
97 if (NT_SUCCESS(Status
)) return TRUE
;
100 /* We failed to or set the state */
105 * @name CsrAllocateThread
107 * The CsrAllocateThread routine allocates a new CSR Thread object.
110 * Pointer to the CSR Process which will contain this CSR Thread.
112 * @return Pointer to the newly allocated CSR Thread.
119 CsrAllocateThread(IN PCSR_PROCESS CsrProcess
)
121 PCSR_THREAD CsrThread
;
123 /* Allocate the structure */
124 CsrThread
= RtlAllocateHeap(CsrHeap
, HEAP_ZERO_MEMORY
, sizeof(CSR_THREAD
));
125 if (!CsrThread
) return(NULL
);
127 /* Reference the Thread and Process */
128 CsrThread
->ReferenceCount
++;
129 CsrProcess
->ReferenceCount
++;
131 /* Set the Parent Process */
132 CsrThread
->Process
= CsrProcess
;
139 * @name CsrLockedReferenceThread
141 * The CsrLockedReferenceThread references a CSR Thread while the
142 * Process Lock is already being held.
145 * Pointer to the CSR Thread to be referenced.
149 * @remarks This routine will return with the Process Lock held.
154 CsrLockedReferenceThread(IN PCSR_THREAD CsrThread
)
156 /* Increment the reference count */
157 ++CsrThread
->ReferenceCount
;
161 * @name CsrLocateThreadByClientId
163 * The CsrLocateThreadByClientId routine locates the CSR Thread and,
164 * optionally, its parent CSR Process, corresponding to a Client ID.
167 * Optional pointer to a CSR Process pointer which will contain
168 * the CSR Thread's parent.
171 * Pointer to a Client ID structure containing the Unique Thread ID
174 * @return Pointer to the CSR Thread corresponding to this CID, or NULL if
182 CsrLocateThreadByClientId(OUT PCSR_PROCESS
*Process OPTIONAL
,
183 IN PCLIENT_ID ClientId
)
186 PLIST_ENTRY ListHead
, NextEntry
;
187 PCSR_THREAD FoundThread
;
188 // ASSERT(ProcessStructureListLocked());
190 /* Hash the Thread */
191 i
= CsrHashThread(ClientId
->UniqueThread
);
193 /* Set the list pointers */
194 ListHead
= &CsrThreadHashTable
[i
];
195 NextEntry
= ListHead
->Flink
;
198 while (NextEntry
!= ListHead
)
201 FoundThread
= CONTAINING_RECORD(NextEntry
, CSR_THREAD
, HashLinks
);
203 /* Compare the CID */
204 if (FoundThread
->ClientId
.UniqueThread
== ClientId
->UniqueThread
)
206 /* Match found, return the process */
207 *Process
= FoundThread
->Process
;
209 /* Return thread too */
210 // DPRINT1("Found: %p %p\n", FoundThread, FoundThread->Process);
215 NextEntry
= NextEntry
->Flink
;
223 * @name CsrLocateThreadInProcess
225 * The CsrLocateThreadInProcess routine locates the CSR Thread
226 * corresponding to a Client ID inside a specific CSR Process.
229 * Optional pointer to the CSR Process which contains the CSR Thread
230 * that will be looked up.
233 * Pointer to a Client ID structure containing the Unique Thread ID
236 * @return Pointer to the CSR Thread corresponding to this CID, or NULL if
239 * @remarks If the CsrProcess argument is NULL, the lookup will be done inside
245 CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL
,
248 PLIST_ENTRY ListHead
, NextEntry
;
249 PCSR_THREAD FoundThread
= NULL
;
251 /* Use the Root Process if none was specified */
252 if (!CsrProcess
) CsrProcess
= CsrRootProcess
;
254 /* Save the List pointers */
255 // DPRINT1("Searching in: %p %d\n", CsrProcess, CsrProcess->ThreadCount);
256 ListHead
= &CsrProcess
->ThreadList
;
257 NextEntry
= ListHead
->Flink
;
260 while (NextEntry
!= ListHead
)
262 /* Get Thread Entry */
263 FoundThread
= CONTAINING_RECORD(NextEntry
, CSR_THREAD
, Link
);
265 /* Check for TID Match */
266 if (FoundThread
->ClientId
.UniqueThread
== Cid
->UniqueThread
) break;
269 NextEntry
= NextEntry
->Flink
;
272 /* Return what we found */
273 // DPRINT1("Found: %p\n", FoundThread);
278 * @name CsrInsertThread
280 * The CsrInsertThread routine inserts a CSR Thread into its parent's
281 * Thread List and into the Thread Hash Table.
284 * Pointer to the CSR Process containing this CSR Thread.
287 * Pointer to the CSR Thread to be inserted.
296 CsrInsertThread(IN PCSR_PROCESS Process
,
297 IN PCSR_THREAD Thread
)
300 // ASSERT(ProcessStructureListLocked());
302 /* Insert it into the Regular List */
303 InsertTailList(&Process
->ThreadList
, &Thread
->Link
);
305 /* Increase Thread Count */
306 Process
->ThreadCount
++;
308 /* Hash the Thread */
309 i
= CsrHashThread(Thread
->ClientId
.UniqueThread
);
310 // DPRINT1("TID %lx HASH: %lx\n", Thread->ClientId.UniqueThread, i);
312 /* Insert it there too */
313 InsertHeadList(&CsrThreadHashTable
[i
], &Thread
->HashLinks
);
317 * @name CsrDeallocateThread
319 * The CsrDeallocateThread frees the memory associated with a CSR Thread.
322 * Pointer to the CSR Thread to be freed.
326 * @remarks Do not call this routine. It is reserved for the internal
327 * thread management routines when a CSR Thread has been cleanly
328 * dereferenced and killed.
333 CsrDeallocateThread(IN PCSR_THREAD CsrThread
)
335 /* Free the process object from the heap */
336 // ASSERT(CsrThread->WaitBlock == NULL);
337 RtlFreeHeap(CsrHeap
, 0, CsrThread
);
341 * @name CsrRemoveThread
343 * The CsrRemoveThread function undoes a CsrInsertThread operation and
344 * removes the CSR Thread from the the Hash Table and Thread List.
347 * Pointer to the CSR Thread to remove.
351 * @remarks If this CSR Thread is the last one inside a CSR Process, the
352 * parent will be dereferenced and the CsrProcessLastThreadTerminated
355 * After executing this routine, the CSR Thread will have the
356 * CsrThreadInTermination flag set.
361 CsrRemoveThread(IN PCSR_THREAD CsrThread
)
363 ASSERT(ProcessStructureListLocked());
365 /* Remove it from the List */
366 RemoveEntryList(&CsrThread
->Link
);
368 /* Decreate the thread count of the process */
369 CsrThread
->Process
->ThreadCount
--;
371 /* Remove it from the Hash List as well */
372 if (CsrThread
->HashLinks
.Flink
) RemoveEntryList(&CsrThread
->HashLinks
);
374 /* Check if this is the last Thread */
375 if (!CsrThread
->Process
->ThreadCount
)
377 /* Check if it's not already been marked for deletion */
378 if (!(CsrThread
->Process
->Flags
& CsrProcessLastThreadTerminated
))
380 /* Let everyone know this process is about to lose the thread */
381 CsrThread
->Process
->Flags
|= CsrProcessLastThreadTerminated
;
383 /* Reference the Process */
384 CsrLockedDereferenceProcess(CsrThread
->Process
);
388 /* Mark the thread for deletion */
389 CsrThread
->Flags
|= CsrThreadInTermination
;
393 * @name CsrCreateRemoteThread
396 * The CsrCreateRemoteThread routine creates a CSR Thread object for
397 * an NT Thread which is not part of the current NT Process.
400 * Handle to an existing NT Thread to which to associate this
404 * Pointer to the Client ID structure of the NT Thread to associate
405 * with this CSR Thread.
407 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
415 CsrCreateRemoteThread(IN HANDLE hThread
,
416 IN PCLIENT_ID ClientId
)
420 PCSR_THREAD CsrThread
;
421 PCSR_PROCESS CsrProcess
;
422 KERNEL_USER_TIMES KernelTimes
;
423 DPRINT("CSRSRV: %s called\n", __FUNCTION__
);
425 /* Get the Thread Create Time */
426 Status
= NtQueryInformationThread(hThread
,
431 if (!NT_SUCCESS(Status
))
433 DPRINT1("Failed to query thread times: %lx\n", Status
);
437 /* Lock the Owner Process */
438 Status
= CsrLockProcessByClientId(&ClientId
->UniqueProcess
, &CsrProcess
);
439 if (!NT_SUCCESS(Status
))
441 DPRINT1("No known process for %lx\n", ClientId
->UniqueProcess
);
445 /* Make sure the thread didn't terminate */
446 if (KernelTimes
.ExitTime
.QuadPart
)
448 /* Unlock the process and return */
449 CsrUnlockProcess(CsrProcess
);
450 DPRINT1("Dead thread: %I64x\n", KernelTimes
.ExitTime
.QuadPart
);
451 return STATUS_THREAD_IS_TERMINATING
;
454 /* Allocate a CSR Thread Structure */
455 CsrThread
= CsrAllocateThread(CsrProcess
);
458 DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__
);
459 CsrUnlockProcess(CsrProcess
);
460 return STATUS_NO_MEMORY
;
463 /* Duplicate the Thread Handle */
464 Status
= NtDuplicateObject(NtCurrentProcess(),
470 DUPLICATE_SAME_ACCESS
);
472 if (!NT_SUCCESS(Status
))
474 DPRINT1("Thread duplication failed: %lx\n", Status
);
475 ThreadHandle
= hThread
;
478 /* Save the data we have */
479 CsrThread
->CreateTime
= KernelTimes
.CreateTime
;
480 CsrThread
->ClientId
= *ClientId
;
481 CsrThread
->ThreadHandle
= ThreadHandle
;
482 ProtectHandle(ThreadHandle
);
483 CsrThread
->Flags
= 0;
485 /* Insert the Thread into the Process */
486 CsrInsertThread(CsrProcess
, CsrThread
);
488 /* Release the lock and return */
489 CsrUnlockProcess(CsrProcess
);
490 return STATUS_SUCCESS
;
494 * @name CsrThreadRefcountZero
496 * The CsrThreadRefcountZero routine is executed when a CSR Thread has lost
497 * all its active references. It removes and de-allocates the CSR Thread.
500 * Pointer to the CSR Thread that is to be deleted.
504 * @remarks Do not call this routine. It is reserved for the internal
505 * thread management routines when a CSR Thread has lost all
508 * This routine is called with the Process Lock held.
513 CsrThreadRefcountZero(IN PCSR_THREAD CsrThread
)
515 PCSR_PROCESS CsrProcess
= CsrThread
->Process
;
517 ASSERT(ProcessStructureListLocked());
519 /* Remove this thread */
520 CsrRemoveThread(CsrThread
);
522 /* Release the Process Lock */
523 CsrReleaseProcessLock();
525 /* Close the NT Thread Handle */
526 if (CsrThread
->ThreadHandle
)
528 UnProtectHandle(CsrThread
->ThreadHandle
);
529 Status
= NtClose(CsrThread
->ThreadHandle
);
530 ASSERT(NT_SUCCESS(Status
));
533 /* De-allocate the CSR Thread Object */
534 CsrDeallocateThread(CsrThread
);
536 /* Remove a reference from the process */
537 CsrDereferenceProcess(CsrProcess
);
541 * @name CsrDestroyThread
544 * The CsrDestroyThread routine destroys the CSR Thread corresponding to
548 * Pointer to the Client ID Structure corresponding to the CSR
549 * Thread which is about to be destroyed.
551 * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING
552 * if the CSR Thread is already terminating.
559 CsrDestroyThread(IN PCLIENT_ID Cid
)
561 CLIENT_ID ClientId
= *Cid
;
562 PCSR_THREAD CsrThread
;
563 PCSR_PROCESS CsrProcess
;
566 CsrAcquireProcessLock();
568 /* Find the thread */
569 CsrThread
= CsrLocateThreadByClientId(&CsrProcess
,
572 /* Make sure we got one back, and that it's not already gone */
573 if (!CsrThread
|| CsrThread
->Flags
& CsrThreadTerminated
)
575 /* Release the lock and return failure */
576 CsrReleaseProcessLock();
577 return STATUS_THREAD_IS_TERMINATING
;
580 /* Set the terminated flag */
581 CsrThread
->Flags
|= CsrThreadTerminated
;
583 /* Acquire the Wait Lock */
584 CsrAcquireWaitLock();
586 /* Do we have an active wait block? */
587 if (CsrThread
->WaitBlock
)
589 /* Notify waiters of termination */
590 CsrNotifyWaitBlock(CsrThread
->WaitBlock
,
594 CsrProcessTerminating
,
598 /* Release the Wait Lock */
599 CsrReleaseWaitLock();
601 /* Dereference the thread */
602 CsrLockedDereferenceThread(CsrThread
);
604 /* Release the Process Lock and return success */
605 CsrReleaseProcessLock();
606 return STATUS_SUCCESS
;
610 * @name CsrLockedDereferenceThread
612 * The CsrLockedDereferenceThread dereferences a CSR Thread while the
613 * Process Lock is already being held.
616 * Pointer to the CSR Thread to be dereferenced.
620 * @remarks This routine will return with the Process Lock held.
625 CsrLockedDereferenceThread(IN PCSR_THREAD CsrThread
)
629 /* Decrease reference count */
630 LockCount
= --CsrThread
->ReferenceCount
;
631 ASSERT(LockCount
>= 0);
634 /* Call the generic cleanup code */
635 CsrThreadRefcountZero(CsrThread
);
636 CsrAcquireProcessLock();
641 * @name CsrCreateThread
644 * The CsrCreateThread routine creates a CSR Thread object for an NT Thread.
647 * Pointer to the CSR Process which will contain the CSR Thread.
650 * Handle to an existing NT Thread to which to associate this
654 * Pointer to the Client ID structure of the NT Thread to associate
655 * with this CSR Thread.
657 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
665 CsrCreateThread(IN PCSR_PROCESS CsrProcess
,
667 IN PCLIENT_ID ClientId
)
669 PCSR_THREAD CsrThread
;
670 PCSR_PROCESS CurrentProcess
;
671 PCSR_THREAD CurrentThread
= NtCurrentTeb()->CsrClientThread
;
672 CLIENT_ID CurrentCid
;
673 KERNEL_USER_TIMES KernelTimes
;
675 /* Get the current thread and CID */
676 CurrentCid
= CurrentThread
->ClientId
;
678 /* Acquire the Process Lock */
679 CsrAcquireProcessLock();
681 /* Get the current Process and make sure the Thread is valid with this CID */
682 CurrentThread
= CsrLocateThreadByClientId(&CurrentProcess
,
685 /* Something is wrong if we get an empty thread back */
688 DPRINT1("CSRSRV:%s: invalid thread!\n", __FUNCTION__
);
689 CsrReleaseProcessLock();
690 return STATUS_THREAD_IS_TERMINATING
;
693 /* Get the Thread Create Time */
694 NtQueryInformationThread(hThread
,
700 /* Allocate a CSR Thread Structure */
701 if (!(CsrThread
= CsrAllocateThread(CsrProcess
)))
703 DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__
);
704 CsrReleaseProcessLock();
705 return STATUS_NO_MEMORY
;
708 /* Save the data we have */
709 CsrThread
->CreateTime
= KernelTimes
.CreateTime
;
710 CsrThread
->ClientId
= *ClientId
;
711 CsrThread
->ThreadHandle
= hThread
;
712 CsrThread
->Flags
= 0;
714 /* Insert the Thread into the Process */
715 CsrInsertThread(CsrProcess
, CsrThread
);
717 /* Release the lock and return */
718 CsrReleaseProcessLock();
719 return STATUS_SUCCESS
;
723 * @name CsrAddStaticServerThread
726 * The CsrAddStaticServerThread routine adds a new CSR Thread to the
727 * CSR Server Process (CsrRootProcess).
730 * Handle to an existing NT Thread to which to associate this
734 * Pointer to the Client ID structure of the NT Thread to associate
735 * with this CSR Thread.
738 * Initial CSR Thread Flags to associate to this CSR Thread. Usually
739 * CsrThreadIsServerThread.
741 * @return Pointer to the newly allocated CSR Thread.
748 CsrAddStaticServerThread(IN HANDLE hThread
,
749 IN PCLIENT_ID ClientId
,
750 IN ULONG ThreadFlags
)
752 PCSR_THREAD CsrThread
;
755 CsrAcquireProcessLock();
757 /* Allocate the Server Thread */
758 CsrThread
= CsrAllocateThread(CsrRootProcess
);
761 /* Setup the Object */
762 CsrThread
->ThreadHandle
= hThread
;
763 ProtectHandle(hThread
);
764 CsrThread
->ClientId
= *ClientId
;
765 CsrThread
->Flags
= ThreadFlags
;
767 /* Insert it into the Thread List */
768 InsertTailList(&CsrRootProcess
->ThreadList
, &CsrThread
->Link
);
770 /* Increment the thread count */
771 CsrRootProcess
->ThreadCount
++;
775 DPRINT1("CsrAddStaticServerThread: alloc failed for thread 0x%x\n", hThread
);
778 /* Release the Process Lock and return */
779 CsrReleaseProcessLock();
784 * @name CsrDereferenceThread
787 * The CsrDereferenceThread routine removes a reference from a CSR Thread.
790 * Pointer to the CSR Thread to dereference.
794 * @remarks If the reference count has reached zero (ie: the CSR Thread has
795 * no more active references), it will be deleted.
800 CsrDereferenceThread(IN PCSR_THREAD CsrThread
)
802 /* Acquire process lock */
803 CsrAcquireProcessLock();
805 /* Decrease reference count */
806 ASSERT(CsrThread
->ReferenceCount
> 0);
807 if (!(--CsrThread
->ReferenceCount
))
809 /* Call the generic cleanup code */
810 CsrThreadRefcountZero(CsrThread
);
814 /* Just release the lock */
815 CsrReleaseProcessLock();