set eol-style:native
[reactos.git] / reactos / lib / rtl / critical.c
index 5e920b6..e368427 100644 (file)
-/*\r
- * COPYRIGHT:       See COPYING in the top level directory\r
- * PROJECT:         ReactOS system libraries\r
- * FILE:            lib/rtl/critical.c\r
- * PURPOSE:         Critical sections\r
- * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)\r
- *                  Gunnar Dalsnes\r
- */\r
-\r
-/* INCLUDES *****************************************************************/\r
-\r
-#include <rtl.h>\r
-\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-#define MAX_STATIC_CS_DEBUG_OBJECTS 64\r
-\r
-static RTL_CRITICAL_SECTION RtlCriticalSectionLock;\r
-static LIST_ENTRY RtlCriticalSectionList;\r
-static BOOLEAN RtlpCritSectInitialized = FALSE;\r
-static RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS];\r
-static BOOLEAN RtlpDebugInfoFreeList[MAX_STATIC_CS_DEBUG_OBJECTS];\r
-\r
-/* FUNCTIONS *****************************************************************/\r
-\r
-/*++\r
- * RtlpCreateCriticalSectionSem\r
- *\r
- *     Checks if an Event has been created for the critical section.\r
- *\r
- * Params:\r
- *     None\r
- *\r
- * Returns:\r
- *     None. Raises an exception if the system call failed.\r
- *\r
- * Remarks:\r
- *     None\r
- *\r
- *--*/\r
-VOID\r
-NTAPI\r
-RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-    HANDLE hEvent = CriticalSection->LockSemaphore;\r
-    HANDLE hNewEvent;\r
-    NTSTATUS Status;\r
-\r
-    /* Chevk if we have an event */\r
-    if (!hEvent) {\r
-\r
-        /* No, so create it */\r
-        if (!NT_SUCCESS(Status = NtCreateEvent(&hNewEvent,\r
-                                               EVENT_ALL_ACCESS,\r
-                                               NULL,\r
-                                               SynchronizationEvent,\r
-                                               FALSE))) {\r
-\r
-                /* We failed, this is bad... */\r
-                DPRINT1("Failed to Create Event!\n");\r
-                InterlockedDecrement(&CriticalSection->LockCount);\r
-                RtlRaiseStatus(Status);\r
-                return;\r
-        }\r
-        DPRINT("Created Event: %p \n", hNewEvent);\r
-\r
-        if ((hEvent = InterlockedCompareExchangePointer((PVOID*)&CriticalSection->LockSemaphore,\r
-                                                         (PVOID)hNewEvent,\r
-                                                         0))) {\r
-\r
-            /* Some just created an event */\r
-            DPRINT("Closing already created event: %p\n", hNewEvent);\r
-            NtClose(hNewEvent);\r
-        }\r
-    }\r
-\r
-    return;\r
-}\r
-\r
-/*++\r
- * RtlpWaitForCriticalSection\r
- *\r
- *     Slow path of RtlEnterCriticalSection. Waits on an Event Object.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to acquire.\r
- *\r
- * Returns:\r
- *     STATUS_SUCCESS, or raises an exception if a deadlock is occuring.\r
- *\r
- * Remarks:\r
- *     None\r
- *\r
- *--*/\r
-NTSTATUS\r
-NTAPI\r
-RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-    NTSTATUS Status;\r
-    EXCEPTION_RECORD ExceptionRecord;\r
-    BOOLEAN LastChance = FALSE;\r
-    LARGE_INTEGER Timeout;\r
-\r
-    /* Wait 2.5 minutes */\r
-    Timeout.QuadPart = 150000L * (ULONGLONG)10000;\r
-    Timeout.QuadPart = -Timeout.QuadPart;\r
-    /* ^^ HACK HACK HACK. Good way:\r
-    Timeout = &NtCurrentPeb()->CriticalSectionTimeout   */\r
-\r
-    /* Do we have an Event yet? */\r
-    if (!CriticalSection->LockSemaphore) {\r
-        RtlpCreateCriticalSectionSem(CriticalSection);\r
-    }\r
-\r
-    /* Increase the Debug Entry count */\r
-    DPRINT("Waiting on Critical Section Event: %p %p\n",\r
-            CriticalSection,\r
-            CriticalSection->LockSemaphore);\r
-    CriticalSection->DebugInfo->EntryCount++;\r
-\r
-    for (;;) {\r
-\r
-        /* Increase the number of times we've had contention */\r
-        CriticalSection->DebugInfo->ContentionCount++;\r
-\r
-        /* Wait on the Event */\r
-        Status = NtWaitForSingleObject(CriticalSection->LockSemaphore,\r
-                                       FALSE,\r
-                                       &Timeout);\r
-\r
-        /* We have Timed out */\r
-        if (Status == STATUS_TIMEOUT) {\r
-\r
-            /* Is this the 2nd time we've timed out? */\r
-            if (LastChance) {\r
-\r
-                DPRINT1("Deadlock: %p\n", CriticalSection);\r
-\r
-                /* Yes it is, we are raising an exception */\r
-                ExceptionRecord.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;\r
-                ExceptionRecord.ExceptionFlags   = 0;\r
-                ExceptionRecord.ExceptionRecord  = NULL;\r
-                ExceptionRecord.ExceptionAddress = RtlRaiseException;\r
-                ExceptionRecord.NumberParameters = 1;\r
-                ExceptionRecord.ExceptionInformation[0] = (ULONG_PTR)CriticalSection;\r
-                RtlRaiseException(&ExceptionRecord);\r
-\r
-            }\r
-\r
-            /* One more try */\r
-            LastChance = TRUE;\r
-\r
-        } else {\r
-\r
-            /* If we are here, everything went fine */\r
-            return STATUS_SUCCESS;\r
-        }\r
-    }\r
-}\r
-\r
-/*++\r
- * RtlpUnWaitCriticalSection\r
- *\r
- *     Slow path of RtlLeaveCriticalSection. Fires an Event Object.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to release.\r
- *\r
- * Returns:\r
- *     None. Raises an exception if the system call failed.\r
- *\r
- * Remarks:\r
- *     None\r
- *\r
- *--*/\r
-VOID\r
-NTAPI\r
-RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-    NTSTATUS Status;\r
-\r
-    /* Do we have an Event yet? */\r
-    if (!CriticalSection->LockSemaphore) {\r
-        RtlpCreateCriticalSectionSem(CriticalSection);\r
-    }\r
-\r
-    /* Signal the Event */\r
-    DPRINT("Signaling Critical Section Event: %p, %p\n",\r
-            CriticalSection,\r
-            CriticalSection->LockSemaphore);\r
-    Status = NtSetEvent(CriticalSection->LockSemaphore, NULL);\r
-\r
-    if (!NT_SUCCESS(Status)) {\r
-\r
-        /* We've failed */\r
-        DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",\r
-                CriticalSection,\r
-                CriticalSection->LockSemaphore,\r
-               Status);\r
-        RtlRaiseStatus(Status);\r
-    }\r
-}\r
-\r
-/*++\r
- * RtlpInitDeferedCriticalSection\r
- *\r
- *     Initializes the Critical Section implementation.\r
- *\r
- * Params:\r
- *     None\r
- *\r
- * Returns:\r
- *     None.\r
- *\r
- * Remarks:\r
- *     After this call, the Process Critical Section list is protected.\r
- *\r
- *--*/\r
-VOID\r
-NTAPI\r
-RtlpInitDeferedCriticalSection(VOID)\r
-{\r
-\r
-    /* Initialize the Process Critical Section List */\r
-    InitializeListHead(&RtlCriticalSectionList);\r
-\r
-    /* Initialize the CS Protecting the List */\r
-    RtlInitializeCriticalSection(&RtlCriticalSectionLock);\r
-\r
-    /* It's now safe to enter it */\r
-    RtlpCritSectInitialized = TRUE;\r
-}\r
-\r
-/*++\r
- * RtlpAllocateDebugInfo\r
- *\r
- *     Finds or allocates memory for a Critical Section Debug Object\r
- *\r
- * Params:\r
- *     None\r
- *\r
- * Returns:\r
- *     A pointer to an empty Critical Section Debug Object.\r
- *\r
- * Remarks:\r
- *     For optimization purposes, the first 64 entries can be cached. From\r
- *     then on, future Critical Sections will allocate memory from the heap.\r
- *\r
- *--*/\r
-PRTL_CRITICAL_SECTION_DEBUG\r
-NTAPI\r
-RtlpAllocateDebugInfo(VOID)\r
-{\r
-    ULONG i;\r
-\r
-    /* Try to allocate from our buffer first */\r
-    for (i = 0; i < MAX_STATIC_CS_DEBUG_OBJECTS; i++) {\r
-\r
-        /* Check if Entry is free */\r
-        if (!RtlpDebugInfoFreeList[i]) {\r
-\r
-            /* Mark entry in use */\r
-            DPRINT("Using entry: %lu. Buffer: %p\n", i, &RtlpStaticDebugInfo[i]);\r
-            RtlpDebugInfoFreeList[i] = TRUE;\r
-\r
-            /* Use free entry found */\r
-            return &RtlpStaticDebugInfo[i];\r
-        }\r
-\r
-    }\r
-\r
-    /* We are out of static buffer, allocate dynamic */\r
-    return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap,\r
-                           0,\r
-                           sizeof(RTL_CRITICAL_SECTION_DEBUG));\r
-}\r
-\r
-/*++\r
- * RtlpFreeDebugInfo\r
- *\r
- *     Frees the memory for a Critical Section Debug Object\r
- *\r
- * Params:\r
- *     DebugInfo - Pointer to Critical Section Debug Object to free.\r
- *\r
- * Returns:\r
- *     None.\r
- *\r
- * Remarks:\r
- *     If the pointer is part of the static buffer, then the entry is made\r
- *     free again. If not, the object is de-allocated from the heap.\r
- *\r
- *--*/\r
-VOID\r
-NTAPI\r
-RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo)\r
-{\r
-    ULONG EntryId;\r
-\r
-    /* Is it part of our cached entries? */\r
-    if ((DebugInfo >= RtlpStaticDebugInfo) &&\r
-        (DebugInfo <= &RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS-1])) {\r
-\r
-        /* Yes. zero it out */\r
-        RtlZeroMemory(DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));\r
-\r
-        /* Mark as free */\r
-        EntryId = (DebugInfo - RtlpStaticDebugInfo);\r
-        DPRINT("Freeing from Buffer: %p. Entry: %lu inside Process: %p\n",\r
-               DebugInfo,\r
-               EntryId,\r
-               NtCurrentTeb()->Cid.UniqueProcess);\r
-        RtlpDebugInfoFreeList[EntryId] = FALSE;\r
-\r
-    } else {\r
-\r
-        /* It's a dynamic one, so free from the heap */\r
-        DPRINT("Freeing from Heap: %p inside Process: %p\n",\r
-               DebugInfo,\r
-               NtCurrentTeb()->Cid.UniqueProcess);\r
-        RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, DebugInfo);\r
-\r
-    }\r
-}\r
-\r
-/*++\r
- * RtlDeleteCriticalSection\r
- * @implemented NT4\r
- *\r
- *     Deletes a Critical Section\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to delete.\r
- *\r
- * Returns:\r
- *     STATUS_SUCCESS, or error value returned by NtClose.\r
- *\r
- * Remarks:\r
- *     The critical section members should not be read after this call.\r
- *\r
- *--*/\r
-NTSTATUS\r
-NTAPI\r
-RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-    NTSTATUS Status = STATUS_SUCCESS;\r
-\r
-    DPRINT("Deleting Critical Section: %p\n", CriticalSection);\r
-    /* Close the Event Object Handle if it exists */\r
-    if (CriticalSection->LockSemaphore) {\r
-\r
-        /* In case NtClose fails, return the status */\r
-        Status = NtClose(CriticalSection->LockSemaphore);\r
-\r
-    }\r
-\r
-    /* Protect List */\r
-    RtlEnterCriticalSection(&RtlCriticalSectionLock);\r
-\r
-    /* Remove it from the list */\r
-    RemoveEntryList(&CriticalSection->DebugInfo->ProcessLocksList);\r
-\r
-    /* Unprotect */\r
-    RtlLeaveCriticalSection(&RtlCriticalSectionLock);\r
-\r
-    /* Free it */\r
-    RtlpFreeDebugInfo(CriticalSection->DebugInfo);\r
-\r
-    /* Wipe it out */\r
-    RtlZeroMemory(CriticalSection, sizeof(RTL_CRITICAL_SECTION));\r
-\r
-    /* Return */\r
-    return Status;\r
-}\r
-\r
-/*++\r
- * RtlSetCriticalSectionSpinCount\r
- * @implemented NT4\r
- *\r
- *     Sets the spin count for a critical section.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to set the spin count for.\r
- *\r
- *     SpinCount - Spin count for the critical section.\r
- *\r
- * Returns:\r
- *     STATUS_SUCCESS.\r
- *\r
- * Remarks:\r
- *     SpinCount is ignored on single-processor systems.\r
- *\r
- *--*/\r
-DWORD\r
-NTAPI\r
-RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection,\r
-                               ULONG SpinCount)\r
-{\r
-    ULONG OldCount = CriticalSection->SpinCount;\r
-\r
-    /* Set to parameter if MP, or to 0 if this is Uniprocessor */\r
-    CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;\r
-    return OldCount;\r
-}\r
-\r
-/*++\r
- * RtlEnterCriticalSection\r
- * @implemented NT4\r
- *\r
- *     Waits to gain ownership of the critical section.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to wait for.\r
- *\r
- * Returns:\r
- *     STATUS_SUCCESS.\r
- *\r
- * Remarks:\r
- *     Uses a fast-path unless contention happens.\r
- *\r
- *--*/\r
-NTSTATUS\r
-NTAPI\r
-RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-    HANDLE Thread = (HANDLE)NtCurrentTeb()->Cid.UniqueThread;\r
-\r
-    /* Try to Lock it */\r
-    if (InterlockedIncrement(&CriticalSection->LockCount) != 0) {\r
-\r
-        /*\r
-         * We've failed to lock it! Does this thread\r
-         * actually own it?\r
-         */\r
-        if (Thread == CriticalSection->OwningThread) {\r
-\r
-            /* You own it, so you'll get it when you're done with it! No need to\r
-               use the interlocked functions as only the thread who already owns\r
-               the lock can modify this data. */\r
-            CriticalSection->RecursionCount++;\r
-            return STATUS_SUCCESS;\r
-        }\r
-\r
-        /* NOTE - CriticalSection->OwningThread can be NULL here because changing\r
-                  this information is not serialized. This happens when thread a\r
-                  acquires the lock (LockCount == 0) and thread b tries to\r
-                  acquire it as well (LockCount == 1) but thread a hasn't had a\r
-                  chance to set the OwningThread! So it's not an error when\r
-                  OwningThread is NULL here! */\r
-\r
-        /* We don't own it, so we must wait for it */\r
-        RtlpWaitForCriticalSection(CriticalSection);\r
-    }\r
-\r
-    /* Lock successful. Changing this information has not to be serialized because\r
-       only one thread at a time can actually change it (the one who acquired\r
-       the lock)! */\r
-    CriticalSection->OwningThread = Thread;\r
-    CriticalSection->RecursionCount = 1;\r
-    return STATUS_SUCCESS;\r
-}\r
-\r
-/*++\r
- * RtlInitializeCriticalSection\r
- * @implemented NT4\r
- *\r
- *     Initialises a new critical section.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to initialise\r
- *\r
- * Returns:\r
- *     STATUS_SUCCESS.\r
- *\r
- * Remarks:\r
- *     Simply calls RtlInitializeCriticalSectionAndSpinCount\r
- *\r
- *--*/\r
-NTSTATUS\r
-NTAPI\r
-RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-    /* Call the Main Function */\r
-    return RtlInitializeCriticalSectionAndSpinCount(CriticalSection, 0);\r
-}\r
-\r
-/*++\r
- * RtlInitializeCriticalSectionAndSpinCount\r
- * @implemented NT4\r
- *\r
- *     Initialises a new critical section.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to initialise\r
- *\r
- *     SpinCount - Spin count for the critical section.\r
- *\r
- * Returns:\r
- *     STATUS_SUCCESS.\r
- *\r
- * Remarks:\r
- *     SpinCount is ignored on single-processor systems.\r
- *\r
- *--*/\r
-NTSTATUS\r
-NTAPI\r
-RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection,\r
-                                         ULONG SpinCount)\r
-{\r
-    PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData;\r
-\r
-    /* First things first, set up the Object */\r
-    DPRINT("Initializing Critical Section: %p\n", CriticalSection);\r
-    CriticalSection->LockCount = -1;\r
-    CriticalSection->RecursionCount = 0;\r
-    CriticalSection->OwningThread = 0;\r
-    CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;\r
-    CriticalSection->LockSemaphore = 0;\r
-\r
-    /* Allocate the Debug Data */\r
-    CritcalSectionDebugData = RtlpAllocateDebugInfo();\r
-    DPRINT("Allocated Debug Data: %p inside Process: %p\n",\r
-           CritcalSectionDebugData,\r
-           NtCurrentTeb()->Cid.UniqueProcess);\r
-\r
-    if (!CritcalSectionDebugData) {\r
-\r
-        /* This is bad! */\r
-        DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection);\r
-        return STATUS_NO_MEMORY;\r
-    }\r
-\r
-    /* Set it up */\r
-    CritcalSectionDebugData->Type = RTL_CRITSECT_TYPE;\r
-    CritcalSectionDebugData->ContentionCount = 0;\r
-    CritcalSectionDebugData->EntryCount = 0;\r
-    CritcalSectionDebugData->CriticalSection = CriticalSection;\r
-    CriticalSection->DebugInfo = CritcalSectionDebugData;\r
-\r
-    /*\r
-    * Add it to the List of Critical Sections owned by the process.\r
-    * If we've initialized the Lock, then use it. If not, then probably\r
-    * this is the lock initialization itself, so insert it directly.\r
-    */\r
-    if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized)) {\r
-\r
-        DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",\r
-               &CritcalSectionDebugData->ProcessLocksList,\r
-               CriticalSection,\r
-               &RtlCriticalSectionList);\r
-\r
-        /* Protect List */\r
-        RtlEnterCriticalSection(&RtlCriticalSectionLock);\r
-\r
-        /* Add this one */\r
-        InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);\r
-\r
-        /* Unprotect */\r
-        RtlLeaveCriticalSection(&RtlCriticalSectionLock);\r
-\r
-    } else {\r
-\r
-        DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",\r
-               &CritcalSectionDebugData->ProcessLocksList,\r
-               CriticalSection,\r
-               &RtlCriticalSectionList);\r
-\r
-        /* Add it directly */\r
-        InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);\r
-    }\r
-\r
-    return STATUS_SUCCESS;\r
-}\r
-\r
-/*++\r
- * RtlLeaveCriticalSection\r
- * @implemented NT4\r
- *\r
- *     Releases a critical section and makes if available for new owners.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to release.\r
- *\r
- * Returns:\r
- *     STATUS_SUCCESS.\r
- *\r
- * Remarks:\r
- *     If another thread was waiting, the slow path is entered.\r
- *\r
- *--*/\r
-NTSTATUS\r
-NTAPI\r
-RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-#ifndef NDEBUG\r
-    HANDLE Thread = (HANDLE)NtCurrentTeb()->Cid.UniqueThread;\r
-\r
-    /* In win this case isn't checked. However it's a valid check so it should only\r
-       be performed in debug builds! */\r
-    if (Thread != CriticalSection->OwningThread)\r
-    {\r
-       DPRINT1("Releasing critical section not owned!\n");\r
-       return STATUS_INVALID_PARAMETER;\r
-    }\r
-#endif\r
-\r
-    /* Decrease the Recursion Count. No need to do this atomically because only\r
-       the thread who holds the lock can call this function (unless the program\r
-       is totally screwed... */\r
-    if (--CriticalSection->RecursionCount) {\r
-\r
-        /* Someone still owns us, but we are free. This needs to be done atomically. */\r
-        InterlockedDecrement(&CriticalSection->LockCount);\r
-\r
-    } else {\r
-\r
-         /* Nobody owns us anymore. No need to do this atomically. See comment\r
-            above. */\r
-        CriticalSection->OwningThread = 0;\r
-\r
-        /* Was someone wanting us? This needs to be done atomically. */\r
-        if (-1 != InterlockedDecrement(&CriticalSection->LockCount)) {\r
-\r
-            /* Let him have us */\r
-            RtlpUnWaitCriticalSection(CriticalSection);\r
-        }\r
-    }\r
-\r
-    /* Sucessful! */\r
-    return STATUS_SUCCESS;\r
-}\r
-\r
-/*++\r
- * RtlTryEnterCriticalSection\r
- * @implemented NT4\r
- *\r
- *     Attemps to gain ownership of the critical section without waiting.\r
- *\r
- * Params:\r
- *     CriticalSection - Critical section to attempt acquiring.\r
- *\r
- * Returns:\r
- *     TRUE if the critical section has been acquired, FALSE otherwise.\r
- *\r
- * Remarks:\r
- *     None\r
- *\r
- *--*/\r
-BOOLEAN\r
-NTAPI\r
-RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)\r
-{\r
-    /* Try to take control */\r
-    if (InterlockedCompareExchange(&CriticalSection->LockCount,\r
-                                   0,\r
-                                   -1) == -1) {\r
-\r
-        /* It's ours */\r
-        CriticalSection->OwningThread =  NtCurrentTeb()->Cid.UniqueThread;\r
-        CriticalSection->RecursionCount = 1;\r
-        return TRUE;\r
-\r
-   } else if (CriticalSection->OwningThread == NtCurrentTeb()->Cid.UniqueThread) {\r
-\r
-        /* It's already ours */\r
-        InterlockedIncrement(&CriticalSection->LockCount);\r
-        CriticalSection->RecursionCount++;\r
-        return TRUE;\r
-    }\r
-\r
-    /* It's not ours */\r
-    return FALSE;\r
-}\r
-\r
-/* EOF */\r
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS system libraries
+ * FILE:            lib/rtl/critical.c
+ * PURPOSE:         Critical sections
+ * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
+ *                  Gunnar Dalsnes
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include <rtl.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#define MAX_STATIC_CS_DEBUG_OBJECTS 64
+
+static RTL_CRITICAL_SECTION RtlCriticalSectionLock;
+static LIST_ENTRY RtlCriticalSectionList;
+static BOOLEAN RtlpCritSectInitialized = FALSE;
+static RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS];
+static BOOLEAN RtlpDebugInfoFreeList[MAX_STATIC_CS_DEBUG_OBJECTS];
+
+/* FUNCTIONS *****************************************************************/
+
+/*++
+ * RtlpCreateCriticalSectionSem
+ *
+ *     Checks if an Event has been created for the critical section.
+ *
+ * Params:
+ *     None
+ *
+ * Returns:
+ *     None. Raises an exception if the system call failed.
+ *
+ * Remarks:
+ *     None
+ *
+ *--*/
+VOID
+NTAPI
+RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection)
+{
+    HANDLE hEvent = CriticalSection->LockSemaphore;
+    HANDLE hNewEvent;
+    NTSTATUS Status;
+
+    /* Chevk if we have an event */
+    if (!hEvent) {
+
+        /* No, so create it */
+        if (!NT_SUCCESS(Status = NtCreateEvent(&hNewEvent,
+                                               EVENT_ALL_ACCESS,
+                                               NULL,
+                                               SynchronizationEvent,
+                                               FALSE))) {
+
+                /* We failed, this is bad... */
+                DPRINT1("Failed to Create Event!\n");
+                InterlockedDecrement(&CriticalSection->LockCount);
+                RtlRaiseStatus(Status);
+                return;
+        }
+        DPRINT("Created Event: %p \n", hNewEvent);
+
+        if ((hEvent = InterlockedCompareExchangePointer((PVOID*)&CriticalSection->LockSemaphore,
+                                                         (PVOID)hNewEvent,
+                                                         0))) {
+
+            /* Some just created an event */
+            DPRINT("Closing already created event: %p\n", hNewEvent);
+            NtClose(hNewEvent);
+        }
+    }
+
+    return;
+}
+
+/*++
+ * RtlpWaitForCriticalSection
+ *
+ *     Slow path of RtlEnterCriticalSection. Waits on an Event Object.
+ *
+ * Params:
+ *     CriticalSection - Critical section to acquire.
+ *
+ * Returns:
+ *     STATUS_SUCCESS, or raises an exception if a deadlock is occuring.
+ *
+ * Remarks:
+ *     None
+ *
+ *--*/
+NTSTATUS
+NTAPI
+RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
+{
+    NTSTATUS Status;
+    EXCEPTION_RECORD ExceptionRecord;
+    BOOLEAN LastChance = FALSE;
+    LARGE_INTEGER Timeout;
+
+    /* Wait 2.5 minutes */
+    Timeout.QuadPart = 150000L * (ULONGLONG)10000;
+    Timeout.QuadPart = -Timeout.QuadPart;
+    /* ^^ HACK HACK HACK. Good way:
+    Timeout = &NtCurrentPeb()->CriticalSectionTimeout   */
+
+    /* Do we have an Event yet? */
+    if (!CriticalSection->LockSemaphore) {
+        RtlpCreateCriticalSectionSem(CriticalSection);
+    }
+
+    /* Increase the Debug Entry count */
+    DPRINT("Waiting on Critical Section Event: %p %p\n",
+            CriticalSection,
+            CriticalSection->LockSemaphore);
+    CriticalSection->DebugInfo->EntryCount++;
+
+    for (;;) {
+
+        /* Increase the number of times we've had contention */
+        CriticalSection->DebugInfo->ContentionCount++;
+
+        /* Wait on the Event */
+        Status = NtWaitForSingleObject(CriticalSection->LockSemaphore,
+                                       FALSE,
+                                       &Timeout);
+
+        /* We have Timed out */
+        if (Status == STATUS_TIMEOUT) {
+
+            /* Is this the 2nd time we've timed out? */
+            if (LastChance) {
+
+                DPRINT1("Deadlock: %p\n", CriticalSection);
+
+                /* Yes it is, we are raising an exception */
+                ExceptionRecord.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
+                ExceptionRecord.ExceptionFlags   = 0;
+                ExceptionRecord.ExceptionRecord  = NULL;
+                ExceptionRecord.ExceptionAddress = RtlRaiseException;
+                ExceptionRecord.NumberParameters = 1;
+                ExceptionRecord.ExceptionInformation[0] = (ULONG_PTR)CriticalSection;
+                RtlRaiseException(&ExceptionRecord);
+
+            }
+
+            /* One more try */
+            LastChance = TRUE;
+
+        } else {
+
+            /* If we are here, everything went fine */
+            return STATUS_SUCCESS;
+        }
+    }
+}
+
+/*++
+ * RtlpUnWaitCriticalSection
+ *
+ *     Slow path of RtlLeaveCriticalSection. Fires an Event Object.
+ *
+ * Params:
+ *     CriticalSection - Critical section to release.
+ *
+ * Returns:
+ *     None. Raises an exception if the system call failed.
+ *
+ * Remarks:
+ *     None
+ *
+ *--*/
+VOID
+NTAPI
+RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
+{
+    NTSTATUS Status;
+
+    /* Do we have an Event yet? */
+    if (!CriticalSection->LockSemaphore) {
+        RtlpCreateCriticalSectionSem(CriticalSection);
+    }
+
+    /* Signal the Event */
+    DPRINT("Signaling Critical Section Event: %p, %p\n",
+            CriticalSection,
+            CriticalSection->LockSemaphore);
+    Status = NtSetEvent(CriticalSection->LockSemaphore, NULL);
+
+    if (!NT_SUCCESS(Status)) {
+
+        /* We've failed */
+        DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",
+                CriticalSection,
+                CriticalSection->LockSemaphore,
+               Status);
+        RtlRaiseStatus(Status);
+    }
+}
+
+/*++
+ * RtlpInitDeferedCriticalSection
+ *
+ *     Initializes the Critical Section implementation.
+ *
+ * Params:
+ *     None
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     After this call, the Process Critical Section list is protected.
+ *
+ *--*/
+VOID
+NTAPI
+RtlpInitDeferedCriticalSection(VOID)
+{
+
+    /* Initialize the Process Critical Section List */
+    InitializeListHead(&RtlCriticalSectionList);
+
+    /* Initialize the CS Protecting the List */
+    RtlInitializeCriticalSection(&RtlCriticalSectionLock);
+
+    /* It's now safe to enter it */
+    RtlpCritSectInitialized = TRUE;
+}
+
+/*++
+ * RtlpAllocateDebugInfo
+ *
+ *     Finds or allocates memory for a Critical Section Debug Object
+ *
+ * Params:
+ *     None
+ *
+ * Returns:
+ *     A pointer to an empty Critical Section Debug Object.
+ *
+ * Remarks:
+ *     For optimization purposes, the first 64 entries can be cached. From
+ *     then on, future Critical Sections will allocate memory from the heap.
+ *
+ *--*/
+PRTL_CRITICAL_SECTION_DEBUG
+NTAPI
+RtlpAllocateDebugInfo(VOID)
+{
+    ULONG i;
+
+    /* Try to allocate from our buffer first */
+    for (i = 0; i < MAX_STATIC_CS_DEBUG_OBJECTS; i++) {
+
+        /* Check if Entry is free */
+        if (!RtlpDebugInfoFreeList[i]) {
+
+            /* Mark entry in use */
+            DPRINT("Using entry: %lu. Buffer: %p\n", i, &RtlpStaticDebugInfo[i]);
+            RtlpDebugInfoFreeList[i] = TRUE;
+
+            /* Use free entry found */
+            return &RtlpStaticDebugInfo[i];
+        }
+
+    }
+
+    /* We are out of static buffer, allocate dynamic */
+    return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap,
+                           0,
+                           sizeof(RTL_CRITICAL_SECTION_DEBUG));
+}
+
+/*++
+ * RtlpFreeDebugInfo
+ *
+ *     Frees the memory for a Critical Section Debug Object
+ *
+ * Params:
+ *     DebugInfo - Pointer to Critical Section Debug Object to free.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     If the pointer is part of the static buffer, then the entry is made
+ *     free again. If not, the object is de-allocated from the heap.
+ *
+ *--*/
+VOID
+NTAPI
+RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo)
+{
+    ULONG EntryId;
+
+    /* Is it part of our cached entries? */
+    if ((DebugInfo >= RtlpStaticDebugInfo) &&
+        (DebugInfo <= &RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS-1])) {
+
+        /* Yes. zero it out */
+        RtlZeroMemory(DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));
+
+        /* Mark as free */
+        EntryId = (DebugInfo - RtlpStaticDebugInfo);
+        DPRINT("Freeing from Buffer: %p. Entry: %lu inside Process: %p\n",
+               DebugInfo,
+               EntryId,
+               NtCurrentTeb()->Cid.UniqueProcess);
+        RtlpDebugInfoFreeList[EntryId] = FALSE;
+
+    } else {
+
+        /* It's a dynamic one, so free from the heap */
+        DPRINT("Freeing from Heap: %p inside Process: %p\n",
+               DebugInfo,
+               NtCurrentTeb()->Cid.UniqueProcess);
+        RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, DebugInfo);
+
+    }
+}
+
+/*++
+ * RtlDeleteCriticalSection
+ * @implemented NT4
+ *
+ *     Deletes a Critical Section
+ *
+ * Params:
+ *     CriticalSection - Critical section to delete.
+ *
+ * Returns:
+ *     STATUS_SUCCESS, or error value returned by NtClose.
+ *
+ * Remarks:
+ *     The critical section members should not be read after this call.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    DPRINT("Deleting Critical Section: %p\n", CriticalSection);
+    /* Close the Event Object Handle if it exists */
+    if (CriticalSection->LockSemaphore) {
+
+        /* In case NtClose fails, return the status */
+        Status = NtClose(CriticalSection->LockSemaphore);
+
+    }
+
+    /* Protect List */
+    RtlEnterCriticalSection(&RtlCriticalSectionLock);
+
+    /* Remove it from the list */
+    RemoveEntryList(&CriticalSection->DebugInfo->ProcessLocksList);
+
+    /* Unprotect */
+    RtlLeaveCriticalSection(&RtlCriticalSectionLock);
+
+    /* Free it */
+    RtlpFreeDebugInfo(CriticalSection->DebugInfo);
+
+    /* Wipe it out */
+    RtlZeroMemory(CriticalSection, sizeof(RTL_CRITICAL_SECTION));
+
+    /* Return */
+    return Status;
+}
+
+/*++
+ * RtlSetCriticalSectionSpinCount
+ * @implemented NT4
+ *
+ *     Sets the spin count for a critical section.
+ *
+ * Params:
+ *     CriticalSection - Critical section to set the spin count for.
+ *
+ *     SpinCount - Spin count for the critical section.
+ *
+ * Returns:
+ *     STATUS_SUCCESS.
+ *
+ * Remarks:
+ *     SpinCount is ignored on single-processor systems.
+ *
+ *--*/
+DWORD
+NTAPI
+RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
+                               ULONG SpinCount)
+{
+    ULONG OldCount = CriticalSection->SpinCount;
+
+    /* Set to parameter if MP, or to 0 if this is Uniprocessor */
+    CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
+    return OldCount;
+}
+
+/*++
+ * RtlEnterCriticalSection
+ * @implemented NT4
+ *
+ *     Waits to gain ownership of the critical section.
+ *
+ * Params:
+ *     CriticalSection - Critical section to wait for.
+ *
+ * Returns:
+ *     STATUS_SUCCESS.
+ *
+ * Remarks:
+ *     Uses a fast-path unless contention happens.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
+{
+    HANDLE Thread = (HANDLE)NtCurrentTeb()->Cid.UniqueThread;
+
+    /* Try to Lock it */
+    if (InterlockedIncrement(&CriticalSection->LockCount) != 0) {
+
+        /*
+         * We've failed to lock it! Does this thread
+         * actually own it?
+         */
+        if (Thread == CriticalSection->OwningThread) {
+
+            /* You own it, so you'll get it when you're done with it! No need to
+               use the interlocked functions as only the thread who already owns
+               the lock can modify this data. */
+            CriticalSection->RecursionCount++;
+            return STATUS_SUCCESS;
+        }
+
+        /* NOTE - CriticalSection->OwningThread can be NULL here because changing
+                  this information is not serialized. This happens when thread a
+                  acquires the lock (LockCount == 0) and thread b tries to
+                  acquire it as well (LockCount == 1) but thread a hasn't had a
+                  chance to set the OwningThread! So it's not an error when
+                  OwningThread is NULL here! */
+
+        /* We don't own it, so we must wait for it */
+        RtlpWaitForCriticalSection(CriticalSection);
+    }
+
+    /* Lock successful. Changing this information has not to be serialized because
+       only one thread at a time can actually change it (the one who acquired
+       the lock)! */
+    CriticalSection->OwningThread = Thread;
+    CriticalSection->RecursionCount = 1;
+    return STATUS_SUCCESS;
+}
+
+/*++
+ * RtlInitializeCriticalSection
+ * @implemented NT4
+ *
+ *     Initialises a new critical section.
+ *
+ * Params:
+ *     CriticalSection - Critical section to initialise
+ *
+ * Returns:
+ *     STATUS_SUCCESS.
+ *
+ * Remarks:
+ *     Simply calls RtlInitializeCriticalSectionAndSpinCount
+ *
+ *--*/
+NTSTATUS
+NTAPI
+RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
+{
+    /* Call the Main Function */
+    return RtlInitializeCriticalSectionAndSpinCount(CriticalSection, 0);
+}
+
+/*++
+ * RtlInitializeCriticalSectionAndSpinCount
+ * @implemented NT4
+ *
+ *     Initialises a new critical section.
+ *
+ * Params:
+ *     CriticalSection - Critical section to initialise
+ *
+ *     SpinCount - Spin count for the critical section.
+ *
+ * Returns:
+ *     STATUS_SUCCESS.
+ *
+ * Remarks:
+ *     SpinCount is ignored on single-processor systems.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
+                                         ULONG SpinCount)
+{
+    PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData;
+
+    /* First things first, set up the Object */
+    DPRINT("Initializing Critical Section: %p\n", CriticalSection);
+    CriticalSection->LockCount = -1;
+    CriticalSection->RecursionCount = 0;
+    CriticalSection->OwningThread = 0;
+    CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
+    CriticalSection->LockSemaphore = 0;
+
+    /* Allocate the Debug Data */
+    CritcalSectionDebugData = RtlpAllocateDebugInfo();
+    DPRINT("Allocated Debug Data: %p inside Process: %p\n",
+           CritcalSectionDebugData,
+           NtCurrentTeb()->Cid.UniqueProcess);
+
+    if (!CritcalSectionDebugData) {
+
+        /* This is bad! */
+        DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection);
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Set it up */
+    CritcalSectionDebugData->Type = RTL_CRITSECT_TYPE;
+    CritcalSectionDebugData->ContentionCount = 0;
+    CritcalSectionDebugData->EntryCount = 0;
+    CritcalSectionDebugData->CriticalSection = CriticalSection;
+    CriticalSection->DebugInfo = CritcalSectionDebugData;
+
+    /*
+    * Add it to the List of Critical Sections owned by the process.
+    * If we've initialized the Lock, then use it. If not, then probably
+    * this is the lock initialization itself, so insert it directly.
+    */
+    if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized)) {
+
+        DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
+               &CritcalSectionDebugData->ProcessLocksList,
+               CriticalSection,
+               &RtlCriticalSectionList);
+
+        /* Protect List */
+        RtlEnterCriticalSection(&RtlCriticalSectionLock);
+
+        /* Add this one */
+        InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
+
+        /* Unprotect */
+        RtlLeaveCriticalSection(&RtlCriticalSectionLock);
+
+    } else {
+
+        DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
+               &CritcalSectionDebugData->ProcessLocksList,
+               CriticalSection,
+               &RtlCriticalSectionList);
+
+        /* Add it directly */
+        InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*++
+ * RtlLeaveCriticalSection
+ * @implemented NT4
+ *
+ *     Releases a critical section and makes if available for new owners.
+ *
+ * Params:
+ *     CriticalSection - Critical section to release.
+ *
+ * Returns:
+ *     STATUS_SUCCESS.
+ *
+ * Remarks:
+ *     If another thread was waiting, the slow path is entered.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
+{
+#ifndef NDEBUG
+    HANDLE Thread = (HANDLE)NtCurrentTeb()->Cid.UniqueThread;
+
+    /* In win this case isn't checked. However it's a valid check so it should only
+       be performed in debug builds! */
+    if (Thread != CriticalSection->OwningThread)
+    {
+       DPRINT1("Releasing critical section not owned!\n");
+       return STATUS_INVALID_PARAMETER;
+    }
+#endif
+
+    /* Decrease the Recursion Count. No need to do this atomically because only
+       the thread who holds the lock can call this function (unless the program
+       is totally screwed... */
+    if (--CriticalSection->RecursionCount) {
+
+        /* Someone still owns us, but we are free. This needs to be done atomically. */
+        InterlockedDecrement(&CriticalSection->LockCount);
+
+    } else {
+
+         /* Nobody owns us anymore. No need to do this atomically. See comment
+            above. */
+        CriticalSection->OwningThread = 0;
+
+        /* Was someone wanting us? This needs to be done atomically. */
+        if (-1 != InterlockedDecrement(&CriticalSection->LockCount)) {
+
+            /* Let him have us */
+            RtlpUnWaitCriticalSection(CriticalSection);
+        }
+    }
+
+    /* Sucessful! */
+    return STATUS_SUCCESS;
+}
+
+/*++
+ * RtlTryEnterCriticalSection
+ * @implemented NT4
+ *
+ *     Attemps to gain ownership of the critical section without waiting.
+ *
+ * Params:
+ *     CriticalSection - Critical section to attempt acquiring.
+ *
+ * Returns:
+ *     TRUE if the critical section has been acquired, FALSE otherwise.
+ *
+ * Remarks:
+ *     None
+ *
+ *--*/
+BOOLEAN
+NTAPI
+RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
+{
+    /* Try to take control */
+    if (InterlockedCompareExchange(&CriticalSection->LockCount,
+                                   0,
+                                   -1) == -1) {
+
+        /* It's ours */
+        CriticalSection->OwningThread =  NtCurrentTeb()->Cid.UniqueThread;
+        CriticalSection->RecursionCount = 1;
+        return TRUE;
+
+   } else if (CriticalSection->OwningThread == NtCurrentTeb()->Cid.UniqueThread) {
+
+        /* It's already ours */
+        InterlockedIncrement(&CriticalSection->LockCount);
+        CriticalSection->RecursionCount++;
+        return TRUE;
+    }
+
+    /* It's not ours */
+    return FALSE;
+}
+
+/* EOF */