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)
10 /* INCLUDES *****************************************************************/
17 #define MAX_STATIC_CS_DEBUG_OBJECTS 64
19 static RTL_CRITICAL_SECTION RtlCriticalSectionLock
;
20 static LIST_ENTRY RtlCriticalSectionList
;
21 static BOOLEAN RtlpCritSectInitialized
= FALSE
;
22 static RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo
[MAX_STATIC_CS_DEBUG_OBJECTS
];
23 static BOOLEAN RtlpDebugInfoFreeList
[MAX_STATIC_CS_DEBUG_OBJECTS
];
24 LARGE_INTEGER RtlpTimeout
;
26 /* FUNCTIONS *****************************************************************/
29 * RtlpCreateCriticalSectionSem
31 * Checks if an Event has been created for the critical section.
37 * None. Raises an exception if the system call failed.
43 _At_(CriticalSection
->LockSemaphore
, _Post_notnull_
)
46 RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection
)
48 HANDLE hEvent
= CriticalSection
->LockSemaphore
;
52 /* Check if we have an event */
56 /* No, so create it */
57 Status
= NtCreateEvent(&hNewEvent
,
62 if (!NT_SUCCESS(Status
))
64 DPRINT1("Failed to Create Event!\n");
66 /* Use INVALID_HANDLE_VALUE (-1) to signal that the global
67 keyed event must be used */
68 hNewEvent
= INVALID_HANDLE_VALUE
;
71 DPRINT("Created Event: %p \n", hNewEvent
);
73 /* Exchange the LockSemaphore field with the new handle, if it is still 0 */
74 if (InterlockedCompareExchangePointer((PVOID
*)&CriticalSection
->LockSemaphore
,
78 /* Someone else just created an event */
79 if (hEvent
!= INVALID_HANDLE_VALUE
)
81 DPRINT("Closing already created event: %p\n", hNewEvent
);
91 * RtlpWaitForCriticalSection
93 * Slow path of RtlEnterCriticalSection. Waits on an Event Object.
96 * CriticalSection - Critical section to acquire.
99 * STATUS_SUCCESS, or raises an exception if a deadlock is occuring.
107 RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
110 EXCEPTION_RECORD ExceptionRecord
;
111 BOOLEAN LastChance
= FALSE
;
113 /* Do we have an Event yet? */
114 if (!CriticalSection
->LockSemaphore
) {
115 RtlpCreateCriticalSectionSem(CriticalSection
);
118 /* Increase the Debug Entry count */
119 DPRINT("Waiting on Critical Section Event: %p %p\n",
121 CriticalSection
->LockSemaphore
);
123 if (CriticalSection
->DebugInfo
)
124 CriticalSection
->DebugInfo
->EntryCount
++;
128 /* Increase the number of times we've had contention */
129 if (CriticalSection
->DebugInfo
)
130 CriticalSection
->DebugInfo
->ContentionCount
++;
132 /* Check if allocating the event failed */
133 if (CriticalSection
->LockSemaphore
== INVALID_HANDLE_VALUE
)
135 /* Use the global keyed event (NULL as keyed event handle) */
136 Status
= NtWaitForKeyedEvent(NULL
,
143 /* Wait on the Event */
144 Status
= NtWaitForSingleObject(CriticalSection
->LockSemaphore
,
149 /* We have Timed out */
150 if (Status
== STATUS_TIMEOUT
) {
152 /* Is this the 2nd time we've timed out? */
155 DPRINT1("Deadlock: %p\n", CriticalSection
);
157 /* Yes it is, we are raising an exception */
158 ExceptionRecord
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
159 ExceptionRecord
.ExceptionFlags
= 0;
160 ExceptionRecord
.ExceptionRecord
= NULL
;
161 ExceptionRecord
.ExceptionAddress
= RtlRaiseException
;
162 ExceptionRecord
.NumberParameters
= 1;
163 ExceptionRecord
.ExceptionInformation
[0] = (ULONG_PTR
)CriticalSection
;
164 RtlRaiseException(&ExceptionRecord
);
173 /* If we are here, everything went fine */
174 return STATUS_SUCCESS
;
180 * RtlpUnWaitCriticalSection
182 * Slow path of RtlLeaveCriticalSection. Fires an Event Object.
185 * CriticalSection - Critical section to release.
188 * None. Raises an exception if the system call failed.
196 RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
200 /* Do we have an Event yet? */
201 if (!CriticalSection
->LockSemaphore
) {
202 RtlpCreateCriticalSectionSem(CriticalSection
);
205 /* Signal the Event */
206 DPRINT("Signaling Critical Section Event: %p, %p\n",
208 CriticalSection
->LockSemaphore
);
210 /* Check if this critical section needs to use the keyed event */
211 if (CriticalSection
->LockSemaphore
== INVALID_HANDLE_VALUE
)
213 /* Release keyed event */
214 Status
= NtReleaseKeyedEvent(NULL
, CriticalSection
, FALSE
, &RtlpTimeout
);
219 Status
= NtSetEvent(CriticalSection
->LockSemaphore
, NULL
);
222 if (!NT_SUCCESS(Status
)) {
225 DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",
227 CriticalSection
->LockSemaphore
,
229 RtlRaiseStatus(Status
);
234 * RtlpInitDeferedCriticalSection
236 * Initializes the Critical Section implementation.
245 * After this call, the Process Critical Section list is protected.
250 RtlpInitDeferedCriticalSection(VOID
)
253 /* Initialize the Process Critical Section List */
254 InitializeListHead(&RtlCriticalSectionList
);
256 /* Initialize the CS Protecting the List */
257 RtlInitializeCriticalSection(&RtlCriticalSectionLock
);
259 /* It's now safe to enter it */
260 RtlpCritSectInitialized
= TRUE
;
264 * RtlpAllocateDebugInfo
266 * Finds or allocates memory for a Critical Section Debug Object
272 * A pointer to an empty Critical Section Debug Object.
275 * For optimization purposes, the first 64 entries can be cached. From
276 * then on, future Critical Sections will allocate memory from the heap.
279 PRTL_CRITICAL_SECTION_DEBUG
281 RtlpAllocateDebugInfo(VOID
)
285 /* Try to allocate from our buffer first */
286 for (i
= 0; i
< MAX_STATIC_CS_DEBUG_OBJECTS
; i
++) {
288 /* Check if Entry is free */
289 if (!RtlpDebugInfoFreeList
[i
]) {
291 /* Mark entry in use */
292 DPRINT("Using entry: %lu. Buffer: %p\n", i
, &RtlpStaticDebugInfo
[i
]);
293 RtlpDebugInfoFreeList
[i
] = TRUE
;
295 /* Use free entry found */
296 return &RtlpStaticDebugInfo
[i
];
301 /* We are out of static buffer, allocate dynamic */
302 return RtlAllocateHeap(RtlGetProcessHeap(),
304 sizeof(RTL_CRITICAL_SECTION_DEBUG
));
310 * Frees the memory for a Critical Section Debug Object
313 * DebugInfo - Pointer to Critical Section Debug Object to free.
319 * If the pointer is part of the static buffer, then the entry is made
320 * free again. If not, the object is de-allocated from the heap.
325 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo
)
329 /* Is it part of our cached entries? */
330 if ((DebugInfo
>= RtlpStaticDebugInfo
) &&
331 (DebugInfo
<= &RtlpStaticDebugInfo
[MAX_STATIC_CS_DEBUG_OBJECTS
-1])) {
333 /* Yes. zero it out */
334 RtlZeroMemory(DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
337 EntryId
= (DebugInfo
- RtlpStaticDebugInfo
);
338 DPRINT("Freeing from Buffer: %p. Entry: %Iu inside Process: %p\n",
341 NtCurrentTeb()->ClientId
.UniqueProcess
);
342 RtlpDebugInfoFreeList
[EntryId
] = FALSE
;
344 } else if (!DebugInfo
->Flags
) {
346 /* It's a dynamic one, so free from the heap */
347 DPRINT("Freeing from Heap: %p inside Process: %p\n",
349 NtCurrentTeb()->ClientId
.UniqueProcess
);
350 RtlFreeHeap(NtCurrentPeb()->ProcessHeap
, 0, DebugInfo
);
354 /* Wine stores a section name pointer in the Flags member */
355 DPRINT("Assuming static: %p inside Process: %p\n",
357 NtCurrentTeb()->ClientId
.UniqueProcess
);
363 * RtlDeleteCriticalSection
366 * Deletes a Critical Section
369 * CriticalSection - Critical section to delete.
372 * STATUS_SUCCESS, or error value returned by NtClose.
375 * The critical section members should not be read after this call.
380 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
382 NTSTATUS Status
= STATUS_SUCCESS
;
384 DPRINT("Deleting Critical Section: %p\n", CriticalSection
);
385 /* Close the Event Object Handle if it exists */
386 if (CriticalSection
->LockSemaphore
) {
388 /* In case NtClose fails, return the status */
389 Status
= NtClose(CriticalSection
->LockSemaphore
);
394 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
396 if (CriticalSection
->DebugInfo
)
398 /* Remove it from the list */
399 RemoveEntryList(&CriticalSection
->DebugInfo
->ProcessLocksList
);
400 #if 0 /* We need to preserve Flags for RtlpFreeDebugInfo */
401 RtlZeroMemory(CriticalSection
->DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
406 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
408 if (CriticalSection
->DebugInfo
)
411 RtlpFreeDebugInfo(CriticalSection
->DebugInfo
);
415 RtlZeroMemory(CriticalSection
, sizeof(RTL_CRITICAL_SECTION
));
422 * RtlSetCriticalSectionSpinCount
425 * Sets the spin count for a critical section.
428 * CriticalSection - Critical section to set the spin count for.
430 * SpinCount - Spin count for the critical section.
436 * SpinCount is ignored on single-processor systems.
441 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
444 ULONG OldCount
= (ULONG
)CriticalSection
->SpinCount
;
446 /* Set to parameter if MP, or to 0 if this is Uniprocessor */
447 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
452 * RtlEnterCriticalSection
455 * Waits to gain ownership of the critical section.
458 * CriticalSection - Critical section to wait for.
464 * Uses a fast-path unless contention happens.
469 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
471 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->ClientId
.UniqueThread
;
474 if (InterlockedIncrement(&CriticalSection
->LockCount
) != 0) {
477 * We've failed to lock it! Does this thread
480 if (Thread
== CriticalSection
->OwningThread
) {
482 /* You own it, so you'll get it when you're done with it! No need to
483 use the interlocked functions as only the thread who already owns
484 the lock can modify this data. */
485 CriticalSection
->RecursionCount
++;
486 return STATUS_SUCCESS
;
489 /* NOTE - CriticalSection->OwningThread can be NULL here because changing
490 this information is not serialized. This happens when thread a
491 acquires the lock (LockCount == 0) and thread b tries to
492 acquire it as well (LockCount == 1) but thread a hasn't had a
493 chance to set the OwningThread! So it's not an error when
494 OwningThread is NULL here! */
496 /* We don't own it, so we must wait for it */
497 RtlpWaitForCriticalSection(CriticalSection
);
500 /* Lock successful. Changing this information has not to be serialized because
501 only one thread at a time can actually change it (the one who acquired
503 CriticalSection
->OwningThread
= Thread
;
504 CriticalSection
->RecursionCount
= 1;
505 return STATUS_SUCCESS
;
509 * RtlInitializeCriticalSection
512 * Initialises a new critical section.
515 * CriticalSection - Critical section to initialise
521 * Simply calls RtlInitializeCriticalSectionAndSpinCount
526 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
528 /* Call the Main Function */
529 return RtlInitializeCriticalSectionAndSpinCount(CriticalSection
, 0);
533 * RtlInitializeCriticalSectionAndSpinCount
536 * Initialises a new critical section.
539 * CriticalSection - Critical section to initialise
541 * SpinCount - Spin count for the critical section.
547 * SpinCount is ignored on single-processor systems.
552 RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
555 PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData
;
557 /* First things first, set up the Object */
558 DPRINT("Initializing Critical Section: %p\n", CriticalSection
);
559 CriticalSection
->LockCount
= -1;
560 CriticalSection
->RecursionCount
= 0;
561 CriticalSection
->OwningThread
= 0;
562 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
563 CriticalSection
->LockSemaphore
= 0;
565 /* Allocate the Debug Data */
566 CritcalSectionDebugData
= RtlpAllocateDebugInfo();
567 DPRINT("Allocated Debug Data: %p inside Process: %p\n",
568 CritcalSectionDebugData
,
569 NtCurrentTeb()->ClientId
.UniqueProcess
);
571 if (!CritcalSectionDebugData
) {
574 DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection
);
575 return STATUS_NO_MEMORY
;
579 CritcalSectionDebugData
->Type
= RTL_CRITSECT_TYPE
;
580 CritcalSectionDebugData
->ContentionCount
= 0;
581 CritcalSectionDebugData
->EntryCount
= 0;
582 CritcalSectionDebugData
->CriticalSection
= CriticalSection
;
583 CritcalSectionDebugData
->Flags
= 0;
584 CriticalSection
->DebugInfo
= CritcalSectionDebugData
;
587 * Add it to the List of Critical Sections owned by the process.
588 * If we've initialized the Lock, then use it. If not, then probably
589 * this is the lock initialization itself, so insert it directly.
591 if ((CriticalSection
!= &RtlCriticalSectionLock
) && (RtlpCritSectInitialized
)) {
593 DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
594 &CritcalSectionDebugData
->ProcessLocksList
,
596 &RtlCriticalSectionList
);
599 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
602 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
605 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
609 DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
610 &CritcalSectionDebugData
->ProcessLocksList
,
612 &RtlCriticalSectionList
);
614 /* Add it directly */
615 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
618 return STATUS_SUCCESS
;
622 * RtlGetCriticalSectionRecursionCount
623 * @implemented NT5.2 SP1
625 * Retrieves the recursion count of a given critical section.
628 * CriticalSection - Critical section to retrieve its recursion count.
631 * The recursion count.
634 * We return the recursion count of the critical section if it is owned
635 * by the current thread, and otherwise we return zero.
640 RtlGetCriticalSectionRecursionCount(PRTL_CRITICAL_SECTION CriticalSection
)
642 if (CriticalSection
->OwningThread
== NtCurrentTeb()->ClientId
.UniqueThread
)
645 * The critical section is owned by the current thread,
646 * therefore retrieve its actual recursion count.
648 return CriticalSection
->RecursionCount
;
653 * It is not owned by the current thread, so
654 * for this thread there is no recursion.
661 * RtlLeaveCriticalSection
664 * Releases a critical section and makes if available for new owners.
667 * CriticalSection - Critical section to release.
673 * If another thread was waiting, the slow path is entered.
678 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
681 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->ClientId
.UniqueThread
;
683 /* In win this case isn't checked. However it's a valid check so it should only
684 be performed in debug builds! */
685 if (Thread
!= CriticalSection
->OwningThread
)
687 DPRINT1("Releasing critical section not owned!\n");
688 return STATUS_INVALID_PARAMETER
;
692 /* Decrease the Recursion Count. No need to do this atomically because only
693 the thread who holds the lock can call this function (unless the program
694 is totally screwed... */
695 if (--CriticalSection
->RecursionCount
) {
697 /* Someone still owns us, but we are free. This needs to be done atomically. */
698 InterlockedDecrement(&CriticalSection
->LockCount
);
702 /* Nobody owns us anymore. No need to do this atomically. See comment
704 CriticalSection
->OwningThread
= 0;
706 /* Was someone wanting us? This needs to be done atomically. */
707 if (-1 != InterlockedDecrement(&CriticalSection
->LockCount
)) {
709 /* Let him have us */
710 RtlpUnWaitCriticalSection(CriticalSection
);
715 return STATUS_SUCCESS
;
719 * RtlTryEnterCriticalSection
722 * Attemps to gain ownership of the critical section without waiting.
725 * CriticalSection - Critical section to attempt acquiring.
728 * TRUE if the critical section has been acquired, FALSE otherwise.
736 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
738 /* Try to take control */
739 if (InterlockedCompareExchange(&CriticalSection
->LockCount
,
744 CriticalSection
->OwningThread
= NtCurrentTeb()->ClientId
.UniqueThread
;
745 CriticalSection
->RecursionCount
= 1;
748 } else if (CriticalSection
->OwningThread
== NtCurrentTeb()->ClientId
.UniqueThread
) {
750 /* It's already ours */
751 InterlockedIncrement(&CriticalSection
->LockCount
);
752 CriticalSection
->RecursionCount
++;
762 RtlCheckForOrphanedCriticalSections(