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