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
)_InterlockedCompareExchangePointer((PVOID
*)&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
);
121 if (CriticalSection
->DebugInfo
)
122 CriticalSection
->DebugInfo
->EntryCount
++;
126 /* Increase the number of times we've had contention */
127 if (CriticalSection
->DebugInfo
)
128 CriticalSection
->DebugInfo
->ContentionCount
++;
130 /* Wait on the Event */
131 Status
= NtWaitForSingleObject(CriticalSection
->LockSemaphore
,
135 /* We have Timed out */
136 if (Status
== STATUS_TIMEOUT
) {
138 /* Is this the 2nd time we've timed out? */
141 DPRINT1("Deadlock: %p\n", CriticalSection
);
143 /* Yes it is, we are raising an exception */
144 ExceptionRecord
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
145 ExceptionRecord
.ExceptionFlags
= 0;
146 ExceptionRecord
.ExceptionRecord
= NULL
;
147 ExceptionRecord
.ExceptionAddress
= RtlRaiseException
;
148 ExceptionRecord
.NumberParameters
= 1;
149 ExceptionRecord
.ExceptionInformation
[0] = (ULONG_PTR
)CriticalSection
;
150 RtlRaiseException(&ExceptionRecord
);
159 /* If we are here, everything went fine */
160 return STATUS_SUCCESS
;
166 * RtlpUnWaitCriticalSection
168 * Slow path of RtlLeaveCriticalSection. Fires an Event Object.
171 * CriticalSection - Critical section to release.
174 * None. Raises an exception if the system call failed.
182 RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
186 /* Do we have an Event yet? */
187 if (!CriticalSection
->LockSemaphore
) {
188 RtlpCreateCriticalSectionSem(CriticalSection
);
191 /* Signal the Event */
192 DPRINT("Signaling Critical Section Event: %p, %p\n",
194 CriticalSection
->LockSemaphore
);
195 Status
= NtSetEvent(CriticalSection
->LockSemaphore
, NULL
);
197 if (!NT_SUCCESS(Status
)) {
200 DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",
202 CriticalSection
->LockSemaphore
,
204 RtlRaiseStatus(Status
);
209 * RtlpInitDeferedCriticalSection
211 * Initializes the Critical Section implementation.
220 * After this call, the Process Critical Section list is protected.
225 RtlpInitDeferedCriticalSection(VOID
)
228 /* Initialize the Process Critical Section List */
229 InitializeListHead(&RtlCriticalSectionList
);
231 /* Initialize the CS Protecting the List */
232 RtlInitializeCriticalSection(&RtlCriticalSectionLock
);
234 /* It's now safe to enter it */
235 RtlpCritSectInitialized
= TRUE
;
239 * RtlpAllocateDebugInfo
241 * Finds or allocates memory for a Critical Section Debug Object
247 * A pointer to an empty Critical Section Debug Object.
250 * For optimization purposes, the first 64 entries can be cached. From
251 * then on, future Critical Sections will allocate memory from the heap.
254 PRTL_CRITICAL_SECTION_DEBUG
256 RtlpAllocateDebugInfo(VOID
)
260 /* Try to allocate from our buffer first */
261 for (i
= 0; i
< MAX_STATIC_CS_DEBUG_OBJECTS
; i
++) {
263 /* Check if Entry is free */
264 if (!RtlpDebugInfoFreeList
[i
]) {
266 /* Mark entry in use */
267 DPRINT("Using entry: %lu. Buffer: %p\n", i
, &RtlpStaticDebugInfo
[i
]);
268 RtlpDebugInfoFreeList
[i
] = TRUE
;
270 /* Use free entry found */
271 return &RtlpStaticDebugInfo
[i
];
276 /* We are out of static buffer, allocate dynamic */
277 return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap
,
279 sizeof(RTL_CRITICAL_SECTION_DEBUG
));
285 * Frees the memory for a Critical Section Debug Object
288 * DebugInfo - Pointer to Critical Section Debug Object to free.
294 * If the pointer is part of the static buffer, then the entry is made
295 * free again. If not, the object is de-allocated from the heap.
300 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo
)
304 /* Is it part of our cached entries? */
305 if ((DebugInfo
>= RtlpStaticDebugInfo
) &&
306 (DebugInfo
<= &RtlpStaticDebugInfo
[MAX_STATIC_CS_DEBUG_OBJECTS
-1])) {
308 /* Yes. zero it out */
309 RtlZeroMemory(DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
312 EntryId
= (DebugInfo
- RtlpStaticDebugInfo
);
313 DPRINT("Freeing from Buffer: %p. Entry: %lu inside Process: %p\n",
316 NtCurrentTeb()->ClientId
.UniqueProcess
);
317 RtlpDebugInfoFreeList
[EntryId
] = FALSE
;
321 /* It's a dynamic one, so free from the heap */
322 DPRINT("Freeing from Heap: %p inside Process: %p\n",
324 NtCurrentTeb()->ClientId
.UniqueProcess
);
325 RtlFreeHeap(NtCurrentPeb()->ProcessHeap
, 0, DebugInfo
);
331 * RtlDeleteCriticalSection
334 * Deletes a Critical Section
337 * CriticalSection - Critical section to delete.
340 * STATUS_SUCCESS, or error value returned by NtClose.
343 * The critical section members should not be read after this call.
348 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
350 NTSTATUS Status
= STATUS_SUCCESS
;
352 DPRINT("Deleting Critical Section: %p\n", CriticalSection
);
353 /* Close the Event Object Handle if it exists */
354 if (CriticalSection
->LockSemaphore
) {
356 /* In case NtClose fails, return the status */
357 Status
= NtClose(CriticalSection
->LockSemaphore
);
362 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
364 if (CriticalSection
->DebugInfo
)
366 /* Remove it from the list */
367 RemoveEntryList(&CriticalSection
->DebugInfo
->ProcessLocksList
);
368 RtlZeroMemory(CriticalSection
->DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
372 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
374 if (CriticalSection
->DebugInfo
)
377 RtlpFreeDebugInfo(CriticalSection
->DebugInfo
);
381 RtlZeroMemory(CriticalSection
, sizeof(RTL_CRITICAL_SECTION
));
388 * RtlSetCriticalSectionSpinCount
391 * Sets the spin count for a critical section.
394 * CriticalSection - Critical section to set the spin count for.
396 * SpinCount - Spin count for the critical section.
402 * SpinCount is ignored on single-processor systems.
407 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
410 ULONG OldCount
= CriticalSection
->SpinCount
;
412 /* Set to parameter if MP, or to 0 if this is Uniprocessor */
413 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
418 * RtlEnterCriticalSection
421 * Waits to gain ownership of the critical section.
424 * CriticalSection - Critical section to wait for.
430 * Uses a fast-path unless contention happens.
435 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
437 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->ClientId
.UniqueThread
;
440 if (_InterlockedIncrement(&CriticalSection
->LockCount
) != 0) {
443 * We've failed to lock it! Does this thread
446 if (Thread
== CriticalSection
->OwningThread
) {
448 /* You own it, so you'll get it when you're done with it! No need to
449 use the interlocked functions as only the thread who already owns
450 the lock can modify this data. */
451 CriticalSection
->RecursionCount
++;
452 return STATUS_SUCCESS
;
455 /* NOTE - CriticalSection->OwningThread can be NULL here because changing
456 this information is not serialized. This happens when thread a
457 acquires the lock (LockCount == 0) and thread b tries to
458 acquire it as well (LockCount == 1) but thread a hasn't had a
459 chance to set the OwningThread! So it's not an error when
460 OwningThread is NULL here! */
462 /* We don't own it, so we must wait for it */
463 RtlpWaitForCriticalSection(CriticalSection
);
466 /* Lock successful. Changing this information has not to be serialized because
467 only one thread at a time can actually change it (the one who acquired
469 CriticalSection
->OwningThread
= Thread
;
470 CriticalSection
->RecursionCount
= 1;
471 return STATUS_SUCCESS
;
475 * RtlInitializeCriticalSection
478 * Initialises a new critical section.
481 * CriticalSection - Critical section to initialise
487 * Simply calls RtlInitializeCriticalSectionAndSpinCount
492 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
494 /* Call the Main Function */
495 return RtlInitializeCriticalSectionAndSpinCount(CriticalSection
, 0);
499 * RtlInitializeCriticalSectionAndSpinCount
502 * Initialises a new critical section.
505 * CriticalSection - Critical section to initialise
507 * SpinCount - Spin count for the critical section.
513 * SpinCount is ignored on single-processor systems.
518 RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection
,
521 PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData
;
523 /* First things first, set up the Object */
524 DPRINT("Initializing Critical Section: %p\n", CriticalSection
);
525 CriticalSection
->LockCount
= -1;
526 CriticalSection
->RecursionCount
= 0;
527 CriticalSection
->OwningThread
= 0;
528 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
529 CriticalSection
->LockSemaphore
= 0;
531 /* Allocate the Debug Data */
532 CritcalSectionDebugData
= RtlpAllocateDebugInfo();
533 DPRINT("Allocated Debug Data: %p inside Process: %p\n",
534 CritcalSectionDebugData
,
535 NtCurrentTeb()->ClientId
.UniqueProcess
);
537 if (!CritcalSectionDebugData
) {
540 DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection
);
541 return STATUS_NO_MEMORY
;
545 CritcalSectionDebugData
->Type
= RTL_CRITSECT_TYPE
;
546 CritcalSectionDebugData
->ContentionCount
= 0;
547 CritcalSectionDebugData
->EntryCount
= 0;
548 CritcalSectionDebugData
->CriticalSection
= CriticalSection
;
549 CriticalSection
->DebugInfo
= CritcalSectionDebugData
;
552 * Add it to the List of Critical Sections owned by the process.
553 * If we've initialized the Lock, then use it. If not, then probably
554 * this is the lock initialization itself, so insert it directly.
556 if ((CriticalSection
!= &RtlCriticalSectionLock
) && (RtlpCritSectInitialized
)) {
558 DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
559 &CritcalSectionDebugData
->ProcessLocksList
,
561 &RtlCriticalSectionList
);
564 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
567 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
570 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
574 DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
575 &CritcalSectionDebugData
->ProcessLocksList
,
577 &RtlCriticalSectionList
);
579 /* Add it directly */
580 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
583 return STATUS_SUCCESS
;
587 * RtlLeaveCriticalSection
590 * Releases a critical section and makes if available for new owners.
593 * CriticalSection - Critical section to release.
599 * If another thread was waiting, the slow path is entered.
604 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
607 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->Cid
.UniqueThread
;
609 /* In win this case isn't checked. However it's a valid check so it should only
610 be performed in debug builds! */
611 if (Thread
!= CriticalSection
->OwningThread
)
613 DPRINT1("Releasing critical section not owned!\n");
614 return STATUS_INVALID_PARAMETER
;
618 /* Decrease the Recursion Count. No need to do this atomically because only
619 the thread who holds the lock can call this function (unless the program
620 is totally screwed... */
621 if (--CriticalSection
->RecursionCount
) {
623 /* Someone still owns us, but we are free. This needs to be done atomically. */
624 _InterlockedDecrement(&CriticalSection
->LockCount
);
628 /* Nobody owns us anymore. No need to do this atomically. See comment
630 CriticalSection
->OwningThread
= 0;
632 /* Was someone wanting us? This needs to be done atomically. */
633 if (-1 != _InterlockedDecrement(&CriticalSection
->LockCount
)) {
635 /* Let him have us */
636 RtlpUnWaitCriticalSection(CriticalSection
);
641 return STATUS_SUCCESS
;
645 * RtlTryEnterCriticalSection
648 * Attemps to gain ownership of the critical section without waiting.
651 * CriticalSection - Critical section to attempt acquiring.
654 * TRUE if the critical section has been acquired, FALSE otherwise.
662 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection
)
664 /* Try to take control */
665 if (_InterlockedCompareExchange(&CriticalSection
->LockCount
,
670 CriticalSection
->OwningThread
= NtCurrentTeb()->ClientId
.UniqueThread
;
671 CriticalSection
->RecursionCount
= 1;
674 } else if (CriticalSection
->OwningThread
== NtCurrentTeb()->ClientId
.UniqueThread
) {
676 /* It's already ours */
677 _InterlockedIncrement(&CriticalSection
->LockCount
);
678 CriticalSection
->RecursionCount
++;