xref: /reactos/sdk/lib/rtl/critical.c (revision 3b1c81b0)
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 = {&RtlCriticalSectionList, &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 CS Protecting the List */
269     RtlInitializeCriticalSection(&RtlCriticalSectionLock);
270 
271     /* It's now safe to enter it */
272     RtlpCritSectInitialized = TRUE;
273 }
274 
275 /*++
276  * RtlpAllocateDebugInfo
277  *
278  *     Finds or allocates memory for a Critical Section Debug Object
279  *
280  * Params:
281  *     None
282  *
283  * Returns:
284  *     A pointer to an empty Critical Section Debug Object.
285  *
286  * Remarks:
287  *     For optimization purposes, the first 64 entries can be cached. From
288  *     then on, future Critical Sections will allocate memory from the heap.
289  *
290  *--*/
291 PRTL_CRITICAL_SECTION_DEBUG
292 NTAPI
293 RtlpAllocateDebugInfo(VOID)
294 {
295     ULONG i;
296 
297     /* Try to allocate from our buffer first */
298     for (i = 0; i < MAX_STATIC_CS_DEBUG_OBJECTS; i++)
299     {
300         /* Check if Entry is free */
301         if (!RtlpDebugInfoFreeList[i])
302         {
303             /* Mark entry in use */
304             DPRINT("Using entry: %lu. Buffer: %p\n", i, &RtlpStaticDebugInfo[i]);
305             RtlpDebugInfoFreeList[i] = TRUE;
306 
307             /* Use free entry found */
308             return &RtlpStaticDebugInfo[i];
309         }
310     }
311 
312     /* We are out of static buffer, allocate dynamic */
313     return RtlAllocateHeap(RtlGetProcessHeap(),
314                            0,
315                            sizeof(RTL_CRITICAL_SECTION_DEBUG));
316 }
317 
318 /*++
319  * RtlpFreeDebugInfo
320  *
321  *     Frees the memory for a Critical Section Debug Object
322  *
323  * Params:
324  *     DebugInfo - Pointer to Critical Section Debug Object to free.
325  *
326  * Returns:
327  *     None.
328  *
329  * Remarks:
330  *     If the pointer is part of the static buffer, then the entry is made
331  *     free again. If not, the object is de-allocated from the heap.
332  *
333  *--*/
334 VOID
335 NTAPI
336 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo)
337 {
338     SIZE_T EntryId;
339 
340     /* Is it part of our cached entries? */
341     if ((DebugInfo >= RtlpStaticDebugInfo) &&
342         (DebugInfo <= &RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS-1]))
343     {
344         /* Yes. zero it out */
345         RtlZeroMemory(DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));
346 
347         /* Mark as free */
348         EntryId = (DebugInfo - RtlpStaticDebugInfo);
349         DPRINT("Freeing from Buffer: %p. Entry: %Iu inside Process: %p\n",
350                DebugInfo,
351                EntryId,
352                NtCurrentTeb()->ClientId.UniqueProcess);
353         RtlpDebugInfoFreeList[EntryId] = FALSE;
354 
355     }
356     else if (!DebugInfo->Flags)
357     {
358         /* It's a dynamic one, so free from the heap */
359         DPRINT("Freeing from Heap: %p inside Process: %p\n",
360                DebugInfo,
361                NtCurrentTeb()->ClientId.UniqueProcess);
362         RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, DebugInfo);
363     }
364     else
365     {
366         /* Wine stores a section name pointer in the Flags member */
367         DPRINT("Assuming static: %p inside Process: %p\n",
368                DebugInfo,
369                NtCurrentTeb()->ClientId.UniqueProcess);
370     }
371 }
372 
373 /*++
374  * RtlDeleteCriticalSection
375  * @implemented NT4
376  *
377  *     Deletes a Critical Section
378  *
379  * Params:
380  *     CriticalSection - Critical section to delete.
381  *
382  * Returns:
383  *     STATUS_SUCCESS, or error value returned by NtClose.
384  *
385  * Remarks:
386  *     The critical section members should not be read after this call.
387  *
388  *--*/
389 NTSTATUS
390 NTAPI
391 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
392 {
393     NTSTATUS Status = STATUS_SUCCESS;
394 
395     DPRINT("Deleting Critical Section: %p\n", CriticalSection);
396 
397     /* Close the Event Object Handle if it exists */
398     if (CriticalSection->LockSemaphore)
399     {
400         /* In case NtClose fails, return the status */
401         Status = NtClose(CriticalSection->LockSemaphore);
402     }
403 
404     /* Protect List */
405     RtlEnterCriticalSection(&RtlCriticalSectionLock);
406 
407     if (CriticalSection->DebugInfo)
408     {
409         /* Remove it from the list */
410         RemoveEntryList(&CriticalSection->DebugInfo->ProcessLocksList);
411 #if 0
412         /* We need to preserve Flags for RtlpFreeDebugInfo */
413         RtlZeroMemory(CriticalSection->DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));
414 #endif
415     }
416 
417     /* Unprotect */
418     RtlLeaveCriticalSection(&RtlCriticalSectionLock);
419 
420     if (CriticalSection->DebugInfo)
421     {
422         /* Free it */
423         RtlpFreeDebugInfo(CriticalSection->DebugInfo);
424     }
425 
426     /* Wipe it out */
427     RtlZeroMemory(CriticalSection, sizeof(RTL_CRITICAL_SECTION));
428 
429     /* Return */
430     return Status;
431 }
432 
433 /*++
434  * RtlSetCriticalSectionSpinCount
435  * @implemented NT4
436  *
437  *     Sets the spin count for a critical section.
438  *
439  * Params:
440  *     CriticalSection - Critical section to set the spin count for.
441  *
442  *     SpinCount - Spin count for the critical section.
443  *
444  * Returns:
445  *     STATUS_SUCCESS.
446  *
447  * Remarks:
448  *     SpinCount is ignored on single-processor systems.
449  *
450  *--*/
451 ULONG
452 NTAPI
453 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
454                                ULONG SpinCount)
455 {
456     ULONG OldCount = (ULONG)CriticalSection->SpinCount;
457 
458     /* Set to parameter if MP, or to 0 if this is Uniprocessor */
459     CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
460     return OldCount;
461 }
462 
463 /*++
464  * RtlEnterCriticalSection
465  * @implemented NT4
466  *
467  *     Waits to gain ownership of the critical section.
468  *
469  * Params:
470  *     CriticalSection - Critical section to wait for.
471  *
472  * Returns:
473  *     STATUS_SUCCESS.
474  *
475  * Remarks:
476  *     Uses a fast-path unless contention happens.
477  *
478  *--*/
479 NTSTATUS
480 NTAPI
481 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
482 {
483     HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread;
484 
485     /* Try to lock it */
486     if (InterlockedIncrement(&CriticalSection->LockCount) != 0)
487     {
488         /* We've failed to lock it! Does this thread actually own it? */
489         if (Thread == CriticalSection->OwningThread)
490         {
491             /*
492              * You own it, so you'll get it when you're done with it! No need to
493              * use the interlocked functions as only the thread who already owns
494              * the lock can modify this data.
495              */
496             CriticalSection->RecursionCount++;
497             return STATUS_SUCCESS;
498         }
499 
500         /* NOTE - CriticalSection->OwningThread can be NULL here because changing
501                   this information is not serialized. This happens when thread a
502                   acquires the lock (LockCount == 0) and thread b tries to
503                   acquire it as well (LockCount == 1) but thread a hasn't had a
504                   chance to set the OwningThread! So it's not an error when
505                   OwningThread is NULL here! */
506 
507         /* We don't own it, so we must wait for it */
508         RtlpWaitForCriticalSection(CriticalSection);
509     }
510 
511     /*
512      * Lock successful. Changing this information has not to be serialized
513      * because only one thread at a time can actually change it (the one who
514      * acquired the lock)!
515      */
516     CriticalSection->OwningThread = Thread;
517     CriticalSection->RecursionCount = 1;
518     return STATUS_SUCCESS;
519 }
520 
521 /*++
522  * RtlInitializeCriticalSection
523  * @implemented NT4
524  *
525  *     Initialises a new critical section.
526  *
527  * Params:
528  *     CriticalSection - Critical section to initialise
529  *
530  * Returns:
531  *     STATUS_SUCCESS.
532  *
533  * Remarks:
534  *     Simply calls RtlInitializeCriticalSectionAndSpinCount
535  *
536  *--*/
537 NTSTATUS
538 NTAPI
539 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
540 {
541     /* Call the Main Function */
542     return RtlInitializeCriticalSectionAndSpinCount(CriticalSection, 0);
543 }
544 
545 /*++
546  * RtlInitializeCriticalSectionAndSpinCount
547  * @implemented NT4
548  *
549  *     Initialises a new critical section.
550  *
551  * Params:
552  *     CriticalSection - Critical section to initialise
553  *
554  *     SpinCount - Spin count for the critical section.
555  *
556  * Returns:
557  *     STATUS_SUCCESS.
558  *
559  * Remarks:
560  *     SpinCount is ignored on single-processor systems.
561  *
562  *--*/
563 NTSTATUS
564 NTAPI
565 RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
566                                          ULONG SpinCount)
567 {
568     PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData;
569 
570     /* First things first, set up the Object */
571     DPRINT("Initializing Critical Section: %p\n", CriticalSection);
572     CriticalSection->LockCount = -1;
573     CriticalSection->RecursionCount = 0;
574     CriticalSection->OwningThread = 0;
575     CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
576     CriticalSection->LockSemaphore = 0;
577 
578     /* Allocate the Debug Data */
579     CritcalSectionDebugData = RtlpAllocateDebugInfo();
580     DPRINT("Allocated Debug Data: %p inside Process: %p\n",
581            CritcalSectionDebugData,
582            NtCurrentTeb()->ClientId.UniqueProcess);
583 
584     if (!CritcalSectionDebugData)
585     {
586         /* This is bad! */
587         DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection);
588         return STATUS_NO_MEMORY;
589     }
590 
591     /* Set it up */
592     CritcalSectionDebugData->Type = RTL_CRITSECT_TYPE;
593     CritcalSectionDebugData->ContentionCount = 0;
594     CritcalSectionDebugData->EntryCount = 0;
595     CritcalSectionDebugData->CriticalSection = CriticalSection;
596     CritcalSectionDebugData->Flags = 0;
597     CriticalSection->DebugInfo = CritcalSectionDebugData;
598 
599     /*
600      * Add it to the List of Critical Sections owned by the process.
601      * If we've initialized the Lock, then use it. If not, then probably
602      * this is the lock initialization itself, so insert it directly.
603      */
604     if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized))
605     {
606         DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
607                &CritcalSectionDebugData->ProcessLocksList,
608                CriticalSection,
609                &RtlCriticalSectionList);
610 
611         /* Protect List */
612         RtlEnterCriticalSection(&RtlCriticalSectionLock);
613 
614         /* Add this one */
615         InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
616 
617         /* Unprotect */
618         RtlLeaveCriticalSection(&RtlCriticalSectionLock);
619     }
620     else
621     {
622         DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
623                &CritcalSectionDebugData->ProcessLocksList,
624                CriticalSection,
625                &RtlCriticalSectionList);
626 
627         /* Add it directly */
628         InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
629     }
630 
631     return STATUS_SUCCESS;
632 }
633 
634 /*++
635  * RtlGetCriticalSectionRecursionCount
636  * @implemented NT5.2 SP1
637  *
638  *     Retrieves the recursion count of a given critical section.
639  *
640  * Params:
641  *     CriticalSection - Critical section to retrieve its recursion count.
642  *
643  * Returns:
644  *     The recursion count.
645  *
646  * Remarks:
647  *     We return the recursion count of the critical section if it is owned
648  *     by the current thread, and otherwise we return zero.
649  *
650  *--*/
651 LONG
652 NTAPI
653 RtlGetCriticalSectionRecursionCount(PRTL_CRITICAL_SECTION CriticalSection)
654 {
655     if (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
656     {
657         /*
658          * The critical section is owned by the current thread,
659          * therefore retrieve its actual recursion count.
660          */
661         return CriticalSection->RecursionCount;
662     }
663     else
664     {
665         /*
666          * It is not owned by the current thread, so
667          * for this thread there is no recursion.
668          */
669         return 0;
670     }
671 }
672 
673 /*++
674  * RtlLeaveCriticalSection
675  * @implemented NT4
676  *
677  *     Releases a critical section and makes if available for new owners.
678  *
679  * Params:
680  *     CriticalSection - Critical section to release.
681  *
682  * Returns:
683  *     STATUS_SUCCESS.
684  *
685  * Remarks:
686  *     If another thread was waiting, the slow path is entered.
687  *
688  *--*/
689 NTSTATUS
690 NTAPI
691 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
692 {
693 #if DBG
694     HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread;
695 
696     /*
697      * In win this case isn't checked. However it's a valid check so it should
698      * only be performed in debug builds!
699      */
700     if (Thread != CriticalSection->OwningThread)
701     {
702        DPRINT1("Releasing critical section not owned!\n");
703        return STATUS_INVALID_PARAMETER;
704     }
705 #endif
706 
707     /*
708      * Decrease the Recursion Count. No need to do this atomically because only
709      * the thread who holds the lock can call this function (unless the program
710      * is totally screwed...
711      */
712     if (--CriticalSection->RecursionCount)
713     {
714         if (CriticalSection->RecursionCount < 0)
715         {
716             DPRINT1("CRITICAL SECTION MESS: Section %p is not acquired!\n", CriticalSection);
717             return STATUS_UNSUCCESSFUL;
718         }
719         /* Someone still owns us, but we are free. This needs to be done atomically. */
720         InterlockedDecrement(&CriticalSection->LockCount);
721     }
722     else
723     {
724         /*
725          * Nobody owns us anymore. No need to do this atomically.
726          * See comment above.
727          */
728         CriticalSection->OwningThread = 0;
729 
730         /* Was someone wanting us? This needs to be done atomically. */
731         if (-1 != InterlockedDecrement(&CriticalSection->LockCount))
732         {
733             /* Let him have us */
734             RtlpUnWaitCriticalSection(CriticalSection);
735         }
736     }
737 
738     /* Sucessful! */
739     return STATUS_SUCCESS;
740 }
741 
742 /*++
743  * RtlTryEnterCriticalSection
744  * @implemented NT4
745  *
746  *     Attemps to gain ownership of the critical section without waiting.
747  *
748  * Params:
749  *     CriticalSection - Critical section to attempt acquiring.
750  *
751  * Returns:
752  *     TRUE if the critical section has been acquired, FALSE otherwise.
753  *
754  * Remarks:
755  *     None
756  *
757  *--*/
758 BOOLEAN
759 NTAPI
760 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
761 {
762     /* Try to take control */
763     if (InterlockedCompareExchange(&CriticalSection->LockCount, 0, -1) == -1)
764     {
765         /* It's ours */
766         CriticalSection->OwningThread =  NtCurrentTeb()->ClientId.UniqueThread;
767         CriticalSection->RecursionCount = 1;
768         return TRUE;
769     }
770     else if (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
771     {
772         /* It's already ours */
773         InterlockedIncrement(&CriticalSection->LockCount);
774         CriticalSection->RecursionCount++;
775         return TRUE;
776     }
777 
778     /* It's not ours */
779     return FALSE;
780 }
781 
782 VOID
783 NTAPI
784 RtlCheckForOrphanedCriticalSections(HANDLE ThreadHandle)
785 {
786     UNIMPLEMENTED;
787 }
788 
789 ULONG
790 NTAPI
791 RtlIsCriticalSectionLocked(PRTL_CRITICAL_SECTION CriticalSection)
792 {
793     return CriticalSection->RecursionCount != 0;
794 }
795 
796 ULONG
797 NTAPI
798 RtlIsCriticalSectionLockedByThread(PRTL_CRITICAL_SECTION CriticalSection)
799 {
800     return CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread &&
801            CriticalSection->RecursionCount != 0;
802 }
803 
804 VOID
805 NTAPI
806 RtlpNotOwnerCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
807 {
808     RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
809 }
810 
811 /* EOF */
812