-/*\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 */