3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/ntdll/rtl/critical.c
6 * PURPOSE: Critical sections
9 * Rewritten ROS version, based on WINE code plus
10 * some fixes useful only for ROS right now - 03/01/05
13 /* INCLUDES ******************************************************************/
19 /* FUNCTIONS *****************************************************************/
21 #define MAX_STATIC_CS_DEBUG_OBJECTS 64
23 static RTL_CRITICAL_SECTION RtlCriticalSectionLock
;
24 static LIST_ENTRY RtlCriticalSectionList
;
25 static BOOLEAN RtlpCritSectInitialized
= FALSE
;
26 static RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo
[MAX_STATIC_CS_DEBUG_OBJECTS
];
27 static BOOLEAN RtlpDebugInfoFreeList
[MAX_STATIC_CS_DEBUG_OBJECTS
];
30 * RtlDeleteCriticalSection
33 * Deletes a Critical Section
36 * CriticalSection - Critical section to delete.
39 * STATUS_SUCCESS, or error value returned by NtClose.
42 * The critical section members should not be read after this call.
47 RtlDeleteCriticalSection(
48 PRTL_CRITICAL_SECTION CriticalSection
)
50 NTSTATUS Status
= STATUS_SUCCESS
;
52 DPRINT("Deleting Critical Section: %x\n", CriticalSection
);
53 /* Close the Event Object Handle if it exists */
54 if (CriticalSection
->LockSemaphore
) {
56 /* In case NtClose fails, return the status */
57 Status
= NtClose(CriticalSection
->LockSemaphore
);
62 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
64 /* Remove it from the list */
65 RemoveEntryList(&CriticalSection
->DebugInfo
->ProcessLocksList
);
68 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
71 RtlpFreeDebugInfo(CriticalSection
->DebugInfo
);
74 RtlZeroMemory(CriticalSection
, sizeof(RTL_CRITICAL_SECTION
));
81 * RtlSetCriticalSectionSpinCount
84 * Sets the spin count for a critical section.
87 * CriticalSection - Critical section to set the spin count for.
89 * SpinCount - Spin count for the critical section.
95 * SpinCount is ignored on single-processor systems.
100 RtlSetCriticalSectionSpinCount(
101 PRTL_CRITICAL_SECTION CriticalSection
,
105 ULONG OldCount
= CriticalSection
->SpinCount
;
107 /* Set to parameter if MP, or to 0 if this is Uniprocessor */
108 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
113 * RtlEnterCriticalSection
116 * Waits to gain ownership of the critical section.
119 * CriticalSection - Critical section to wait for.
125 * Uses a fast-path unless contention happens.
130 RtlEnterCriticalSection(
131 PRTL_CRITICAL_SECTION CriticalSection
)
133 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->Cid
.UniqueThread
;
136 if (InterlockedIncrement(&CriticalSection
->LockCount
) != 0) {
139 * We've failed to lock it! Does this thread
142 if (Thread
== CriticalSection
->OwningThread
) {
144 /* You own it, so you'll get it when you're done with it! No need to
145 use the interlocked functions as only the thread who already owns
146 the lock can modify this data. */
147 CriticalSection
->RecursionCount
++;
148 return STATUS_SUCCESS
;
151 /* NOTE - CriticalSection->OwningThread can be NULL here because changing
152 this information is not serialized. This happens when thread a
153 acquires the lock (LockCount == 0) and thread b tries to
154 acquire it as well (LockCount == 1) but thread a hasn't had a
155 chance to set the OwningThread! So it's not an error when
156 OwningThread is NULL here! */
158 /* We don't own it, so we must wait for it */
159 RtlpWaitForCriticalSection(CriticalSection
);
162 /* Lock successful. Changing this information has not to be serialized because
163 only one thread at a time can actually change it (the one who acquired
165 CriticalSection
->OwningThread
= Thread
;
166 CriticalSection
->RecursionCount
= 1;
167 return STATUS_SUCCESS
;
171 * RtlInitializeCriticalSection
174 * Initialises a new critical section.
177 * CriticalSection - Critical section to initialise
183 * Simply calls RtlInitializeCriticalSectionAndSpinCount
188 RtlInitializeCriticalSection(
189 PRTL_CRITICAL_SECTION CriticalSection
)
191 /* Call the Main Function */
192 return RtlInitializeCriticalSectionAndSpinCount(CriticalSection
, 0);
196 * RtlInitializeCriticalSectionAndSpinCount
199 * Initialises a new critical section.
202 * CriticalSection - Critical section to initialise
204 * SpinCount - Spin count for the critical section.
210 * SpinCount is ignored on single-processor systems.
215 RtlInitializeCriticalSectionAndSpinCount (
216 PRTL_CRITICAL_SECTION CriticalSection
,
219 PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData
;
221 /* First things first, set up the Object */
222 DPRINT("Initializing Critical Section: %x\n", CriticalSection
);
223 CriticalSection
->LockCount
= -1;
224 CriticalSection
->RecursionCount
= 0;
225 CriticalSection
->OwningThread
= 0;
226 CriticalSection
->SpinCount
= (NtCurrentPeb()->NumberOfProcessors
> 1) ? SpinCount
: 0;
227 CriticalSection
->LockSemaphore
= 0;
229 /* Allocate the Debug Data */
230 CritcalSectionDebugData
= RtlpAllocateDebugInfo();
231 DPRINT("Allocated Debug Data: %x inside Process: %x\n",
232 CritcalSectionDebugData
,
233 NtCurrentTeb()->Cid
.UniqueProcess
);
235 if (!CritcalSectionDebugData
) {
238 DPRINT1("Couldn't allocate Debug Data for: %x\n", CriticalSection
);
239 return STATUS_NO_MEMORY
;
243 CritcalSectionDebugData
->Type
= RTL_CRITSECT_TYPE
;
244 CritcalSectionDebugData
->ContentionCount
= 0;
245 CritcalSectionDebugData
->EntryCount
= 0;
246 CritcalSectionDebugData
->CriticalSection
= CriticalSection
;
247 CriticalSection
->DebugInfo
= CritcalSectionDebugData
;
250 * Add it to the List of Critical Sections owned by the process.
251 * If we've initialized the Lock, then use it. If not, then probably
252 * this is the lock initialization itself, so insert it directly.
254 if ((CriticalSection
!= &RtlCriticalSectionLock
) && (RtlpCritSectInitialized
)) {
256 DPRINT("Securely Inserting into ProcessLocks: %x, %x, %x\n",
257 &CritcalSectionDebugData
->ProcessLocksList
,
259 &RtlCriticalSectionList
);
262 RtlEnterCriticalSection(&RtlCriticalSectionLock
);
265 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
268 RtlLeaveCriticalSection(&RtlCriticalSectionLock
);
272 DPRINT("Inserting into ProcessLocks: %x, %x, %x\n",
273 &CritcalSectionDebugData
->ProcessLocksList
,
275 &RtlCriticalSectionList
);
277 /* Add it directly */
278 InsertTailList(&RtlCriticalSectionList
, &CritcalSectionDebugData
->ProcessLocksList
);
281 return STATUS_SUCCESS
;
285 * RtlLeaveCriticalSection
288 * Releases a critical section and makes if available for new owners.
291 * CriticalSection - Critical section to release.
297 * If another thread was waiting, the slow path is entered.
302 RtlLeaveCriticalSection(
303 PRTL_CRITICAL_SECTION CriticalSection
)
306 HANDLE Thread
= (HANDLE
)NtCurrentTeb()->Cid
.UniqueThread
;
308 /* In win this case isn't checked. However it's a valid check so it should only
309 be performed in debug builds! */
310 if (Thread
!= CriticalSection
->OwningThread
)
312 DPRINT1("Releasing critical section not owned!\n");
313 return STATUS_INVALID_PARAMETER
;
317 /* Decrease the Recursion Count. No need to do this atomically because only
318 the thread who holds the lock can call this function (unless the program
319 is totally screwed... */
320 if (--CriticalSection
->RecursionCount
) {
322 /* Someone still owns us, but we are free. This needs to be done atomically. */
323 InterlockedDecrement(&CriticalSection
->LockCount
);
327 /* Nobody owns us anymore. No need to do this atomically. See comment
329 CriticalSection
->OwningThread
= 0;
331 /* Was someone wanting us? This needs to be done atomically. */
332 if (-1 != InterlockedDecrement(&CriticalSection
->LockCount
)) {
334 /* Let him have us */
335 RtlpUnWaitCriticalSection(CriticalSection
);
340 return STATUS_SUCCESS
;
344 * RtlTryEnterCriticalSection
347 * Attemps to gain ownership of the critical section without waiting.
350 * CriticalSection - Critical section to attempt acquiring.
353 * TRUE if the critical section has been acquired, FALSE otherwise.
361 RtlTryEnterCriticalSection(
362 PRTL_CRITICAL_SECTION CriticalSection
)
364 /* Try to take control */
365 if (InterlockedCompareExchange(&CriticalSection
->LockCount
,
370 CriticalSection
->OwningThread
= NtCurrentTeb()->Cid
.UniqueThread
;
371 CriticalSection
->RecursionCount
= 1;
374 } else if (CriticalSection
->OwningThread
== NtCurrentTeb()->Cid
.UniqueThread
) {
376 /* It's already ours */
377 InterlockedIncrement(&CriticalSection
->LockCount
);
378 CriticalSection
->RecursionCount
++;
387 * RtlpWaitForCriticalSection
389 * Slow path of RtlEnterCriticalSection. Waits on an Event Object.
392 * CriticalSection - Critical section to acquire.
395 * STATUS_SUCCESS, or raises an exception if a deadlock is occuring.
403 RtlpWaitForCriticalSection(
404 PRTL_CRITICAL_SECTION CriticalSection
)
407 EXCEPTION_RECORD ExceptionRecord
;
408 BOOLEAN LastChance
= FALSE
;
409 LARGE_INTEGER Timeout
;
411 /* Wait 2.5 minutes */
412 Timeout
.QuadPart
= 150000L * (ULONGLONG
)10000;
413 Timeout
.QuadPart
= -Timeout
.QuadPart
;
414 /* ^^ HACK HACK HACK. Good way:
415 Timeout = &NtCurrentPeb()->CriticalSectionTimeout */
417 /* Do we have an Event yet? */
418 if (!CriticalSection
->LockSemaphore
) {
419 RtlpCreateCriticalSectionSem(CriticalSection
);
422 /* Increase the Debug Entry count */
423 DPRINT("Waiting on Critical Section Event: %x %x\n",
425 CriticalSection
->LockSemaphore
);
426 CriticalSection
->DebugInfo
->EntryCount
++;
430 /* Increase the number of times we've had contention */
431 CriticalSection
->DebugInfo
->ContentionCount
++;
433 /* Wait on the Event */
434 Status
= NtWaitForSingleObject(CriticalSection
->LockSemaphore
,
438 /* We have Timed out */
439 if (Status
== STATUS_TIMEOUT
) {
441 /* Is this the 2nd time we've timed out? */
444 DPRINT1("Deadlock: %x\n", CriticalSection
);
446 /* Yes it is, we are raising an exception */
447 ExceptionRecord
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
448 ExceptionRecord
.ExceptionFlags
= 0;
449 ExceptionRecord
.ExceptionRecord
= NULL
;
450 ExceptionRecord
.ExceptionAddress
= RtlRaiseException
;
451 ExceptionRecord
.NumberParameters
= 1;
452 ExceptionRecord
.ExceptionInformation
[0] = (ULONG_PTR
)CriticalSection
;
453 RtlRaiseException(&ExceptionRecord
);
462 /* If we are here, everything went fine */
463 return STATUS_SUCCESS
;
469 * RtlpUnWaitCriticalSection
471 * Slow path of RtlLeaveCriticalSection. Fires an Event Object.
474 * CriticalSection - Critical section to release.
477 * None. Raises an exception if the system call failed.
485 RtlpUnWaitCriticalSection(
486 PRTL_CRITICAL_SECTION CriticalSection
)
491 /* Do we have an Event yet? */
492 if (!CriticalSection
->LockSemaphore
) {
493 RtlpCreateCriticalSectionSem(CriticalSection
);
496 /* Signal the Event */
497 DPRINT("Signaling Critical Section Event: %x, %x\n",
499 CriticalSection
->LockSemaphore
);
500 Status
= NtSetEvent(CriticalSection
->LockSemaphore
, NULL
);
502 if (!NT_SUCCESS(Status
)) {
505 DPRINT1("Signaling Failed for: %x, %x, %x\n",
507 CriticalSection
->LockSemaphore
,
509 RtlRaiseStatus(Status
);
514 * RtlpCreateCriticalSectionSem
516 * Checks if an Event has been created for the critical section.
522 * None. Raises an exception if the system call failed.
530 RtlpCreateCriticalSectionSem(
531 PRTL_CRITICAL_SECTION CriticalSection
)
533 HANDLE hEvent
= CriticalSection
->LockSemaphore
;
537 /* Chevk if we have an event */
540 /* No, so create it */
541 if (!NT_SUCCESS(Status
= NtCreateEvent(&hNewEvent
,
544 SynchronizationEvent
,
547 /* We failed, this is bad... */
548 DPRINT1("Failed to Create Event!\n");
549 InterlockedDecrement(&CriticalSection
->LockCount
);
550 RtlRaiseStatus(Status
);
553 DPRINT("Created Event: %x \n", hNewEvent
);
555 if ((hEvent
= InterlockedCompareExchangePointer((PVOID
*)&CriticalSection
->LockSemaphore
,
559 /* Some just created an event */
560 DPRINT("Closing already created event: %x\n", hNewEvent
);
569 * RtlpInitDeferedCriticalSection
571 * Initializes the Critical Section implementation.
580 * After this call, the Process Critical Section list is protected.
585 RtlpInitDeferedCriticalSection(
589 /* Initialize the Process Critical Section List */
590 InitializeListHead(&RtlCriticalSectionList
);
592 /* Initialize the CS Protecting the List */
593 RtlInitializeCriticalSection(&RtlCriticalSectionLock
);
595 /* It's now safe to enter it */
596 RtlpCritSectInitialized
= TRUE
;
600 * RtlpAllocateDebugInfo
602 * Finds or allocates memory for a Critical Section Debug Object
608 * A pointer to an empty Critical Section Debug Object.
611 * For optimization purposes, the first 64 entries can be cached. From
612 * then on, future Critical Sections will allocate memory from the heap.
615 PRTL_CRITICAL_SECTION_DEBUG
617 RtlpAllocateDebugInfo(
622 /* Try to allocate from our buffer first */
623 for (i
= 0; i
< MAX_STATIC_CS_DEBUG_OBJECTS
; i
++) {
625 /* Check if Entry is free */
626 if (!RtlpDebugInfoFreeList
[i
]) {
628 /* Mark entry in use */
629 DPRINT("Using entry: %d. Buffer: %x\n", i
, &RtlpStaticDebugInfo
[i
]);
630 RtlpDebugInfoFreeList
[i
] = TRUE
;
632 /* Use free entry found */
633 return &RtlpStaticDebugInfo
[i
];
638 /* We are out of static buffer, allocate dynamic */
639 return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap
,
641 sizeof(RTL_CRITICAL_SECTION_DEBUG
));
647 * Frees the memory for a Critical Section Debug Object
650 * DebugInfo - Pointer to Critical Section Debug Object to free.
656 * If the pointer is part of the static buffer, then the entry is made
657 * free again. If not, the object is de-allocated from the heap.
663 PRTL_CRITICAL_SECTION_DEBUG DebugInfo
)
667 /* Is it part of our cached entries? */
668 if ((DebugInfo
>= RtlpStaticDebugInfo
) &&
669 (DebugInfo
<= &RtlpStaticDebugInfo
[MAX_STATIC_CS_DEBUG_OBJECTS
-1])) {
671 /* Yes. zero it out */
672 RtlZeroMemory(DebugInfo
, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
675 EntryId
= (DebugInfo
- RtlpStaticDebugInfo
);
676 DPRINT("Freeing from Buffer: %x. Entry: %d inside Process: %x\n",
679 NtCurrentTeb()->Cid
.UniqueProcess
);
680 RtlpDebugInfoFreeList
[EntryId
] = FALSE
;
684 /* It's a dynamic one, so free from the heap */
685 DPRINT("Freeing from Heap: %x inside Process: %x\n",
687 NtCurrentTeb()->Cid
.UniqueProcess
);
688 RtlFreeHeap(NtCurrentPeb()->ProcessHeap
, 0, DebugInfo
);