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 extern BOOLEAN LdrpShutdownInProgress
;
27 extern HANDLE LdrpShutdownThreadId
;
29 /* FUNCTIONS *****************************************************************/
32 * RtlpCreateCriticalSectionSem
34 * Checks if an Event has been created for the critical section.
40 * None. Raises an exception if the system call failed.
46 _At_(CriticalSection
->LockSemaphore
, _Post_notnull_
)
49 RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection
)
51 HANDLE hEvent
= CriticalSection
->LockSemaphore
;
55 /* Check if we have an event */
58 /* No, so create it */
59 Status
= NtCreateEvent(&hNewEvent
,
64 if (!NT_SUCCESS(Status
))
66 DPRINT1("Failed to Create Event!\n");
69 * Use INVALID_HANDLE_VALUE (-1) to signal that
70 * the global keyed event must be used.
72 hNewEvent
= INVALID_HANDLE_VALUE
;
75 DPRINT("Created Event: %p \n", hNewEvent
);
77 /* Exchange the LockSemaphore field with the new handle, if it is still 0 */
78 if (InterlockedCompareExchangePointer((PVOID
*)&CriticalSection
->LockSemaphore
,
82 /* Someone else just created an event */
83 if (hEvent
!= INVALID_HANDLE_VALUE
)
85 DPRINT("Closing already created event: %p\n", hNewEvent
);
95 * RtlpWaitForCriticalSection
97 * Slow path of RtlEnterCriticalSection. Waits on an Event Object.
100 * CriticalSection - Critical section to acquire.
103 * STATUS_SUCCESS, or raises an exception if a deadlock is occuring.
111 RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
114 EXCEPTION_RECORD ExceptionRecord
;
115 BOOLEAN LastChance
= FALSE
;
117 /* Do we have an Event yet? */
118 if (!CriticalSection
->LockSemaphore
)
120 RtlpCreateCriticalSectionSem(CriticalSection
);
123 /* Increase the Debug Entry count */
124 DPRINT("Waiting on Critical Section Event: %p %p\n",
126 CriticalSection
->LockSemaphore
);
128 if (CriticalSection
->DebugInfo
)
129 CriticalSection
->DebugInfo
->EntryCount
++;
132 * If we're shutting down the process, we're allowed to acquire any
133 * critical sections by force (the loader lock in particular)
135 if (LdrpShutdownInProgress
&&
136 LdrpShutdownThreadId
== NtCurrentTeb()->RealClientId
.UniqueThread
)
138 DPRINT("Forcing ownership of critical section %p\n", CriticalSection
);
139 CriticalSection
->LockCount
= 0;
140 return STATUS_SUCCESS
;
145 /* Increase the number of times we've had contention */
146 if (CriticalSection
->DebugInfo
)
147 CriticalSection
->DebugInfo
->ContentionCount
++;
149 /* Check if allocating the event failed */
150 if (CriticalSection
->LockSemaphore
== INVALID_HANDLE_VALUE
)
152 /* Use the global keyed event (NULL as keyed event handle) */
153 Status
= NtWaitForKeyedEvent(NULL
,
160 /* Wait on the Event */
161 Status
= NtWaitForSingleObject(CriticalSection
->LockSemaphore
,
166 /* We have Timed out */
167 if (Status
== STATUS_TIMEOUT
)
169 /* Is this the 2nd time we've timed out? */
172 ERROR_DBGBREAK("Deadlock: 0x%p\n", CriticalSection
);
174 /* Yes it is, we are raising an exception */
175 ExceptionRecord
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
176 ExceptionRecord
.ExceptionFlags
= 0;
177 ExceptionRecord
.ExceptionRecord
= NULL
;
178 ExceptionRecord
.ExceptionAddress
= RtlRaiseException
;
179 ExceptionRecord
.NumberParameters
= 1;
180 ExceptionRecord
.ExceptionInformation
[0] = (ULONG_PTR
)CriticalSection
;
181 RtlRaiseException(&ExceptionRecord
);
189 /* If we are here, everything went fine */
190 return STATUS_SUCCESS
;
196 * RtlpUnWaitCriticalSection
198 * Slow path of RtlLeaveCriticalSection. Fires an Event Object.
201 * CriticalSection - Critical section to release.
204 * None. Raises an exception if the system call failed.
212 RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
216 /* Do we have an Event yet? */
217 if (!CriticalSection
->LockSemaphore
)
219 RtlpCreateCriticalSectionSem(CriticalSection
);
222 /* Signal the Event */
223 DPRINT("Signaling Critical Section Event: %p, %p\n",
225 CriticalSection
->LockSemaphore
);
227 /* Check if this critical section needs to use the keyed event */
228 if (CriticalSection
->LockSemaphore
== INVALID_HANDLE_VALUE
)
230 /* Release keyed event */
231 Status
= NtReleaseKeyedEvent(NULL
, CriticalSection
, FALSE
, &RtlpTimeout
);
236 Status
= NtSetEvent(CriticalSection
->LockSemaphore
, NULL
);
239 if (!NT_SUCCESS(Status
))
242 DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",
244 CriticalSection
->LockSemaphore
,
246 RtlRaiseStatus(Status
);
251 * RtlpInitDeferedCriticalSection
253 * Initializes the Critical Section implementation.
262 * After this call, the Process Critical Section list is protected.
267 RtlpInitDeferedCriticalSection(VOID
)
269 /* Initialize the Process Critical Section List */
270 InitializeListHead(&RtlCriticalSectionList
);
272 /* Initialize the CS Protecting the List */
273 RtlInitializeCriticalSection(&RtlCriticalSectionLock
);
275 /* It's now safe to enter it */
276 RtlpCritSectInitialized
= TRUE
;
280 * RtlpAllocateDebugInfo
282 * Finds or allocates memory for a Critical Section Debug Object
288 * A pointer to an empty Critical Section Debug Object.
291 * For optimization purposes, the first 64 entries can be cached. From
292 * then on, future Critical Sections will allocate memory from the heap.
295 PRTL_CRITICAL_SECTION_DEBUG
297 RtlpAllocateDebugInfo(VOID
)
301 /* Try to allocate from our buffer first */
302 for (i
= 0; i
< MAX_STATIC_CS_DEBUG_OBJECTS
; i
++)
304 /* Check if Entry is free */
305 if (!RtlpDebugInfoFreeList
[i
])
307 /* Mark entry in use */
308 DPRINT("Using entry: %lu. Buffer: %p\n", i
, &RtlpStaticDebugInfo
[i
]);
309 RtlpDebugInfoFreeList
[i
] = TRUE
;
311 /* Use free entry found */
312 return &RtlpStaticDebugInfo
[i
];
316 /* We are out of static buffer, allocate dynamic */
317 return RtlAllocateHeap(RtlGetProcessHeap(),
319 sizeof(RTL_CRITICAL_SECTION_DEBUG
));
325 * Frees the memory for a Critical Section Debug Object
328 * DebugInfo - Pointer to Critical Section Debug Object to free.
334 * If the pointer is part of the static buffer, then the entry is made
335 * free again. If not, the object is de-allocated from the heap.
340 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo
)
344 /* Is it part of our cached entries? */
345 if ((DebugInfo
>= RtlpStaticDebugInfo
) &&
346 (DebugInfo
<= &RtlpStaticDebugInfo
[MAX_STATIC_CS_DEBUG_OBJECTS
-1]))
348 /* Yes. zero it out */
349 RtlZeroMemory(DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
352 EntryId
= (DebugInfo
- RtlpStaticDebugInfo
);
353 DPRINT("Freeing from Buffer: %p. Entry: %Iu inside Process: %p\n",
356 NtCurrentTeb()->ClientId
.UniqueProcess
);
357 RtlpDebugInfoFreeList
[EntryId
] = FALSE
;
360 else if (!DebugInfo
->Flags
)
362 /* It's a dynamic one, so free from the heap */
363 DPRINT("Freeing from Heap: %p inside Process: %p\n",
365 NtCurrentTeb()->ClientId
.UniqueProcess
);
366 RtlFreeHeap(NtCurrentPeb()->ProcessHeap
, 0, DebugInfo
);
370 /* Wine stores a section name pointer in the Flags member */
371 DPRINT("Assuming static: %p inside Process: %p\n",
373 NtCurrentTeb()->ClientId
.UniqueProcess
);
378 * RtlDeleteCriticalSection
381 * Deletes a Critical Section
384 * CriticalSection - Critical section to delete.
387 * STATUS_SUCCESS, or error value returned by NtClose.
390 * The critical section members should not be read after this call.
395 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
397 NTSTATUS Status
= STATUS_SUCCESS
;
399 DPRINT("Deleting Critical Section: %p\n", CriticalSection
);
401 /* Close the Event Object Handle if it exists */
402 if (CriticalSection
->LockSemaphore
)
404 /* In case NtClose fails, return the status */
405 Status
= NtClose(CriticalSection
->LockSemaphore
);
409 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
411 if (CriticalSection
->DebugInfo
)
413 /* Remove it from the list */
414 RemoveEntryList(&CriticalSection
->DebugInfo
->ProcessLocksList
);
416 /* We need to preserve Flags for RtlpFreeDebugInfo */
417 RtlZeroMemory(CriticalSection
->DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
422 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
424 if (CriticalSection
->DebugInfo
)
427 RtlpFreeDebugInfo(CriticalSection
->DebugInfo
);
431 RtlZeroMemory(CriticalSection
, sizeof(RTL_CRITICAL_SECTION
));
438 * RtlSetCriticalSectionSpinCount
441 * Sets the spin count for a critical section.
444 * CriticalSection - Critical section to set the spin count for.
446 * SpinCount - Spin count for the critical section.
452 * SpinCount is ignored on single-processor systems.
457 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
460 ULONG OldCount
= (ULONG
)CriticalSection
->SpinCount
;
462 /* Set to parameter if MP, or to 0 if this is Uniprocessor */
463 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
468 * RtlEnterCriticalSection
471 * Waits to gain ownership of the critical section.
474 * CriticalSection - Critical section to wait for.
480 * Uses a fast-path unless contention happens.
485 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
487 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->ClientId
.UniqueThread
;
490 if (InterlockedIncrement(&CriticalSection
->LockCount
) != 0)
492 /* We've failed to lock it! Does this thread actually own it? */
493 if (Thread
== CriticalSection
->OwningThread
)
496 * You own it, so you'll get it when you're done with it! No need to
497 * use the interlocked functions as only the thread who already owns
498 * the lock can modify this data.
500 CriticalSection
->RecursionCount
++;
501 return STATUS_SUCCESS
;
504 /* NOTE - CriticalSection->OwningThread can be NULL here because changing
505 this information is not serialized. This happens when thread a
506 acquires the lock (LockCount == 0) and thread b tries to
507 acquire it as well (LockCount == 1) but thread a hasn't had a
508 chance to set the OwningThread! So it's not an error when
509 OwningThread is NULL here! */
511 /* We don't own it, so we must wait for it */
512 RtlpWaitForCriticalSection(CriticalSection
);
516 * Lock successful. Changing this information has not to be serialized
517 * because only one thread at a time can actually change it (the one who
518 * acquired the lock)!
520 CriticalSection
->OwningThread
= Thread
;
521 CriticalSection
->RecursionCount
= 1;
522 return STATUS_SUCCESS
;
526 * RtlInitializeCriticalSection
529 * Initialises a new critical section.
532 * CriticalSection - Critical section to initialise
538 * Simply calls RtlInitializeCriticalSectionAndSpinCount
543 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
545 /* Call the Main Function */
546 return RtlInitializeCriticalSectionAndSpinCount(CriticalSection
, 0);
550 * RtlInitializeCriticalSectionAndSpinCount
553 * Initialises a new critical section.
556 * CriticalSection - Critical section to initialise
558 * SpinCount - Spin count for the critical section.
564 * SpinCount is ignored on single-processor systems.
569 RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
572 PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData
;
574 /* First things first, set up the Object */
575 DPRINT("Initializing Critical Section: %p\n", CriticalSection
);
576 CriticalSection
->LockCount
= -1;
577 CriticalSection
->RecursionCount
= 0;
578 CriticalSection
->OwningThread
= 0;
579 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
580 CriticalSection
->LockSemaphore
= 0;
582 /* Allocate the Debug Data */
583 CritcalSectionDebugData
= RtlpAllocateDebugInfo();
584 DPRINT("Allocated Debug Data: %p inside Process: %p\n",
585 CritcalSectionDebugData
,
586 NtCurrentTeb()->ClientId
.UniqueProcess
);
588 if (!CritcalSectionDebugData
)
591 DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection
);
592 return STATUS_NO_MEMORY
;
596 CritcalSectionDebugData
->Type
= RTL_CRITSECT_TYPE
;
597 CritcalSectionDebugData
->ContentionCount
= 0;
598 CritcalSectionDebugData
->EntryCount
= 0;
599 CritcalSectionDebugData
->CriticalSection
= CriticalSection
;
600 CritcalSectionDebugData
->Flags
= 0;
601 CriticalSection
->DebugInfo
= CritcalSectionDebugData
;
604 * Add it to the List of Critical Sections owned by the process.
605 * If we've initialized the Lock, then use it. If not, then probably
606 * this is the lock initialization itself, so insert it directly.
608 if ((CriticalSection
!= &RtlCriticalSectionLock
) && (RtlpCritSectInitialized
))
610 DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
611 &CritcalSectionDebugData
->ProcessLocksList
,
613 &RtlCriticalSectionList
);
616 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
619 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
622 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
626 DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
627 &CritcalSectionDebugData
->ProcessLocksList
,
629 &RtlCriticalSectionList
);
631 /* Add it directly */
632 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
635 return STATUS_SUCCESS
;
639 * RtlGetCriticalSectionRecursionCount
640 * @implemented NT5.2 SP1
642 * Retrieves the recursion count of a given critical section.
645 * CriticalSection - Critical section to retrieve its recursion count.
648 * The recursion count.
651 * We return the recursion count of the critical section if it is owned
652 * by the current thread, and otherwise we return zero.
657 RtlGetCriticalSectionRecursionCount(PRTL_CRITICAL_SECTION CriticalSection
)
659 if (CriticalSection
->OwningThread
== NtCurrentTeb()->ClientId
.UniqueThread
)
662 * The critical section is owned by the current thread,
663 * therefore retrieve its actual recursion count.
665 return CriticalSection
->RecursionCount
;
670 * It is not owned by the current thread, so
671 * for this thread there is no recursion.
678 * RtlLeaveCriticalSection
681 * Releases a critical section and makes if available for new owners.
684 * CriticalSection - Critical section to release.
690 * If another thread was waiting, the slow path is entered.
695 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
698 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->ClientId
.UniqueThread
;
701 * In win this case isn't checked. However it's a valid check so it should
702 * only be performed in debug builds!
704 if (Thread
!= CriticalSection
->OwningThread
)
706 DPRINT1("Releasing critical section not owned!\n");
707 return STATUS_INVALID_PARAMETER
;
712 * Decrease the Recursion Count. No need to do this atomically because only
713 * the thread who holds the lock can call this function (unless the program
714 * is totally screwed...
716 if (--CriticalSection
->RecursionCount
)
718 /* Someone still owns us, but we are free. This needs to be done atomically. */
719 InterlockedDecrement(&CriticalSection
->LockCount
);
725 * Nobody owns us anymore. No need to do this atomically.
728 CriticalSection
->OwningThread
= 0;
730 /* Was someone wanting us? This needs to be done atomically. */
731 if (-1 != InterlockedDecrement(&CriticalSection
->LockCount
))
733 /* Let him have us */
734 RtlpUnWaitCriticalSection(CriticalSection
);
739 return STATUS_SUCCESS
;
743 * RtlTryEnterCriticalSection
746 * Attemps to gain ownership of the critical section without waiting.
749 * CriticalSection - Critical section to attempt acquiring.
752 * TRUE if the critical section has been acquired, FALSE otherwise.
760 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
762 /* Try to take control */
763 if (InterlockedCompareExchange(&CriticalSection
->LockCount
, 0, -1) == -1)
766 CriticalSection
->OwningThread
= NtCurrentTeb()->ClientId
.UniqueThread
;
767 CriticalSection
->RecursionCount
= 1;
770 else if (CriticalSection
->OwningThread
== NtCurrentTeb()->ClientId
.UniqueThread
)
772 /* It's already ours */
773 InterlockedIncrement(&CriticalSection
->LockCount
);
774 CriticalSection
->RecursionCount
++;
784 RtlCheckForOrphanedCriticalSections(HANDLE ThreadHandle
)
791 RtlIsCriticalSectionLocked(PRTL_CRITICAL_SECTION CriticalSection
)
793 return CriticalSection
->RecursionCount
!= 0;
798 RtlIsCriticalSectionLockedByThread(PRTL_CRITICAL_SECTION CriticalSection
)
800 return CriticalSection
->OwningThread
== NtCurrentTeb()->ClientId
.UniqueThread
&&
801 CriticalSection
->RecursionCount
!= 0;