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
];
25 /* FUNCTIONS *****************************************************************/
28 * RtlpCreateCriticalSectionSem
30 * Checks if an Event has been created for the critical section.
36 * None. Raises an exception if the system call failed.
44 RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection
)
46 HANDLE hEvent
= CriticalSection
->LockSemaphore
;
50 /* Chevk if we have an event */
53 /* No, so create it */
54 if (!NT_SUCCESS(Status
= NtCreateEvent(&hNewEvent
,
60 /* We failed, this is bad... */
61 DPRINT1("Failed to Create Event!\n");
62 _InterlockedDecrement(&CriticalSection
->LockCount
);
63 RtlRaiseStatus(Status
);
66 DPRINT("Created Event: %p \n", hNewEvent
);
68 if ((hEvent
= (HANDLE
)_InterlockedCompareExchange((PLONG
)&CriticalSection
->LockSemaphore
,
72 /* Some just created an event */
73 DPRINT("Closing already created event: %p\n", hNewEvent
);
82 * RtlpWaitForCriticalSection
84 * Slow path of RtlEnterCriticalSection. Waits on an Event Object.
87 * CriticalSection - Critical section to acquire.
90 * STATUS_SUCCESS, or raises an exception if a deadlock is occuring.
98 RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
101 EXCEPTION_RECORD ExceptionRecord
;
102 BOOLEAN LastChance
= FALSE
;
103 LARGE_INTEGER Timeout
;
105 /* Wait 2.5 minutes */
106 Timeout
.QuadPart
= 150000L * (ULONGLONG
)10000;
107 Timeout
.QuadPart
= -Timeout
.QuadPart
;
108 /* ^^ HACK HACK HACK. Good way:
109 Timeout = &NtCurrentPeb()->CriticalSectionTimeout */
111 /* Do we have an Event yet? */
112 if (!CriticalSection
->LockSemaphore
) {
113 RtlpCreateCriticalSectionSem(CriticalSection
);
116 /* Increase the Debug Entry count */
117 DPRINT("Waiting on Critical Section Event: %p %p\n",
119 CriticalSection
->LockSemaphore
);
120 CriticalSection
->DebugInfo
->EntryCount
++;
124 /* Increase the number of times we've had contention */
125 CriticalSection
->DebugInfo
->ContentionCount
++;
127 /* Wait on the Event */
128 Status
= NtWaitForSingleObject(CriticalSection
->LockSemaphore
,
132 /* We have Timed out */
133 if (Status
== STATUS_TIMEOUT
) {
135 /* Is this the 2nd time we've timed out? */
138 DPRINT1("Deadlock: %p\n", CriticalSection
);
140 /* Yes it is, we are raising an exception */
141 ExceptionRecord
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
142 ExceptionRecord
.ExceptionFlags
= 0;
143 ExceptionRecord
.ExceptionRecord
= NULL
;
144 ExceptionRecord
.ExceptionAddress
= RtlRaiseException
;
145 ExceptionRecord
.NumberParameters
= 1;
146 ExceptionRecord
.ExceptionInformation
[0] = (ULONG_PTR
)CriticalSection
;
147 RtlRaiseException(&ExceptionRecord
);
156 /* If we are here, everything went fine */
157 return STATUS_SUCCESS
;
163 * RtlpUnWaitCriticalSection
165 * Slow path of RtlLeaveCriticalSection. Fires an Event Object.
168 * CriticalSection - Critical section to release.
171 * None. Raises an exception if the system call failed.
179 RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
183 /* Do we have an Event yet? */
184 if (!CriticalSection
->LockSemaphore
) {
185 RtlpCreateCriticalSectionSem(CriticalSection
);
188 /* Signal the Event */
189 DPRINT("Signaling Critical Section Event: %p, %p\n",
191 CriticalSection
->LockSemaphore
);
192 Status
= NtSetEvent(CriticalSection
->LockSemaphore
, NULL
);
194 if (!NT_SUCCESS(Status
)) {
197 DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",
199 CriticalSection
->LockSemaphore
,
201 RtlRaiseStatus(Status
);
206 * RtlpInitDeferedCriticalSection
208 * Initializes the Critical Section implementation.
217 * After this call, the Process Critical Section list is protected.
222 RtlpInitDeferedCriticalSection(VOID
)
225 /* Initialize the Process Critical Section List */
226 InitializeListHead(&RtlCriticalSectionList
);
228 /* Initialize the CS Protecting the List */
229 RtlInitializeCriticalSection(&RtlCriticalSectionLock
);
231 /* It's now safe to enter it */
232 RtlpCritSectInitialized
= TRUE
;
236 * RtlpAllocateDebugInfo
238 * Finds or allocates memory for a Critical Section Debug Object
244 * A pointer to an empty Critical Section Debug Object.
247 * For optimization purposes, the first 64 entries can be cached. From
248 * then on, future Critical Sections will allocate memory from the heap.
251 PRTL_CRITICAL_SECTION_DEBUG
253 RtlpAllocateDebugInfo(VOID
)
257 /* Try to allocate from our buffer first */
258 for (i
= 0; i
< MAX_STATIC_CS_DEBUG_OBJECTS
; i
++) {
260 /* Check if Entry is free */
261 if (!RtlpDebugInfoFreeList
[i
]) {
263 /* Mark entry in use */
264 DPRINT("Using entry: %lu. Buffer: %p\n", i
, &RtlpStaticDebugInfo
[i
]);
265 RtlpDebugInfoFreeList
[i
] = TRUE
;
267 /* Use free entry found */
268 return &RtlpStaticDebugInfo
[i
];
273 /* We are out of static buffer, allocate dynamic */
274 return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap
,
276 sizeof(RTL_CRITICAL_SECTION_DEBUG
));
282 * Frees the memory for a Critical Section Debug Object
285 * DebugInfo - Pointer to Critical Section Debug Object to free.
291 * If the pointer is part of the static buffer, then the entry is made
292 * free again. If not, the object is de-allocated from the heap.
297 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo
)
301 /* Is it part of our cached entries? */
302 if ((DebugInfo
>= RtlpStaticDebugInfo
) &&
303 (DebugInfo
<= &RtlpStaticDebugInfo
[MAX_STATIC_CS_DEBUG_OBJECTS
-1])) {
305 /* Yes. zero it out */
306 RtlZeroMemory(DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
309 EntryId
= (DebugInfo
- RtlpStaticDebugInfo
);
310 DPRINT("Freeing from Buffer: %p. Entry: %lu inside Process: %p\n",
313 NtCurrentTeb()->Cid
.UniqueProcess
);
314 RtlpDebugInfoFreeList
[EntryId
] = FALSE
;
318 /* It's a dynamic one, so free from the heap */
319 DPRINT("Freeing from Heap: %p inside Process: %p\n",
321 NtCurrentTeb()->Cid
.UniqueProcess
);
322 RtlFreeHeap(NtCurrentPeb()->ProcessHeap
, 0, DebugInfo
);
328 * RtlDeleteCriticalSection
331 * Deletes a Critical Section
334 * CriticalSection - Critical section to delete.
337 * STATUS_SUCCESS, or error value returned by NtClose.
340 * The critical section members should not be read after this call.
345 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
347 NTSTATUS Status
= STATUS_SUCCESS
;
349 DPRINT("Deleting Critical Section: %p\n", CriticalSection
);
350 /* Close the Event Object Handle if it exists */
351 if (CriticalSection
->LockSemaphore
) {
353 /* In case NtClose fails, return the status */
354 Status
= NtClose(CriticalSection
->LockSemaphore
);
359 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
361 /* Remove it from the list */
362 RemoveEntryList(&CriticalSection
->DebugInfo
->ProcessLocksList
);
365 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
368 RtlpFreeDebugInfo(CriticalSection
->DebugInfo
);
371 RtlZeroMemory(CriticalSection
, sizeof(RTL_CRITICAL_SECTION
));
378 * RtlSetCriticalSectionSpinCount
381 * Sets the spin count for a critical section.
384 * CriticalSection - Critical section to set the spin count for.
386 * SpinCount - Spin count for the critical section.
392 * SpinCount is ignored on single-processor systems.
397 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
400 ULONG OldCount
= CriticalSection
->SpinCount
;
402 /* Set to parameter if MP, or to 0 if this is Uniprocessor */
403 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
408 * RtlEnterCriticalSection
411 * Waits to gain ownership of the critical section.
414 * CriticalSection - Critical section to wait for.
420 * Uses a fast-path unless contention happens.
425 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
427 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->Cid
.UniqueThread
;
430 if (_InterlockedIncrement(&CriticalSection
->LockCount
) != 0) {
433 * We've failed to lock it! Does this thread
436 if (Thread
== CriticalSection
->OwningThread
) {
438 /* You own it, so you'll get it when you're done with it! No need to
439 use the interlocked functions as only the thread who already owns
440 the lock can modify this data. */
441 CriticalSection
->RecursionCount
++;
442 return STATUS_SUCCESS
;
445 /* NOTE - CriticalSection->OwningThread can be NULL here because changing
446 this information is not serialized. This happens when thread a
447 acquires the lock (LockCount == 0) and thread b tries to
448 acquire it as well (LockCount == 1) but thread a hasn't had a
449 chance to set the OwningThread! So it's not an error when
450 OwningThread is NULL here! */
452 /* We don't own it, so we must wait for it */
453 RtlpWaitForCriticalSection(CriticalSection
);
456 /* Lock successful. Changing this information has not to be serialized because
457 only one thread at a time can actually change it (the one who acquired
459 CriticalSection
->OwningThread
= Thread
;
460 CriticalSection
->RecursionCount
= 1;
461 return STATUS_SUCCESS
;
465 * RtlInitializeCriticalSection
468 * Initialises a new critical section.
471 * CriticalSection - Critical section to initialise
477 * Simply calls RtlInitializeCriticalSectionAndSpinCount
482 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
484 /* Call the Main Function */
485 return RtlInitializeCriticalSectionAndSpinCount(CriticalSection
, 0);
489 * RtlInitializeCriticalSectionAndSpinCount
492 * Initialises a new critical section.
495 * CriticalSection - Critical section to initialise
497 * SpinCount - Spin count for the critical section.
503 * SpinCount is ignored on single-processor systems.
508 RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
511 PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData
;
513 /* First things first, set up the Object */
514 DPRINT("Initializing Critical Section: %p\n", CriticalSection
);
515 CriticalSection
->LockCount
= -1;
516 CriticalSection
->RecursionCount
= 0;
517 CriticalSection
->OwningThread
= 0;
518 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
519 CriticalSection
->LockSemaphore
= 0;
521 /* Allocate the Debug Data */
522 CritcalSectionDebugData
= RtlpAllocateDebugInfo();
523 DPRINT("Allocated Debug Data: %p inside Process: %p\n",
524 CritcalSectionDebugData
,
525 NtCurrentTeb()->Cid
.UniqueProcess
);
527 if (!CritcalSectionDebugData
) {
530 DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection
);
531 return STATUS_NO_MEMORY
;
535 CritcalSectionDebugData
->Type
= RTL_CRITSECT_TYPE
;
536 CritcalSectionDebugData
->ContentionCount
= 0;
537 CritcalSectionDebugData
->EntryCount
= 0;
538 CritcalSectionDebugData
->CriticalSection
= CriticalSection
;
539 CriticalSection
->DebugInfo
= CritcalSectionDebugData
;
542 * Add it to the List of Critical Sections owned by the process.
543 * If we've initialized the Lock, then use it. If not, then probably
544 * this is the lock initialization itself, so insert it directly.
546 if ((CriticalSection
!= &RtlCriticalSectionLock
) && (RtlpCritSectInitialized
)) {
548 DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
549 &CritcalSectionDebugData
->ProcessLocksList
,
551 &RtlCriticalSectionList
);
554 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
557 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
560 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
564 DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
565 &CritcalSectionDebugData
->ProcessLocksList
,
567 &RtlCriticalSectionList
);
569 /* Add it directly */
570 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
573 return STATUS_SUCCESS
;
577 * RtlLeaveCriticalSection
580 * Releases a critical section and makes if available for new owners.
583 * CriticalSection - Critical section to release.
589 * If another thread was waiting, the slow path is entered.
594 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
597 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->Cid
.UniqueThread
;
599 /* In win this case isn't checked. However it's a valid check so it should only
600 be performed in debug builds! */
601 if (Thread
!= CriticalSection
->OwningThread
)
603 DPRINT1("Releasing critical section not owned!\n");
604 return STATUS_INVALID_PARAMETER
;
608 /* Decrease the Recursion Count. No need to do this atomically because only
609 the thread who holds the lock can call this function (unless the program
610 is totally screwed... */
611 if (--CriticalSection
->RecursionCount
) {
613 /* Someone still owns us, but we are free. This needs to be done atomically. */
614 _InterlockedDecrement(&CriticalSection
->LockCount
);
618 /* Nobody owns us anymore. No need to do this atomically. See comment
620 CriticalSection
->OwningThread
= 0;
622 /* Was someone wanting us? This needs to be done atomically. */
623 if (-1 != _InterlockedDecrement(&CriticalSection
->LockCount
)) {
625 /* Let him have us */
626 RtlpUnWaitCriticalSection(CriticalSection
);
631 return STATUS_SUCCESS
;
635 * RtlTryEnterCriticalSection
638 * Attemps to gain ownership of the critical section without waiting.
641 * CriticalSection - Critical section to attempt acquiring.
644 * TRUE if the critical section has been acquired, FALSE otherwise.
652 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
654 /* Try to take control */
655 if (_InterlockedCompareExchange(&CriticalSection
->LockCount
,
660 CriticalSection
->OwningThread
= NtCurrentTeb()->Cid
.UniqueThread
;
661 CriticalSection
->RecursionCount
= 1;
664 } else if (CriticalSection
->OwningThread
== NtCurrentTeb()->Cid
.UniqueThread
) {
666 /* It's already ours */
667 _InterlockedIncrement(&CriticalSection
->LockCount
);
668 CriticalSection
->RecursionCount
++;