6124af4ebb2534529e8cce1b50a0fdf27df26360
[reactos.git] / reactos / subsystems / win32 / csrss / csrsrv / thredsup.c
1 /*
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 Process Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 * Alex Ionescu
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <srv.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #define CsrHashThread(t) \
18 (HandleToUlong(t)&(256 - 1))
19
20 /* GLOBALS ********************************************************************/
21
22 LIST_ENTRY CsrThreadHashTable[256];
23
24 /* 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
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
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 PCSR_THREAD
105 NTAPI
106 CsrAllocateThread(IN PCSR_PROCESS CsrProcess)
107 {
108 PCSR_THREAD CsrThread;
109
110 /* Allocate the structure */
111 CsrThread = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, sizeof(CSR_THREAD));
112 if (!CsrThread) return(NULL);
113
114 /* Reference the Thread and Process */
115 CsrThread->ReferenceCount++;
116 CsrProcess->ReferenceCount++;
117
118 /* Set the Parent Process */
119 CsrThread->Process = CsrProcess;
120
121 /* Return Thread */
122 return CsrThread;
123 }
124
125 /*++
126 * @name CsrLockedReferenceThread
127 *
128 * The CsrLockedReferenceThread refences a CSR Thread while the
129 * Process Lock is already being held.
130 *
131 * @param CsrThread
132 * Pointer to the CSR Thread to be referenced.
133 *
134 * @return None.
135 *
136 * @remarks This routine will return with the Process Lock held.
137 *
138 *--*/
139 VOID
140 NTAPI
141 CsrLockedReferenceThread(IN PCSR_THREAD CsrThread)
142 {
143 /* Increment the reference count */
144 ++CsrThread->ReferenceCount;
145 }
146
147 PCSR_THREAD
148 NTAPI
149 CsrLocateThreadByClientId(OUT PCSR_PROCESS *Process OPTIONAL,
150 IN PCLIENT_ID ClientId)
151 {
152 ULONG i;
153 PLIST_ENTRY ListHead, NextEntry;
154 PCSR_THREAD FoundThread;
155
156 /* Hash the Thread */
157 i = CsrHashThread(ClientId->UniqueThread);
158
159 /* Set the list pointers */
160 ListHead = &CsrThreadHashTable[i];
161 NextEntry = ListHead->Flink;
162
163 /* Star the loop */
164 while (NextEntry != ListHead)
165 {
166 /* Get the thread */
167 FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, HashLinks);
168
169 /* Compare the CID */
170 if (FoundThread->ClientId.UniqueThread == ClientId->UniqueThread)
171 {
172 /* Match found, return the process */
173 *Process = FoundThread->Process;
174
175 /* Return thread too */
176 // DPRINT1("Found: %p %p\n", FoundThread, FoundThread->Process);
177 return FoundThread;
178 }
179
180 /* Next */
181 NextEntry = NextEntry->Flink;
182 }
183
184 /* Nothing found */
185 return NULL;
186 }
187
188 PCSR_THREAD
189 NTAPI
190 CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL,
191 IN PCLIENT_ID Cid)
192 {
193 PLIST_ENTRY ListHead, NextEntry;
194 PCSR_THREAD FoundThread = NULL;
195
196 /* Use the Root Process if none was specified */
197 if (!CsrProcess) CsrProcess = CsrRootProcess;
198
199 /* Save the List pointers */
200 // DPRINT1("Searching in: %p %d\n", CsrProcess, CsrProcess->ThreadCount);
201 ListHead = &CsrProcess->ThreadList;
202 NextEntry = ListHead->Flink;
203
204 /* Start the Loop */
205 while (NextEntry != ListHead)
206 {
207 /* Get Thread Entry */
208 FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
209
210 /* Check for TID Match */
211 if (FoundThread->ClientId.UniqueThread == Cid->UniqueThread) break;
212
213 /* Next entry */
214 NextEntry = NextEntry->Flink;
215 }
216
217 /* Return what we found */
218 // DPRINT1("Found: %p\n", FoundThread);
219 return FoundThread;
220 }
221
222 VOID
223 NTAPI
224 CsrInsertThread(IN PCSR_PROCESS Process,
225 IN PCSR_THREAD Thread)
226 {
227 ULONG i;
228
229 /* Insert it into the Regular List */
230 InsertTailList(&Process->ThreadList, &Thread->Link);
231
232 /* Increase Thread Count */
233 Process->ThreadCount++;
234
235 /* Hash the Thread */
236 i = CsrHashThread(Thread->ClientId.UniqueThread);
237 // DPRINT1("TID %lx HASH: %lx\n", Thread->ClientId.UniqueThread, i);
238
239 /* Insert it there too */
240 InsertHeadList(&CsrThreadHashTable[i], &Thread->HashLinks);
241 }
242
243 VOID
244 NTAPI
245 CsrDeallocateThread(IN PCSR_THREAD CsrThread)
246 {
247 /* Free the process object from the heap */
248 RtlFreeHeap(CsrHeap, 0, CsrThread);
249 }
250
251 VOID
252 NTAPI
253 CsrRemoveThread(IN PCSR_THREAD CsrThread)
254 {
255 ASSERT(ProcessStructureListLocked());
256
257 /* Remove it from the List */
258 RemoveEntryList(&CsrThread->Link);
259
260 /* Decreate the thread count of the process */
261 CsrThread->Process->ThreadCount--;
262
263 /* Remove it from the Hash List as well */
264 if (CsrThread->HashLinks.Flink) RemoveEntryList(&CsrThread->HashLinks);
265
266 /* Check if this is the last Thread */
267 if (!CsrThread->Process->ThreadCount)
268 {
269 /* Check if it's not already been marked for deletion */
270 if (!(CsrThread->Process->Flags & CsrProcessLastThreadTerminated))
271 {
272 /* Let everyone know this process is about to lose the thread */
273 CsrThread->Process->Flags |= CsrProcessLastThreadTerminated;
274
275 /* Reference the Process */
276 CsrLockedDereferenceProcess(CsrThread->Process);
277 }
278 }
279
280 /* Mark the thread for deletion */
281 CsrThread->Flags |= CsrThreadInTermination;
282 }
283
284 /*++
285 * @name CsrCreateRemoteThread
286 * @implemented NT4
287 *
288 * The CsrCreateRemoteThread routine creates a CSR Thread object for
289 * an NT Thread which is not part of the current NT Process.
290 *
291 * @param hThread
292 * Handle to an existing NT Thread to which to associate this
293 * CSR Thread.
294 *
295 * @param ClientId
296 * Pointer to the Client ID structure of the NT Thread to associate
297 * with this CSR Thread.
298 *
299 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
300 * othwerwise.
301 *
302 * @remarks None.
303 *
304 *--*/
305 NTSTATUS
306 NTAPI
307 CsrCreateRemoteThread(IN HANDLE hThread,
308 IN PCLIENT_ID ClientId)
309 {
310 NTSTATUS Status;
311 HANDLE ThreadHandle;
312 PCSR_THREAD CsrThread;
313 PCSR_PROCESS CsrProcess;
314 KERNEL_USER_TIMES KernelTimes;
315 DPRINT("CSRSRV: %s called\n", __FUNCTION__);
316
317 /* Get the Thread Create Time */
318 Status = NtQueryInformationThread(hThread,
319 ThreadTimes,
320 &KernelTimes,
321 sizeof(KernelTimes),
322 NULL);
323 if (!NT_SUCCESS(Status))
324 {
325 DPRINT1("Failed to query thread times: %lx\n", Status);
326 return Status;
327 }
328
329 /* Lock the Owner Process */
330 Status = CsrLockProcessByClientId(&ClientId->UniqueProcess, &CsrProcess);
331 if (!NT_SUCCESS(Status))
332 {
333 DPRINT1("No known process for %lx\n", ClientId->UniqueProcess);
334 return Status;
335 }
336
337 /* Make sure the thread didn't terminate */
338 if (KernelTimes.ExitTime.QuadPart)
339 {
340 /* Unlock the process and return */
341 CsrUnlockProcess(CsrProcess);
342 DPRINT1("Dead thread: %I64x\n", KernelTimes.ExitTime.QuadPart);
343 return STATUS_THREAD_IS_TERMINATING;
344 }
345
346 /* Allocate a CSR Thread Structure */
347 CsrThread = CsrAllocateThread(CsrProcess);
348 if (!CsrThread)
349 {
350 DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__);
351 CsrUnlockProcess(CsrProcess);
352 return STATUS_NO_MEMORY;
353 }
354
355 /* Duplicate the Thread Handle */
356 Status = NtDuplicateObject(NtCurrentProcess(),
357 hThread,
358 NtCurrentProcess(),
359 &ThreadHandle,
360 0,
361 0,
362 DUPLICATE_SAME_ACCESS);
363 /* Allow failure */
364 if (!NT_SUCCESS(Status))
365 {
366 DPRINT1("Thread duplication failed: %lx\n", Status);
367 ThreadHandle = hThread;
368 }
369
370 /* Save the data we have */
371 CsrThread->CreateTime = KernelTimes.CreateTime;
372 CsrThread->ClientId = *ClientId;
373 CsrThread->ThreadHandle = ThreadHandle;
374 ProtectHandle(ThreadHandle);
375 CsrThread->Flags = 0;
376
377 /* Insert the Thread into the Process */
378 CsrInsertThread(CsrProcess, CsrThread);
379
380 /* Release the lock and return */
381 CsrUnlockProcess(CsrProcess);
382 return STATUS_SUCCESS;
383 }
384
385 VOID
386 NTAPI
387 CsrThreadRefcountZero(IN PCSR_THREAD CsrThread)
388 {
389 PCSR_PROCESS CsrProcess = CsrThread->Process;
390 NTSTATUS Status;
391 ASSERT(ProcessStructureListLocked());
392
393 /* Remove this thread */
394 CsrRemoveThread(CsrThread);
395
396 /* Release the Process Lock */
397 CsrReleaseProcessLock();
398
399 /* Close the NT Thread Handle */
400 if (CsrThread->ThreadHandle)
401 {
402 UnProtectHandle(CsrThread->ThreadHandle);
403 Status = NtClose(CsrThread->ThreadHandle);
404 ASSERT(NT_SUCCESS(Status));
405 }
406
407 /* De-allocate the CSR Thread Object */
408 CsrDeallocateThread(CsrThread);
409
410 /* Remove a reference from the process */
411 CsrDereferenceProcess(CsrProcess);
412 }
413
414 /*++
415 * @name CsrDestroyThread
416 * @implemented NT4
417 *
418 * The CsrDestroyThread routine destroys the CSR Thread corresponding to
419 * a given Thread ID.
420 *
421 * @param Cid
422 * Pointer to the Client ID Structure corresponding to the CSR
423 * Thread which is about to be destroyed.
424 *
425 * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING
426 * if the CSR Thread is already terminating.
427 *
428 * @remarks None.
429 *
430 *--*/
431 NTSTATUS
432 NTAPI
433 CsrDestroyThread(IN PCLIENT_ID Cid)
434 {
435 CLIENT_ID ClientId = *Cid;
436 PCSR_THREAD CsrThread;
437 PCSR_PROCESS CsrProcess;
438
439 /* Acquire lock */
440 CsrAcquireProcessLock();
441
442 /* Find the thread */
443 CsrThread = CsrLocateThreadByClientId(&CsrProcess,
444 &ClientId);
445
446 /* Make sure we got one back, and that it's not already gone */
447 if (!CsrThread || CsrThread->Flags & CsrThreadTerminated)
448 {
449 /* Release the lock and return failure */
450 CsrReleaseProcessLock();
451 return STATUS_THREAD_IS_TERMINATING;
452 }
453
454 /* Set the terminated flag */
455 CsrThread->Flags |= CsrThreadTerminated;
456
457 /* Acquire the Wait Lock */
458 CsrAcquireWaitLock();
459
460 /* Do we have an active wait block? */
461 if (CsrThread->WaitBlock)
462 {
463 /* Notify waiters of termination */
464 CsrNotifyWaitBlock(CsrThread->WaitBlock,
465 NULL,
466 NULL,
467 NULL,
468 CsrProcessTerminating,
469 TRUE);
470 }
471
472 /* Release the Wait Lock */
473 CsrReleaseWaitLock();
474
475 /* Dereference the thread */
476 CsrLockedDereferenceThread(CsrThread);
477
478 /* Release the Process Lock and return success */
479 CsrReleaseProcessLock();
480 return STATUS_SUCCESS;
481 }
482
483 /*++
484 * @name CsrLockedDereferenceThread
485 *
486 * The CsrLockedDereferenceThread derefences a CSR Thread while the
487 * Process Lock is already being held.
488 *
489 * @param CsrThread
490 * Pointer to the CSR Thread to be dereferenced.
491 *
492 * @return None.
493 *
494 * @remarks This routine will return with the Process Lock held.
495 *
496 *--*/
497 VOID
498 NTAPI
499 CsrLockedDereferenceThread(IN PCSR_THREAD CsrThread)
500 {
501 LONG LockCount;
502
503 /* Decrease reference count */
504 LockCount = --CsrThread->ReferenceCount;
505 ASSERT(LockCount >= 0);
506 if (!LockCount)
507 {
508 /* Call the generic cleanup code */
509 CsrThreadRefcountZero(CsrThread);
510 CsrAcquireProcessLock();
511 }
512 }
513
514 NTSTATUS
515 NTAPI
516 CsrCreateThread(IN PCSR_PROCESS CsrProcess,
517 IN HANDLE hThread,
518 IN PCLIENT_ID ClientId)
519 {
520 PCSR_THREAD CsrThread;
521 PCSR_PROCESS CurrentProcess;
522 PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread;
523 CLIENT_ID CurrentCid;
524 KERNEL_USER_TIMES KernelTimes;
525
526 /* Get the current thread and CID */
527 CurrentCid = CurrentThread->ClientId;
528
529 /* Acquire the Process Lock */
530 CsrAcquireProcessLock();
531
532 /* Get the current Process and make sure the Thread is valid with this CID */
533 CurrentThread = CsrLocateThreadByClientId(&CurrentProcess,
534 &CurrentCid);
535
536 /* Something is wrong if we get an empty thread back */
537 if (!CurrentThread)
538 {
539 DPRINT1("CSRSRV:%s: invalid thread!\n", __FUNCTION__);
540 CsrReleaseProcessLock();
541 return STATUS_THREAD_IS_TERMINATING;
542 }
543
544 /* Get the Thread Create Time */
545 NtQueryInformationThread(hThread,
546 ThreadTimes,
547 (PVOID)&KernelTimes,
548 sizeof(KernelTimes),
549 NULL);
550
551 /* Allocate a CSR Thread Structure */
552 if (!(CsrThread = CsrAllocateThread(CsrProcess)))
553 {
554 DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__);
555 CsrReleaseProcessLock();
556 return STATUS_NO_MEMORY;
557 }
558
559 /* Save the data we have */
560 CsrThread->CreateTime = KernelTimes.CreateTime;
561 CsrThread->ClientId = *ClientId;
562 CsrThread->ThreadHandle = hThread;
563 CsrThread->Flags = 0;
564
565 /* Insert the Thread into the Process */
566 CsrInsertThread(CsrProcess, CsrThread);
567
568 /* Release the lock and return */
569 CsrReleaseProcessLock();
570 return STATUS_SUCCESS;
571 }
572
573 /*++
574 * @name CsrAddStaticServerThread
575 * @implemented NT4
576 *
577 * The CsrAddStaticServerThread routine adds a new CSR Thread to the
578 * CSR Server Process (CsrRootProcess).
579 *
580 * @param hThread
581 * Handle to an existing NT Thread to which to associate this
582 * CSR Thread.
583 *
584 * @param ClientId
585 * Pointer to the Client ID structure of the NT Thread to associate
586 * with this CSR Thread.
587 *
588 * @param ThreadFlags
589 * Initial CSR Thread Flags to associate to this CSR Thread. Usually
590 * CsrThreadIsServerThread.
591 *
592 * @return Pointer to the newly allocated CSR Thread.
593 *
594 * @remarks None.
595 *
596 *--*/
597 PCSR_THREAD
598 NTAPI
599 CsrAddStaticServerThread(IN HANDLE hThread,
600 IN PCLIENT_ID ClientId,
601 IN ULONG ThreadFlags)
602 {
603 PCSR_THREAD CsrThread;
604
605 /* Get the Lock */
606 CsrAcquireProcessLock();
607
608 /* Allocate the Server Thread */
609 CsrThread = CsrAllocateThread(CsrRootProcess);
610 if (CsrThread)
611 {
612 /* Setup the Object */
613 CsrThread->ThreadHandle = hThread;
614 ProtectHandle(hThread);
615 CsrThread->ClientId = *ClientId;
616 CsrThread->Flags = ThreadFlags;
617
618 /* Insert it into the Thread List */
619 InsertTailList(&CsrRootProcess->ThreadList, &CsrThread->Link);
620
621 /* Increment the thread count */
622 CsrRootProcess->ThreadCount++;
623 }
624 else
625 {
626 DPRINT1("CsrAddStaticServerThread: alloc failed for thread 0x%x\n", hThread);
627 }
628
629 /* Release the Process Lock and return */
630 CsrReleaseProcessLock();
631 return CsrThread;
632 }
633
634 /*++
635 * @name CsrDereferenceThread
636 * @implemented NT4
637 *
638 * The CsrDereferenceThread routine removes a reference from a CSR Thread.
639 *
640 * @param CsrThread
641 * Pointer to the CSR Thread to dereference.
642 *
643 * @return None.
644 *
645 * @remarks If the reference count has reached zero (ie: the CSR Thread has
646 * no more active references), it will be deleted.
647 *
648 *--*/
649 VOID
650 NTAPI
651 CsrDereferenceThread(IN PCSR_THREAD CsrThread)
652 {
653 /* Acquire process lock */
654 CsrAcquireProcessLock();
655
656 /* Decrease reference count */
657 ASSERT(CsrThread->ReferenceCount > 0);
658 if (!(--CsrThread->ReferenceCount))
659 {
660 /* Call the generic cleanup code */
661 CsrThreadRefcountZero(CsrThread);
662 }
663 else
664 {
665 /* Just release the lock */
666 CsrReleaseProcessLock();
667 }
668 }
669
670 /* EOF */