xref: /reactos/sdk/lib/rtl/critical.c (revision 682f85ad)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            lib/rtl/critical.c
5  * PURPOSE:         Critical sections
6  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
7  *                  Gunnar Dalsnes
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <rtl.h>
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #define MAX_STATIC_CS_DEBUG_OBJECTS 64
18 
19 static RTL_CRITICAL_SECTION RtlCriticalSectionLock;
20 static LIST_ENTRY RtlCriticalSectionList;
21 static BOOLEAN RtlpCritSectInitialized = FALSE;
22 static RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS];
23 static BOOLEAN RtlpDebugInfoFreeList[MAX_STATIC_CS_DEBUG_OBJECTS];
24 LARGE_INTEGER RtlpTimeout;
25 
26 extern BOOLEAN LdrpShutdownInProgress;
27 extern HANDLE LdrpShutdownThreadId;
28 
29 /* FUNCTIONS *****************************************************************/
30 
31 /*++
32  * RtlpCreateCriticalSectionSem
33  *
34  *     Checks if an Event has been created for the critical section.
35  *
36  * Params:
37  *     None
38  *
39  * Returns:
40  *     None. Raises an exception if the system call failed.
41  *
42  * Remarks:
43  *     None
44  *
45  *--*/
46 _At_(CriticalSection->LockSemaphore, _Post_notnull_)
47 VOID
48 NTAPI
49 RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection)
50 {
51     HANDLE hEvent = CriticalSection->LockSemaphore;
52     HANDLE hNewEvent;
53     NTSTATUS Status;
54 
55     /* Check if we have an event */
56     if (!hEvent)
57     {
58         /* No, so create it */
59         Status = NtCreateEvent(&hNewEvent,
60                                EVENT_ALL_ACCESS,
61                                NULL,
62                                SynchronizationEvent,
63                                FALSE);
64         if (!NT_SUCCESS(Status))
65         {
66             DPRINT1("Failed to Create Event!\n");
67 
68             /*
69              * Use INVALID_HANDLE_VALUE (-1) to signal that
70              * the global keyed event must be used.
71              */
72             hNewEvent = INVALID_HANDLE_VALUE;
73         }
74 
75         DPRINT("Created Event: %p \n", hNewEvent);
76 
77         /* Exchange the LockSemaphore field with the new handle, if it is still 0 */
78         if (InterlockedCompareExchangePointer((PVOID*)&CriticalSection->LockSemaphore,
79                                               (PVOID)hNewEvent,
80                                                NULL) != NULL)
81         {
82             /* Someone else just created an event */
83             if (hNewEvent != INVALID_HANDLE_VALUE)
84             {
85                 DPRINT("Closing already created event: %p\n", hNewEvent);
86                 NtClose(hNewEvent);
87             }
88         }
89     }
90 
91     return;
92 }
93 
94 /*++
95  * RtlpWaitForCriticalSection
96  *
97  *     Slow path of RtlEnterCriticalSection. Waits on an Event Object.
98  *
99  * Params:
100  *     CriticalSection - Critical section to acquire.
101  *
102  * Returns:
103  *     STATUS_SUCCESS, or raises an exception if a deadlock is occuring.
104  *
105  * Remarks:
106  *     None
107  *
108  *--*/
109 NTSTATUS
110 NTAPI
111 RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
112 {
113     NTSTATUS Status;
114     EXCEPTION_RECORD ExceptionRecord;
115     BOOLEAN LastChance = FALSE;
116 
117     /* Increase the Debug Entry count */
118     DPRINT("Waiting on Critical Section Event: %p %p\n",
119             CriticalSection,
120             CriticalSection->LockSemaphore);
121 
122     if (CriticalSection->DebugInfo)
123         CriticalSection->DebugInfo->EntryCount++;
124 
125     /*
126      * If we're shutting down the process, we're allowed to acquire any
127      * critical sections by force (the loader lock in particular)
128      */
129     if (LdrpShutdownInProgress &&
130         LdrpShutdownThreadId == NtCurrentTeb()->RealClientId.UniqueThread)
131     {
132         DPRINT("Forcing ownership of critical section %p\n", CriticalSection);
133         return STATUS_SUCCESS;
134     }
135 
136     /* Do we have an Event yet? */
137     if (!CriticalSection->LockSemaphore)
138     {
139         RtlpCreateCriticalSectionSem(CriticalSection);
140     }
141 
142     for (;;)
143     {
144         /* Increase the number of times we've had contention */
145         if (CriticalSection->DebugInfo)
146             CriticalSection->DebugInfo->ContentionCount++;
147 
148         /* Check if allocating the event failed */
149         if (CriticalSection->LockSemaphore == INVALID_HANDLE_VALUE)
150         {
151             /* Use the global keyed event (NULL as keyed event handle) */
152             Status = NtWaitForKeyedEvent(NULL,
153                                          CriticalSection,
154                                          FALSE,
155                                          &RtlpTimeout);
156         }
157         else
158         {
159             /* Wait on the Event */
160             Status = NtWaitForSingleObject(CriticalSection->LockSemaphore,
161                                            FALSE,
162                                            &RtlpTimeout);
163         }
164 
165         /* We have Timed out */
166         if (Status == STATUS_TIMEOUT)
167         {
168             /* Is this the 2nd time we've timed out? */
169             if (LastChance)
170             {
171                 ERROR_DBGBREAK("Deadlock: 0x%p\n", CriticalSection);
172 
173                 /* Yes it is, we are raising an exception */
174                 ExceptionRecord.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
175                 ExceptionRecord.ExceptionFlags   = 0;
176                 ExceptionRecord.ExceptionRecord  = NULL;
177                 ExceptionRecord.ExceptionAddress = RtlRaiseException;
178                 ExceptionRecord.NumberParameters = 1;
179                 ExceptionRecord.ExceptionInformation[0] = (ULONG_PTR)CriticalSection;
180                 RtlRaiseException(&ExceptionRecord);
181             }
182 
183             /* One more try */
184             LastChance = TRUE;
185         }
186         else
187         {
188             /* If we are here, everything went fine */
189             return STATUS_SUCCESS;
190         }
191     }
192 }
193 
194 /*++
195  * RtlpUnWaitCriticalSection
196  *
197  *     Slow path of RtlLeaveCriticalSection. Fires an Event Object.
198  *
199  * Params:
200  *     CriticalSection - Critical section to release.
201  *
202  * Returns:
203  *     None. Raises an exception if the system call failed.
204  *
205  * Remarks:
206  *     None
207  *
208  *--*/
209 VOID
210 NTAPI
211 RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
212 {
213     NTSTATUS Status;
214 
215     /* Do we have an Event yet? */
216     if (!CriticalSection->LockSemaphore)
217     {
218         RtlpCreateCriticalSectionSem(CriticalSection);
219     }
220 
221     /* Signal the Event */
222     DPRINT("Signaling Critical Section Event: %p, %p\n",
223             CriticalSection,
224             CriticalSection->LockSemaphore);
225 
226     /* Check if this critical section needs to use the keyed event */
227     if (CriticalSection->LockSemaphore == INVALID_HANDLE_VALUE)
228     {
229         /* Release keyed event */
230         Status = NtReleaseKeyedEvent(NULL, CriticalSection, FALSE, &RtlpTimeout);
231     }
232     else
233     {
234         /* Set the event */
235         Status = NtSetEvent(CriticalSection->LockSemaphore, NULL);
236     }
237 
238     if (!NT_SUCCESS(Status))
239     {
240         /* We've failed */
241         DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",
242                 CriticalSection,
243                 CriticalSection->LockSemaphore,
244                 Status);
245         RtlRaiseStatus(Status);
246     }
247 }
248 
249 /*++
250  * RtlpInitDeferedCriticalSection
251  *
252  *     Initializes the Critical Section implementation.
253  *
254  * Params:
255  *     None
256  *
257  * Returns:
258  *     None.
259  *
260  * Remarks:
261  *     After this call, the Process Critical Section list is protected.
262  *
263  *--*/
264 VOID
265 NTAPI
266 RtlpInitDeferedCriticalSection(VOID)
267 {
268     /* Initialize the Process Critical Section List */
269     InitializeListHead(&RtlCriticalSectionList);
270 
271     /* Initialize the CS Protecting the List */
272     RtlInitializeCriticalSection(&RtlCriticalSectionLock);
273 
274     /* It's now safe to enter it */
275     RtlpCritSectInitialized = TRUE;
276 }
277 
278 /*++
279  * RtlpAllocateDebugInfo
280  *
281  *     Finds or allocates memory for a Critical Section Debug Object
282  *
283  * Params:
284  *     None
285  *
286  * Returns:
287  *     A pointer to an empty Critical Section Debug Object.
288  *
289  * Remarks:
290  *     For optimization purposes, the first 64 entries can be cached. From
291  *     then on, future Critical Sections will allocate memory from the heap.
292  *
293  *--*/
294 PRTL_CRITICAL_SECTION_DEBUG
295 NTAPI
296 RtlpAllocateDebugInfo(VOID)
297 {
298     ULONG i;
299 
300     /* Try to allocate from our buffer first */
301     for (i = 0; i < MAX_STATIC_CS_DEBUG_OBJECTS; i++)
302     {
303         /* Check if Entry is free */
304         if (!RtlpDebugInfoFreeList[i])
305         {
306             /* Mark entry in use */
307             DPRINT("Using entry: %lu. Buffer: %p\n", i, &RtlpStaticDebugInfo[i]);
308             RtlpDebugInfoFreeList[i] = TRUE;
309 
310             /* Use free entry found */
311             return &RtlpStaticDebugInfo[i];
312         }
313     }
314 
315     /* We are out of static buffer, allocate dynamic */
316     return RtlAllocateHeap(RtlGetProcessHeap(),
317                            0,
318                            sizeof(RTL_CRITICAL_SECTION_DEBUG));
319 }
320 
321 /*++
322  * RtlpFreeDebugInfo
323  *
324  *     Frees the memory for a Critical Section Debug Object
325  *
326  * Params:
327  *     DebugInfo - Pointer to Critical Section Debug Object to free.
328  *
329  * Returns:
330  *     None.
331  *
332  * Remarks:
333  *     If the pointer is part of the static buffer, then the entry is made
334  *     free again. If not, the object is de-allocated from the heap.
335  *
336  *--*/
337 VOID
338 NTAPI
339 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo)
340 {
341     SIZE_T EntryId;
342 
343     /* Is it part of our cached entries? */
344     if ((DebugInfo >= RtlpStaticDebugInfo) &&
345         (DebugInfo <= &RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS-1]))
346     {
347         /* Yes. zero it out */
348         RtlZeroMemory(DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));
349 
350         /* Mark as free */
351         EntryId = (DebugInfo - RtlpStaticDebugInfo);
352         DPRINT("Freeing from Buffer: %p. Entry: %Iu inside Process: %p\n",
353                DebugInfo,
354                EntryId,
355                NtCurrentTeb()->ClientId.UniqueProcess);
356         RtlpDebugInfoFreeList[EntryId] = FALSE;
357 
358     }
359     else if (!DebugInfo->Flags)
360     {
361         /* It's a dynamic one, so free from the heap */
362         DPRINT("Freeing from Heap: %p inside Process: %p\n",
363                DebugInfo,
364                NtCurrentTeb()->ClientId.UniqueProcess);
365         RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, DebugInfo);
366     }
367     else
368     {
369         /* Wine stores a section name pointer in the Flags member */
370         DPRINT("Assuming static: %p inside Process: %p\n",
371                DebugInfo,
372                NtCurrentTeb()->ClientId.UniqueProcess);
373     }
374 }
375 
376 /*++
377  * RtlDeleteCriticalSection
378  * @implemented NT4
379  *
380  *     Deletes a Critical Section
381  *
382  * Params:
383  *     CriticalSection - Critical section to delete.
384  *
385  * Returns:
386  *     STATUS_SUCCESS, or error value returned by NtClose.
387  *
388  * Remarks:
389  *     The critical section members should not be read after this call.
390  *
391  *--*/
392 NTSTATUS
393 NTAPI
394 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
395 {
396     NTSTATUS Status = STATUS_SUCCESS;
397 
398     DPRINT("Deleting Critical Section: %p\n", CriticalSection);
399 
400     /* Close the Event Object Handle if it exists */
401     if (CriticalSection->LockSemaphore)
402     {
403         /* In case NtClose fails, return the status */
404         Status = NtClose(CriticalSection->LockSemaphore);
405     }
406 
407     /* Protect List */
408     RtlEnterCriticalSection(&RtlCriticalSectionLock);
409 
410     if (CriticalSection->DebugInfo)
411     {
412         /* Remove it from the list */
413         RemoveEntryList(&CriticalSection->DebugInfo->ProcessLocksList);
414 #if 0
415         /* We need to preserve Flags for RtlpFreeDebugInfo */
416         RtlZeroMemory(CriticalSection->DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));
417 #endif
418     }
419 
420     /* Unprotect */
421     RtlLeaveCriticalSection(&RtlCriticalSectionLock);
422 
423     if (CriticalSection->DebugInfo)
424     {
425         /* Free it */
426         RtlpFreeDebugInfo(CriticalSection->DebugInfo);
427     }
428 
429     /* Wipe it out */
430     RtlZeroMemory(CriticalSection, sizeof(RTL_CRITICAL_SECTION));
431 
432     /* Return */
433     return Status;
434 }
435 
436 /*++
437  * RtlSetCriticalSectionSpinCount
438  * @implemented NT4
439  *
440  *     Sets the spin count for a critical section.
441  *
442  * Params:
443  *     CriticalSection - Critical section to set the spin count for.
444  *
445  *     SpinCount - Spin count for the critical section.
446  *
447  * Returns:
448  *     STATUS_SUCCESS.
449  *
450  * Remarks:
451  *     SpinCount is ignored on single-processor systems.
452  *
453  *--*/
454 ULONG
455 NTAPI
456 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
457                                ULONG SpinCount)
458 {
459     ULONG OldCount = (ULONG)CriticalSection->SpinCount;
460 
461     /* Set to parameter if MP, or to 0 if this is Uniprocessor */
462     CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
463     return OldCount;
464 }
465 
466 /*++
467  * RtlEnterCriticalSection
468  * @implemented NT4
469  *
470  *     Waits to gain ownership of the critical section.
471  *
472  * Params:
473  *     CriticalSection - Critical section to wait for.
474  *
475  * Returns:
476  *     STATUS_SUCCESS.
477  *
478  * Remarks:
479  *     Uses a fast-path unless contention happens.
480  *
481  *--*/
482 NTSTATUS
483 NTAPI
484 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
485 {
486     HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread;
487 
488     /* Try to lock it */
489     if (InterlockedIncrement(&CriticalSection->LockCount) != 0)
490     {
491         /* We've failed to lock it! Does this thread actually own it? */
492         if (Thread == CriticalSection->OwningThread)
493         {
494             /*
495              * You own it, so you'll get it when you're done with it! No need to
496              * use the interlocked functions as only the thread who already owns
497              * the lock can modify this data.
498              */
499             CriticalSection->RecursionCount++;
500             return STATUS_SUCCESS;
501         }
502 
503         /* NOTE - CriticalSection->OwningThread can be NULL here because changing
504                   this information is not serialized. This happens when thread a
505                   acquires the lock (LockCount == 0) and thread b tries to
506                   acquire it as well (LockCount == 1) but thread a hasn't had a
507                   chance to set the OwningThread! So it's not an error when
508                   OwningThread is NULL here! */
509 
510         /* We don't own it, so we must wait for it */
511         RtlpWaitForCriticalSection(CriticalSection);
512     }
513 
514     /*
515      * Lock successful. Changing this information has not to be serialized
516      * because only one thread at a time can actually change it (the one who
517      * acquired the lock)!
518      */
519     CriticalSection->OwningThread = Thread;
520     CriticalSection->RecursionCount = 1;
521     return STATUS_SUCCESS;
522 }
523 
524 /*++
525  * RtlInitializeCriticalSection
526  * @implemented NT4
527  *
528  *     Initialises a new critical section.
529  *
530  * Params:
531  *     CriticalSection - Critical section to initialise
532  *
533  * Returns:
534  *     STATUS_SUCCESS.
535  *
536  * Remarks:
537  *     Simply calls RtlInitializeCriticalSectionAndSpinCount
538  *
539  *--*/
540 NTSTATUS
541 NTAPI
542 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
543 {
544     /* Call the Main Function */
545     return RtlInitializeCriticalSectionAndSpinCount(CriticalSection, 0);
546 }
547 
548 /*++
549  * RtlInitializeCriticalSectionAndSpinCount
550  * @implemented NT4
551  *
552  *     Initialises a new critical section.
553  *
554  * Params:
555  *     CriticalSection - Critical section to initialise
556  *
557  *     SpinCount - Spin count for the critical section.
558  *
559  * Returns:
560  *     STATUS_SUCCESS.
561  *
562  * Remarks:
563  *     SpinCount is ignored on single-processor systems.
564  *
565  *--*/
566 NTSTATUS
567 NTAPI
568 RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
569                                          ULONG SpinCount)
570 {
571     PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData;
572 
573     /* First things first, set up the Object */
574     DPRINT("Initializing Critical Section: %p\n", CriticalSection);
575     CriticalSection->LockCount = -1;
576     CriticalSection->RecursionCount = 0;
577     CriticalSection->OwningThread = 0;
578     CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
579     CriticalSection->LockSemaphore = 0;
580 
581     /* Allocate the Debug Data */
582     CritcalSectionDebugData = RtlpAllocateDebugInfo();
583     DPRINT("Allocated Debug Data: %p inside Process: %p\n",
584            CritcalSectionDebugData,
585            NtCurrentTeb()->ClientId.UniqueProcess);
586 
587     if (!CritcalSectionDebugData)
588     {
589         /* This is bad! */
590         DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection);
591         return STATUS_NO_MEMORY;
592     }
593 
594     /* Set it up */
595     CritcalSectionDebugData->Type = RTL_CRITSECT_TYPE;
596     CritcalSectionDebugData->ContentionCount = 0;
597     CritcalSectionDebugData->EntryCount = 0;
598     CritcalSectionDebugData->CriticalSection = CriticalSection;
599     CritcalSectionDebugData->Flags = 0;
600     CriticalSection->DebugInfo = CritcalSectionDebugData;
601 
602     /*
603      * Add it to the List of Critical Sections owned by the process.
604      * If we've initialized the Lock, then use it. If not, then probably
605      * this is the lock initialization itself, so insert it directly.
606      */
607     if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized))
608     {
609         DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
610                &CritcalSectionDebugData->ProcessLocksList,
611                CriticalSection,
612                &RtlCriticalSectionList);
613 
614         /* Protect List */
615         RtlEnterCriticalSection(&RtlCriticalSectionLock);
616 
617         /* Add this one */
618         InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
619 
620         /* Unprotect */
621         RtlLeaveCriticalSection(&RtlCriticalSectionLock);
622     }
623     else
624     {
625         DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
626                &CritcalSectionDebugData->ProcessLocksList,
627                CriticalSection,
628                &RtlCriticalSectionList);
629 
630         /* Add it directly */
631         InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
632     }
633 
634     return STATUS_SUCCESS;
635 }
636 
637 /*++
638  * RtlGetCriticalSectionRecursionCount
639  * @implemented NT5.2 SP1
640  *
641  *     Retrieves the recursion count of a given critical section.
642  *
643  * Params:
644  *     CriticalSection - Critical section to retrieve its recursion count.
645  *
646  * Returns:
647  *     The recursion count.
648  *
649  * Remarks:
650  *     We return the recursion count of the critical section if it is owned
651  *     by the current thread, and otherwise we return zero.
652  *
653  *--*/
654 LONG
655 NTAPI
656 RtlGetCriticalSectionRecursionCount(PRTL_CRITICAL_SECTION CriticalSection)
657 {
658     if (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
659     {
660         /*
661          * The critical section is owned by the current thread,
662          * therefore retrieve its actual recursion count.
663          */
664         return CriticalSection->RecursionCount;
665     }
666     else
667     {
668         /*
669          * It is not owned by the current thread, so
670          * for this thread there is no recursion.
671          */
672         return 0;
673     }
674 }
675 
676 /*++
677  * RtlLeaveCriticalSection
678  * @implemented NT4
679  *
680  *     Releases a critical section and makes if available for new owners.
681  *
682  * Params:
683  *     CriticalSection - Critical section to release.
684  *
685  * Returns:
686  *     STATUS_SUCCESS.
687  *
688  * Remarks:
689  *     If another thread was waiting, the slow path is entered.
690  *
691  *--*/
692 NTSTATUS
693 NTAPI
694 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
695 {
696 #if DBG
697     HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread;
698 
699     /*
700      * In win this case isn't checked. However it's a valid check so it should
701      * only be performed in debug builds!
702      */
703     if (Thread != CriticalSection->OwningThread)
704     {
705        DPRINT1("Releasing critical section not owned!\n");
706        return STATUS_INVALID_PARAMETER;
707     }
708 #endif
709 
710     /*
711      * Decrease the Recursion Count. No need to do this atomically because only
712      * the thread who holds the lock can call this function (unless the program
713      * is totally screwed...
714      */
715     if (--CriticalSection->RecursionCount)
716     {
717         if (CriticalSection->RecursionCount < 0)
718         {
719             DPRINT1("CRITICAL SECTION MESS: Section %p is not acquired!\n", CriticalSection);
720             return STATUS_UNSUCCESSFUL;
721         }
722         /* Someone still owns us, but we are free. This needs to be done atomically. */
723         InterlockedDecrement(&CriticalSection->LockCount);
724     }
725     else
726     {
727         /*
728          * Nobody owns us anymore. No need to do this atomically.
729          * See comment above.
730          */
731         CriticalSection->OwningThread = 0;
732 
733         /* Was someone wanting us? This needs to be done atomically. */
734         if (-1 != InterlockedDecrement(&CriticalSection->LockCount))
735         {
736             /* Let him have us */
737             RtlpUnWaitCriticalSection(CriticalSection);
738         }
739     }
740 
741     /* Sucessful! */
742     return STATUS_SUCCESS;
743 }
744 
745 /*++
746  * RtlTryEnterCriticalSection
747  * @implemented NT4
748  *
749  *     Attemps to gain ownership of the critical section without waiting.
750  *
751  * Params:
752  *     CriticalSection - Critical section to attempt acquiring.
753  *
754  * Returns:
755  *     TRUE if the critical section has been acquired, FALSE otherwise.
756  *
757  * Remarks:
758  *     None
759  *
760  *--*/
761 BOOLEAN
762 NTAPI
763 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
764 {
765     /* Try to take control */
766     if (InterlockedCompareExchange(&CriticalSection->LockCount, 0, -1) == -1)
767     {
768         /* It's ours */
769         CriticalSection->OwningThread =  NtCurrentTeb()->ClientId.UniqueThread;
770         CriticalSection->RecursionCount = 1;
771         return TRUE;
772     }
773     else if (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
774     {
775         /* It's already ours */
776         InterlockedIncrement(&CriticalSection->LockCount);
777         CriticalSection->RecursionCount++;
778         return TRUE;
779     }
780 
781     /* It's not ours */
782     return FALSE;
783 }
784 
785 VOID
786 NTAPI
787 RtlCheckForOrphanedCriticalSections(HANDLE ThreadHandle)
788 {
789     UNIMPLEMENTED;
790 }
791 
792 ULONG
793 NTAPI
794 RtlIsCriticalSectionLocked(PRTL_CRITICAL_SECTION CriticalSection)
795 {
796     return CriticalSection->RecursionCount != 0;
797 }
798 
799 ULONG
800 NTAPI
801 RtlIsCriticalSectionLockedByThread(PRTL_CRITICAL_SECTION CriticalSection)
802 {
803     return CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread &&
804            CriticalSection->RecursionCount != 0;
805 }
806 
807 VOID
808 NTAPI
809 RtlpNotOwnerCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
810 {
811     RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
812 }
813 
814 /* EOF */
815