1 /*
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)
7 * Gunnar Dalsnes
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <rtl.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #define MAX_STATIC_CS_DEBUG_OBJECTS 64
18
19 static RTL_CRITICAL_SECTION RtlCriticalSectionLock;
20 static LIST_ENTRY RtlCriticalSectionList = {&RtlCriticalSectionList, &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
25 LARGE_INTEGER RtlpTimeout;
26 BOOLEAN RtlpTimeoutDisable;
27
28 extern BOOLEAN LdrpShutdownInProgress;
29 extern HANDLE LdrpShutdownThreadId;
30
31 /* FUNCTIONS *****************************************************************/
32
33 #define CRITSECT_HAS_DEBUG_INFO(CriticalSection) \
34 (((CriticalSection)->DebugInfo != NULL) && \
35 ((CriticalSection)->DebugInfo != LongToPtr(-1)))
36
37 /*++
38 * RtlpCreateCriticalSectionSem
39 *
40 * Checks if an Event has been created for the critical section.
41 *
42 * Params:
43 * None
44 *
45 * Returns:
46 * None. Raises an exception if the system call failed.
47 *
48 * Remarks:
49 * None
50 *
51 *--*/
52 _At_(CriticalSection->LockSemaphore, _Post_notnull_)
53 VOID
54 NTAPI
RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection)55 RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection)
56 {
57 HANDLE hEvent = CriticalSection->LockSemaphore;
58 HANDLE hNewEvent;
59 NTSTATUS Status;
60
61 /* Check if we have an event */
62 if (!hEvent)
63 {
64 /* No, so create it */
65 Status = NtCreateEvent(&hNewEvent,
66 EVENT_ALL_ACCESS,
67 NULL,
68 SynchronizationEvent,
69 FALSE);
70 if (!NT_SUCCESS(Status))
71 {
72 DPRINT1("Failed to Create Event!\n");
73
74 /*
75 * Use INVALID_HANDLE_VALUE (-1) to signal that
76 * the global keyed event must be used.
77 */
78 hNewEvent = INVALID_HANDLE_VALUE;
79 }
80
81 DPRINT("Created Event: %p\n", hNewEvent);
82
83 /* Exchange the LockSemaphore field with the new handle, if it is still 0 */
84 if (InterlockedCompareExchangePointer((PVOID*)&CriticalSection->LockSemaphore,
85 (PVOID)hNewEvent,
86 NULL) != NULL)
87 {
88 /* Someone else just created an event */
89 if (hNewEvent != INVALID_HANDLE_VALUE)
90 {
91 DPRINT("Closing already created event: %p\n", hNewEvent);
92 NtClose(hNewEvent);
93 }
94 }
95 }
96
97 return;
98 }
99
100 /*++
101 * RtlpWaitForCriticalSection
102 *
103 * Slow path of RtlEnterCriticalSection. Waits on an Event Object.
104 *
105 * Params:
106 * CriticalSection - Critical section to acquire.
107 *
108 * Returns:
109 * STATUS_SUCCESS, or raises an exception if a deadlock is occuring.
110 *
111 * Remarks:
112 * None
113 *
114 *--*/
115 NTSTATUS
116 NTAPI
RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)117 RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
118 {
119 NTSTATUS Status;
120 EXCEPTION_RECORD ExceptionRecord;
121 BOOLEAN LastChance = FALSE;
122
123 /* Increase the Debug Entry count */
124 DPRINT("Waiting on Critical Section Event: %p %p\n",
125 CriticalSection,
126 CriticalSection->LockSemaphore);
127
128 if (CRITSECT_HAS_DEBUG_INFO(CriticalSection))
129 CriticalSection->DebugInfo->EntryCount++;
130
131 /*
132 * If we're shutting down the process, we're allowed to acquire any
133 * critical sections by force (the loader lock in particular)
134 */
135 if (LdrpShutdownInProgress &&
136 LdrpShutdownThreadId == NtCurrentTeb()->RealClientId.UniqueThread)
137 {
138 DPRINT("Forcing ownership of critical section %p\n", CriticalSection);
139 return STATUS_SUCCESS;
140 }
141
142 /* Do we have an Event yet? */
143 if (!CriticalSection->LockSemaphore)
144 {
145 RtlpCreateCriticalSectionSem(CriticalSection);
146 }
147
148 for (;;)
149 {
150 /* Increase the number of times we've had contention */
151 if (CRITSECT_HAS_DEBUG_INFO(CriticalSection))
152 CriticalSection->DebugInfo->ContentionCount++;
153
154 /* Check if allocating the event failed */
155 if (CriticalSection->LockSemaphore == INVALID_HANDLE_VALUE)
156 {
157 /* Use the global keyed event (NULL as keyed event handle) */
158 Status = NtWaitForKeyedEvent(NULL,
159 CriticalSection,
160 FALSE,
161 (RtlpTimeoutDisable ? NULL : &RtlpTimeout));
162 }
163 else
164 {
165 /* Wait on the Event */
166 Status = NtWaitForSingleObject(CriticalSection->LockSemaphore,
167 FALSE,
168 (RtlpTimeoutDisable ? NULL : &RtlpTimeout));
169 }
170
171 /* We have Timed out */
172 if (Status == STATUS_TIMEOUT)
173 {
174 /* Is this the 2nd time we've timed out? */
175 if (LastChance)
176 {
177 ERROR_DBGBREAK("Deadlock: 0x%p\n", CriticalSection);
178
179 /* Yes it is, we are raising an exception */
180 ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
181 ExceptionRecord.ExceptionFlags = 0;
182 ExceptionRecord.ExceptionRecord = NULL;
183 ExceptionRecord.ExceptionAddress = RtlRaiseException;
184 ExceptionRecord.NumberParameters = 1;
185 ExceptionRecord.ExceptionInformation[0] = (ULONG_PTR)CriticalSection;
186 RtlRaiseException(&ExceptionRecord);
187 }
188
189 /* One more try */
190 LastChance = TRUE;
191 }
192 else
193 {
194 /* If we are here, everything went fine */
195 return STATUS_SUCCESS;
196 }
197 }
198 }
199
200 /*++
201 * RtlpUnWaitCriticalSection
202 *
203 * Slow path of RtlLeaveCriticalSection. Fires an Event Object.
204 *
205 * Params:
206 * CriticalSection - Critical section to release.
207 *
208 * Returns:
209 * None. Raises an exception if the system call failed.
210 *
211 * Remarks:
212 * None
213 *
214 *--*/
215 VOID
216 NTAPI
RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)217 RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
218 {
219 NTSTATUS Status;
220
221 /* Do we have an Event yet? */
222 if (!CriticalSection->LockSemaphore)
223 {
224 RtlpCreateCriticalSectionSem(CriticalSection);
225 }
226
227 /* Signal the Event */
228 DPRINT("Signaling Critical Section Event: %p, %p\n",
229 CriticalSection,
230 CriticalSection->LockSemaphore);
231
232 /* Check if this critical section needs to use the keyed event */
233 if (CriticalSection->LockSemaphore == INVALID_HANDLE_VALUE)
234 {
235 /* Release keyed event */
236 Status = NtReleaseKeyedEvent(NULL, CriticalSection, FALSE, &RtlpTimeout);
237 }
238 else
239 {
240 /* Set the event */
241 Status = NtSetEvent(CriticalSection->LockSemaphore, NULL);
242 }
243
244 if (!NT_SUCCESS(Status))
245 {
246 /* We've failed */
247 DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n",
248 CriticalSection,
249 CriticalSection->LockSemaphore,
250 Status);
251 RtlRaiseStatus(Status);
252 }
253 }
254
255 /*++
256 * RtlpInitDeferredCriticalSection
257 *
258 * Initializes the Critical Section implementation.
259 *
260 * Params:
261 * None
262 *
263 * Returns:
264 * None.
265 *
266 * Remarks:
267 * After this call, the Process Critical Section list is protected.
268 *
269 *--*/
270 VOID
271 NTAPI
RtlpInitDeferredCriticalSection(VOID)272 RtlpInitDeferredCriticalSection(VOID)
273 {
274 /* Initialize the CS Protecting the List */
275 RtlInitializeCriticalSection(&RtlCriticalSectionLock);
276
277 /* It's now safe to enter it */
278 RtlpCritSectInitialized = TRUE;
279 }
280
281 /*++
282 * RtlpAllocateDebugInfo
283 *
284 * Finds or allocates memory for a Critical Section Debug Object
285 *
286 * Params:
287 * None
288 *
289 * Returns:
290 * A pointer to an empty Critical Section Debug Object.
291 *
292 * Remarks:
293 * For optimization purposes, the first 64 entries can be cached. From
294 * then on, future Critical Sections will allocate memory from the heap.
295 *
296 *--*/
297 PRTL_CRITICAL_SECTION_DEBUG
298 NTAPI
RtlpAllocateDebugInfo(VOID)299 RtlpAllocateDebugInfo(VOID)
300 {
301 ULONG i;
302
303 /* Try to allocate from our buffer first */
304 for (i = 0; i < MAX_STATIC_CS_DEBUG_OBJECTS; i++)
305 {
306 /* Check if Entry is free */
307 if (!RtlpDebugInfoFreeList[i])
308 {
309 /* Mark entry in use */
310 DPRINT("Using entry: %lu. Buffer: %p\n", i, &RtlpStaticDebugInfo[i]);
311 RtlpDebugInfoFreeList[i] = TRUE;
312
313 /* Use free entry found */
314 return &RtlpStaticDebugInfo[i];
315 }
316 }
317
318 /* We are out of static buffer, allocate dynamic */
319 return RtlAllocateHeap(RtlGetProcessHeap(),
320 0,
321 sizeof(RTL_CRITICAL_SECTION_DEBUG));
322 }
323
324 /*++
325 * RtlpFreeDebugInfo
326 *
327 * Frees the memory for a Critical Section Debug Object
328 *
329 * Params:
330 * DebugInfo - Pointer to Critical Section Debug Object to free.
331 *
332 * Returns:
333 * None.
334 *
335 * Remarks:
336 * If the pointer is part of the static buffer, then the entry is made
337 * free again. If not, the object is de-allocated from the heap.
338 *
339 *--*/
340 VOID
341 NTAPI
RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo)342 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo)
343 {
344 SIZE_T EntryId;
345
346 /* Is it part of our cached entries? */
347 if ((DebugInfo >= RtlpStaticDebugInfo) &&
348 (DebugInfo <= &RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS-1]))
349 {
350 /* Yes. zero it out */
351 RtlZeroMemory(DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));
352
353 /* Mark as free */
354 EntryId = (DebugInfo - RtlpStaticDebugInfo);
355 DPRINT("Freeing from Buffer: %p. Entry: %Iu inside Process: %p\n",
356 DebugInfo,
357 EntryId,
358 NtCurrentTeb()->ClientId.UniqueProcess);
359 RtlpDebugInfoFreeList[EntryId] = FALSE;
360
361 }
362 else if (!DebugInfo->Flags)
363 {
364 /* It's a dynamic one, so free from the heap */
365 DPRINT("Freeing from Heap: %p inside Process: %p\n",
366 DebugInfo,
367 NtCurrentTeb()->ClientId.UniqueProcess);
368 RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, DebugInfo);
369 }
370 else
371 {
372 /* Wine stores a section name pointer in the Flags member */
373 DPRINT("Assuming static: %p inside Process: %p\n",
374 DebugInfo,
375 NtCurrentTeb()->ClientId.UniqueProcess);
376 }
377 }
378
379 /*++
380 * RtlDeleteCriticalSection
381 * @implemented NT4
382 *
383 * Deletes a Critical Section
384 *
385 * Params:
386 * CriticalSection - Critical section to delete.
387 *
388 * Returns:
389 * STATUS_SUCCESS, or error value returned by NtClose.
390 *
391 * Remarks:
392 * The critical section members should not be read after this call.
393 *
394 *--*/
395 NTSTATUS
396 NTAPI
RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)397 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
398 {
399 NTSTATUS Status = STATUS_SUCCESS;
400
401 DPRINT("Deleting Critical Section: %p\n", CriticalSection);
402
403 /* Close the Event Object Handle if it exists */
404 if (CriticalSection->LockSemaphore)
405 {
406 /* In case NtClose fails, return the status */
407 Status = NtClose(CriticalSection->LockSemaphore);
408 }
409
410 /* Protect List */
411 RtlEnterCriticalSection(&RtlCriticalSectionLock);
412
413 if (CRITSECT_HAS_DEBUG_INFO(CriticalSection))
414 {
415 /* Remove it from the list */
416 RemoveEntryList(&CriticalSection->DebugInfo->ProcessLocksList);
417 #if 0
418 /* We need to preserve Flags for RtlpFreeDebugInfo */
419 RtlZeroMemory(CriticalSection->DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG));
420 #endif
421 }
422
423 /* Unprotect */
424 RtlLeaveCriticalSection(&RtlCriticalSectionLock);
425
426 if (CRITSECT_HAS_DEBUG_INFO(CriticalSection))
427 {
428 /* Free it */
429 RtlpFreeDebugInfo(CriticalSection->DebugInfo);
430 }
431
432 /* Wipe it out */
433 RtlZeroMemory(CriticalSection, sizeof(RTL_CRITICAL_SECTION));
434
435 /* Return */
436 return Status;
437 }
438
439 /*++
440 * RtlSetCriticalSectionSpinCount
441 * @implemented NT4
442 *
443 * Sets the spin count for a critical section.
444 *
445 * Params:
446 * CriticalSection - Critical section to set the spin count for.
447 *
448 * SpinCount - Spin count for the critical section.
449 *
450 * Returns:
451 * STATUS_SUCCESS.
452 *
453 * Remarks:
454 * SpinCount is ignored on single-processor systems.
455 *
456 *--*/
457 ULONG
458 NTAPI
RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection,ULONG SpinCount)459 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
460 ULONG SpinCount)
461 {
462 ULONG OldCount = (ULONG)CriticalSection->SpinCount;
463
464 /* Set to parameter if MP, or to 0 if this is Uniprocessor */
465 CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
466 return OldCount;
467 }
468
469 /*++
470 * RtlEnterCriticalSection
471 * @implemented NT4
472 *
473 * Waits to gain ownership of the critical section.
474 *
475 * Params:
476 * CriticalSection - Critical section to wait for.
477 *
478 * Returns:
479 * STATUS_SUCCESS.
480 *
481 * Remarks:
482 * Uses a fast-path unless contention happens.
483 *
484 *--*/
485 NTSTATUS
486 NTAPI
RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)487 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
488 {
489 HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread;
490
491 /* Try to lock it */
492 if (InterlockedIncrement(&CriticalSection->LockCount) != 0)
493 {
494 /* We've failed to lock it! Does this thread actually own it? */
495 if (Thread == CriticalSection->OwningThread)
496 {
497 /*
498 * You own it, so you'll get it when you're done with it! No need to
499 * use the interlocked functions as only the thread who already owns
500 * the lock can modify this data.
501 */
502 CriticalSection->RecursionCount++;
503 return STATUS_SUCCESS;
504 }
505
506 /* NOTE - CriticalSection->OwningThread can be NULL here because changing
507 this information is not serialized. This happens when thread a
508 acquires the lock (LockCount == 0) and thread b tries to
509 acquire it as well (LockCount == 1) but thread a hasn't had a
510 chance to set the OwningThread! So it's not an error when
511 OwningThread is NULL here! */
512
513 /* We don't own it, so we must wait for it */
514 RtlpWaitForCriticalSection(CriticalSection);
515 }
516
517 /*
518 * Lock successful. Changing this information has not to be serialized
519 * because only one thread at a time can actually change it (the one who
520 * acquired the lock)!
521 */
522 CriticalSection->OwningThread = Thread;
523 CriticalSection->RecursionCount = 1;
524 return STATUS_SUCCESS;
525 }
526
527 /*++
528 * RtlInitializeCriticalSection
529 * @implemented NT4
530 *
531 * Initialises a new critical section.
532 *
533 * Params:
534 * CriticalSection - Critical section to initialise
535 *
536 * Returns:
537 * STATUS_SUCCESS.
538 *
539 * Remarks:
540 * Simply calls RtlInitializeCriticalSectionAndSpinCount
541 *
542 *--*/
543 NTSTATUS
544 NTAPI
RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)545 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
546 {
547 /* Call the Main Function */
548 return RtlInitializeCriticalSectionAndSpinCount(CriticalSection, 0);
549 }
550
551 /*++
552 * RtlInitializeCriticalSectionEx
553 * @implemented NT6.0
554 *
555 * Initialises a new critical section.
556 *
557 * Params:
558 * CriticalSection - Critical section to initialise
559 *
560 * SpinCount - Spin count for the critical section.
561 *
562 * Flags - Flags for initialization.
563 *
564 * Returns:
565 * STATUS_SUCCESS.
566 *
567 * Remarks:
568 * SpinCount is ignored on single-processor systems.
569 *
570 *--*/
571 NTSTATUS
572 NTAPI
RtlInitializeCriticalSectionEx(_Out_ PRTL_CRITICAL_SECTION CriticalSection,_In_ ULONG SpinCount,_In_ ULONG Flags)573 RtlInitializeCriticalSectionEx(
574 _Out_ PRTL_CRITICAL_SECTION CriticalSection,
575 _In_ ULONG SpinCount,
576 _In_ ULONG Flags)
577 {
578 PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData;
579 ULONG AllowedFlags;
580 ULONG OsVersion;
581
582 /* Remove lower bits from flags */
583 Flags &= RTL_CRITICAL_SECTION_ALL_FLAG_BITS;
584
585 /* These flags generally allowed */
586 AllowedFlags = RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO |
587 RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN |
588 RTL_CRITICAL_SECTION_FLAG_STATIC_INIT;
589
590 /* Flags for Windows 7+ (CHECKME) */
591 OsVersion = NtCurrentPeb()->OSMajorVersion << 8 |
592 NtCurrentPeb()->OSMinorVersion;
593 if (OsVersion >= _WIN32_WINNT_WIN7)
594 {
595 AllowedFlags |= RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE |
596 RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO;
597 }
598
599 if (Flags & ~AllowedFlags)
600 {
601 return STATUS_INVALID_PARAMETER_3;
602 }
603
604 if (SpinCount & RTL_CRITICAL_SECTION_ALL_FLAG_BITS)
605 {
606 return STATUS_INVALID_PARAMETER_2;
607 }
608
609 /* First things first, set up the Object */
610 DPRINT("Initializing Critical Section: %p\n", CriticalSection);
611 CriticalSection->LockCount = -1;
612 CriticalSection->RecursionCount = 0;
613 CriticalSection->OwningThread = 0;
614 CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
615 CriticalSection->LockSemaphore = 0;
616
617 if (Flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO)
618 {
619 CriticalSection->DebugInfo = LongToPtr(-1);
620 }
621 else
622 {
623 /* Allocate the Debug Data */
624 CritcalSectionDebugData = RtlpAllocateDebugInfo();
625 DPRINT("Allocated Debug Data: %p inside Process: %p\n",
626 CritcalSectionDebugData,
627 NtCurrentTeb()->ClientId.UniqueProcess);
628
629 if (!CritcalSectionDebugData)
630 {
631 /* This is bad! */
632 DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection);
633 return STATUS_NO_MEMORY;
634 }
635
636 /* Set it up */
637 CritcalSectionDebugData->Type = RTL_CRITSECT_TYPE;
638 CritcalSectionDebugData->ContentionCount = 0;
639 CritcalSectionDebugData->EntryCount = 0;
640 CritcalSectionDebugData->CriticalSection = CriticalSection;
641 CritcalSectionDebugData->Flags = 0;
642 CriticalSection->DebugInfo = CritcalSectionDebugData;
643
644 /*
645 * Add it to the List of Critical Sections owned by the process.
646 * If we've initialized the Lock, then use it. If not, then probably
647 * this is the lock initialization itself, so insert it directly.
648 */
649 if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized))
650 {
651 DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n",
652 &CritcalSectionDebugData->ProcessLocksList,
653 CriticalSection,
654 &RtlCriticalSectionList);
655
656 /* Protect List */
657 RtlEnterCriticalSection(&RtlCriticalSectionLock);
658
659 /* Add this one */
660 InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
661
662 /* Unprotect */
663 RtlLeaveCriticalSection(&RtlCriticalSectionLock);
664 }
665 else
666 {
667 DPRINT("Inserting into ProcessLocks: %p, %p, %p\n",
668 &CritcalSectionDebugData->ProcessLocksList,
669 CriticalSection,
670 &RtlCriticalSectionList);
671
672 /* Add it directly */
673 InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
674 }
675 }
676
677 return STATUS_SUCCESS;
678 }
679
680 /*++
681 * RtlInitializeCriticalSectionAndSpinCount
682 * @implemented NT4
683 *
684 * Initialises a new critical section.
685 *
686 * Params:
687 * CriticalSection - Critical section to initialise
688 *
689 * SpinCount - Spin count for the critical section.
690 *
691 * Returns:
692 * STATUS_SUCCESS.
693 *
694 * Remarks:
695 * SpinCount is ignored on single-processor systems.
696 *
697 *--*/
698 NTSTATUS
699 NTAPI
RtlInitializeCriticalSectionAndSpinCount(_Out_ PRTL_CRITICAL_SECTION CriticalSection,_In_ ULONG SpinCount)700 RtlInitializeCriticalSectionAndSpinCount(
701 _Out_ PRTL_CRITICAL_SECTION CriticalSection,
702 _In_ ULONG SpinCount)
703 {
704 SpinCount &= ~RTL_CRITICAL_SECTION_ALL_FLAG_BITS;
705 return RtlInitializeCriticalSectionEx(CriticalSection, SpinCount, 0);
706 }
707
708 /*++
709 * RtlGetCriticalSectionRecursionCount
710 * @implemented NT5.2 SP1
711 *
712 * Retrieves the recursion count of a given critical section.
713 *
714 * Params:
715 * CriticalSection - Critical section to retrieve its recursion count.
716 *
717 * Returns:
718 * The recursion count.
719 *
720 * Remarks:
721 * We return the recursion count of the critical section if it is owned
722 * by the current thread, and otherwise we return zero.
723 *
724 *--*/
725 LONG
726 NTAPI
RtlGetCriticalSectionRecursionCount(PRTL_CRITICAL_SECTION CriticalSection)727 RtlGetCriticalSectionRecursionCount(PRTL_CRITICAL_SECTION CriticalSection)
728 {
729 if (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
730 {
731 /*
732 * The critical section is owned by the current thread,
733 * therefore retrieve its actual recursion count.
734 */
735 return CriticalSection->RecursionCount;
736 }
737 else
738 {
739 /*
740 * It is not owned by the current thread, so
741 * for this thread there is no recursion.
742 */
743 return 0;
744 }
745 }
746
747 /*++
748 * RtlLeaveCriticalSection
749 * @implemented NT4
750 *
751 * Releases a critical section and makes if available for new owners.
752 *
753 * Params:
754 * CriticalSection - Critical section to release.
755 *
756 * Returns:
757 * STATUS_SUCCESS.
758 *
759 * Remarks:
760 * If another thread was waiting, the slow path is entered.
761 *
762 *--*/
763 NTSTATUS
764 NTAPI
RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)765 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
766 {
767 #if DBG
768 HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread;
769
770 /*
771 * In win this case isn't checked. However it's a valid check so it should
772 * only be performed in debug builds!
773 */
774 if (Thread != CriticalSection->OwningThread)
775 {
776 DPRINT1("Releasing critical section not owned!\n");
777 return STATUS_INVALID_PARAMETER;
778 }
779 #endif
780
781 /*
782 * Decrease the Recursion Count. No need to do this atomically because only
783 * the thread who holds the lock can call this function (unless the program
784 * is totally screwed...
785 */
786 if (--CriticalSection->RecursionCount)
787 {
788 if (CriticalSection->RecursionCount < 0)
789 {
790 DPRINT1("CRITICAL SECTION MESS: Section %p is not acquired!\n", CriticalSection);
791 return STATUS_UNSUCCESSFUL;
792 }
793 /* Someone still owns us, but we are free. This needs to be done atomically. */
794 InterlockedDecrement(&CriticalSection->LockCount);
795 }
796 else
797 {
798 /*
799 * Nobody owns us anymore. No need to do this atomically.
800 * See comment above.
801 */
802 CriticalSection->OwningThread = 0;
803
804 /* Was someone wanting us? This needs to be done atomically. */
805 if (-1 != InterlockedDecrement(&CriticalSection->LockCount))
806 {
807 /* Let him have us */
808 RtlpUnWaitCriticalSection(CriticalSection);
809 }
810 }
811
812 /* Sucessful! */
813 return STATUS_SUCCESS;
814 }
815
816 /*++
817 * RtlTryEnterCriticalSection
818 * @implemented NT4
819 *
820 * Attemps to gain ownership of the critical section without waiting.
821 *
822 * Params:
823 * CriticalSection - Critical section to attempt acquiring.
824 *
825 * Returns:
826 * TRUE if the critical section has been acquired, FALSE otherwise.
827 *
828 * Remarks:
829 * None
830 *
831 *--*/
832 BOOLEAN
833 NTAPI
RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)834 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
835 {
836 /* Try to take control */
837 if (InterlockedCompareExchange(&CriticalSection->LockCount, 0, -1) == -1)
838 {
839 /* It's ours */
840 CriticalSection->OwningThread = NtCurrentTeb()->ClientId.UniqueThread;
841 CriticalSection->RecursionCount = 1;
842 return TRUE;
843 }
844 else if (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
845 {
846 /* It's already ours */
847 InterlockedIncrement(&CriticalSection->LockCount);
848 CriticalSection->RecursionCount++;
849 return TRUE;
850 }
851
852 /* It's not ours */
853 return FALSE;
854 }
855
856 VOID
857 NTAPI
RtlCheckForOrphanedCriticalSections(HANDLE ThreadHandle)858 RtlCheckForOrphanedCriticalSections(HANDLE ThreadHandle)
859 {
860 UNIMPLEMENTED;
861 }
862
863 ULONG
864 NTAPI
RtlIsCriticalSectionLocked(PRTL_CRITICAL_SECTION CriticalSection)865 RtlIsCriticalSectionLocked(PRTL_CRITICAL_SECTION CriticalSection)
866 {
867 return CriticalSection->RecursionCount != 0;
868 }
869
870 ULONG
871 NTAPI
RtlIsCriticalSectionLockedByThread(PRTL_CRITICAL_SECTION CriticalSection)872 RtlIsCriticalSectionLockedByThread(PRTL_CRITICAL_SECTION CriticalSection)
873 {
874 return CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread &&
875 CriticalSection->RecursionCount != 0;
876 }
877
878 VOID
879 NTAPI
RtlpNotOwnerCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)880 RtlpNotOwnerCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
881 {
882 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
883 }
884
885 /* EOF */
886