2 * Win32 critical sections
4 * Copyright 1998 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include <sys/types.h>
28 #define WIN32_NO_STATUS
31 #include "wine/debug.h"
32 #include "ntdll_misc.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(ntdll
);
35 WINE_DECLARE_DEBUG_CHANNEL(relay
);
37 static inline void small_pause(void)
40 __asm__
__volatile__( "rep;nop" : : : "memory" );
42 __asm__
__volatile__( "" : : : "memory" );
46 static void *no_debug_info_marker
= (void *)(ULONG_PTR
)-1;
48 static BOOL
crit_section_has_debuginfo(const RTL_CRITICAL_SECTION
*crit
)
50 return crit
->DebugInfo
!= NULL
&& crit
->DebugInfo
!= no_debug_info_marker
;
53 /***********************************************************************
54 * RtlInitializeCriticalSection (NTDLL.@)
56 * Initialises a new critical section.
59 * crit [O] Critical section to initialise
65 * RtlInitializeCriticalSectionEx(),
66 * RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(),
67 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
68 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
70 NTSTATUS WINAPI
RtlInitializeCriticalSection( RTL_CRITICAL_SECTION
*crit
)
72 return RtlInitializeCriticalSectionEx( crit
, 0, 0 );
75 /***********************************************************************
76 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
78 * Initialises a new critical section with a given spin count.
81 * crit [O] Critical section to initialise
82 * spincount [I] Spin count for crit
88 * Available on NT4 SP3 or later.
91 * RtlInitializeCriticalSectionEx(),
92 * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
93 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
94 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
96 NTSTATUS WINAPI
RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
98 return RtlInitializeCriticalSectionEx( crit
, spincount
, 0 );
101 /***********************************************************************
102 * RtlInitializeCriticalSectionEx (NTDLL.@)
104 * Initialises a new critical section with a given spin count and flags.
107 * crit [O] Critical section to initialise.
108 * spincount [I] Number of times to spin upon contention.
109 * flags [I] RTL_CRITICAL_SECTION_FLAG_ flags from winnt.h.
115 * Available on Vista or later.
118 * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
119 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
120 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
122 NTSTATUS WINAPI
RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
, ULONG flags
)
124 if (flags
& (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN
|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT
))
125 FIXME("(%p,%u,0x%08x) semi-stub\n", crit
, spincount
, flags
);
127 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
128 * memory from a static pool to hold the debug info. Then heap.c could pass
129 * this flag rather than initialising the process heap CS by hand. If this
130 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
131 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
133 if (flags
& RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO
)
134 crit
->DebugInfo
= no_debug_info_marker
;
137 crit
->DebugInfo
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
140 crit
->DebugInfo
->Type
= 0;
141 crit
->DebugInfo
->CreatorBackTraceIndex
= 0;
142 crit
->DebugInfo
->CriticalSection
= crit
;
143 crit
->DebugInfo
->ProcessLocksList
.Blink
= &(crit
->DebugInfo
->ProcessLocksList
);
144 crit
->DebugInfo
->ProcessLocksList
.Flink
= &(crit
->DebugInfo
->ProcessLocksList
);
145 crit
->DebugInfo
->EntryCount
= 0;
146 crit
->DebugInfo
->ContentionCount
= 0;
147 memset( crit
->DebugInfo
->Spare
, 0, sizeof(crit
->DebugInfo
->Spare
) );
150 crit
->LockCount
= -1;
151 crit
->RecursionCount
= 0;
152 crit
->OwningThread
= 0;
153 crit
->LockSemaphore
= 0;
154 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
155 crit
->SpinCount
= spincount
& ~0x80000000;
156 return STATUS_SUCCESS
;
159 /***********************************************************************
160 * RtlSetCriticalSectionSpinCount (NTDLL.@)
162 * Sets the spin count of a critical section.
165 * crit [I/O] Critical section
166 * spincount [I] Spin count for crit
169 * The previous spin count.
172 * If the system is not SMP, spincount is ignored and set to 0.
175 * RtlInitializeCriticalSectionEx(),
176 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
177 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
178 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
180 ULONG WINAPI
RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
182 ULONG oldspincount
= crit
->SpinCount
;
183 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
184 crit
->SpinCount
= spincount
;
188 /***********************************************************************
189 * RtlDeleteCriticalSection (NTDLL.@)
191 * Frees the resources used by a critical section.
194 * crit [I/O] Critical section to free
200 * RtlInitializeCriticalSectionEx(),
201 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
202 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
203 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
205 NTSTATUS WINAPI
RtlDeleteCriticalSection( RTL_CRITICAL_SECTION
*crit
)
207 crit
->LockCount
= -1;
208 crit
->RecursionCount
= 0;
209 crit
->OwningThread
= 0;
210 if (crit_section_has_debuginfo( crit
))
212 /* only free the ones we made in here */
213 if (!crit
->DebugInfo
->Spare
[0])
215 RtlFreeHeap( GetProcessHeap(), 0, crit
->DebugInfo
);
216 crit
->DebugInfo
= NULL
;
218 if (unix_funcs
->fast_RtlDeleteCriticalSection( crit
) == STATUS_NOT_IMPLEMENTED
)
219 NtClose( crit
->LockSemaphore
);
221 else NtClose( crit
->LockSemaphore
);
222 crit
->LockSemaphore
= 0;
223 return STATUS_SUCCESS
;
227 /***********************************************************************
228 * RtlpWaitForCriticalSection (NTDLL.@)
230 * Waits for a busy critical section to become free.
233 * crit [I/O] Critical section to wait for
239 * Use RtlEnterCriticalSection() instead of this function as it is often much
243 * RtlInitializeCriticalSectionEx(),
244 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
245 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
246 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
248 NTSTATUS WINAPI
RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION
*crit
)
250 LONGLONG timeout
= NtCurrentTeb()->Peb
->CriticalSectionTimeout
.QuadPart
/ -10000000;
252 /* Don't allow blocking on a critical section during process termination */
253 if (RtlDllShutdownInProgress())
255 WARN( "process %s is shutting down, returning STATUS_SUCCESS\n",
256 debugstr_w(NtCurrentTeb()->Peb
->ProcessParameters
->ImagePathName
.Buffer
) );
257 return STATUS_SUCCESS
;
262 EXCEPTION_RECORD rec
;
263 NTSTATUS status
= unix_funcs
->fast_RtlpWaitForCriticalSection( crit
, 5 );
266 if ( status
== STATUS_TIMEOUT
)
268 const char *name
= NULL
;
269 if (crit_section_has_debuginfo( crit
)) name
= (char *)crit
->DebugInfo
->Spare
[0];
270 if (!name
) name
= "?";
271 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
272 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
273 status
= unix_funcs
->fast_RtlpWaitForCriticalSection( crit
, 60 );
276 if ( status
== STATUS_TIMEOUT
&& TRACE_ON(relay
) )
278 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
279 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
280 status
= unix_funcs
->fast_RtlpWaitForCriticalSection( crit
, 300 );
284 if (status
== STATUS_WAIT_0
) break;
286 /* Throw exception only for Wine internal locks */
287 if (!crit_section_has_debuginfo( crit
) || !crit
->DebugInfo
->Spare
[0]) continue;
289 /* only throw deadlock exception if configured timeout is reached */
290 if (timeout
> 0) continue;
292 rec
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
293 rec
.ExceptionFlags
= 0;
294 rec
.ExceptionRecord
= NULL
;
295 rec
.ExceptionAddress
= RtlRaiseException
; /* sic */
296 rec
.NumberParameters
= 1;
297 rec
.ExceptionInformation
[0] = (ULONG_PTR
)crit
;
298 RtlRaiseException( &rec
);
300 if (crit_section_has_debuginfo( crit
)) crit
->DebugInfo
->ContentionCount
++;
301 return STATUS_SUCCESS
;
305 /***********************************************************************
306 * RtlpUnWaitCriticalSection (NTDLL.@)
308 * Notifies other threads waiting on the busy critical section that it has
312 * crit [I/O] Critical section
315 * Success: STATUS_SUCCESS.
316 * Failure: Any error returned by NtReleaseSemaphore()
319 * Use RtlLeaveCriticalSection() instead of this function as it is often much
323 * RtlInitializeCriticalSectionEx(),
324 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
325 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
326 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
328 NTSTATUS WINAPI
RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION
*crit
)
330 NTSTATUS ret
= unix_funcs
->fast_RtlpUnWaitCriticalSection( crit
);
332 if (ret
) RtlRaiseStatus( ret
);
337 /***********************************************************************
338 * RtlEnterCriticalSection (NTDLL.@)
340 * Enters a critical section, waiting for it to become available if necessary.
343 * crit [I/O] Critical section to enter
346 * STATUS_SUCCESS. The critical section is held by the caller.
349 * RtlInitializeCriticalSectionEx(),
350 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
351 * RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(),
352 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
354 NTSTATUS WINAPI
RtlEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
360 if (RtlTryEnterCriticalSection( crit
)) return STATUS_SUCCESS
;
361 for (count
= crit
->SpinCount
; count
> 0; count
--)
363 if (crit
->LockCount
> 0) break; /* more than one waiter, don't bother spinning */
364 if (crit
->LockCount
== -1) /* try again */
366 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1) goto done
;
372 if (InterlockedIncrement( &crit
->LockCount
))
374 if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
376 crit
->RecursionCount
++;
377 return STATUS_SUCCESS
;
380 /* Now wait for it */
381 RtlpWaitForCriticalSection( crit
);
384 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
385 crit
->RecursionCount
= 1;
386 return STATUS_SUCCESS
;
390 /***********************************************************************
391 * RtlTryEnterCriticalSection (NTDLL.@)
393 * Tries to enter a critical section without waiting.
396 * crit [I/O] Critical section to enter
399 * Success: TRUE. The critical section is held by the caller.
400 * Failure: FALSE. The critical section is currently held by another thread.
403 * RtlInitializeCriticalSectionEx(),
404 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
405 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
406 * RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount()
408 BOOL WINAPI
RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
411 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1)
413 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
414 crit
->RecursionCount
= 1;
417 else if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
419 InterlockedIncrement( &crit
->LockCount
);
420 crit
->RecursionCount
++;
427 /***********************************************************************
428 * RtlIsCriticalSectionLocked (NTDLL.@)
430 * Checks if the critical section is locked by any thread.
433 * crit [I/O] Critical section to check.
436 * Success: TRUE. The critical section is locked.
437 * Failure: FALSE. The critical section is not locked.
439 BOOL WINAPI
RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION
*crit
)
441 return crit
->RecursionCount
!= 0;
445 /***********************************************************************
446 * RtlIsCriticalSectionLockedByThread (NTDLL.@)
448 * Checks if the critical section is locked by the current thread.
451 * crit [I/O] Critical section to check.
454 * Success: TRUE. The critical section is locked.
455 * Failure: FALSE. The critical section is not locked.
457 BOOL WINAPI
RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION
*crit
)
459 return crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()) &&
460 crit
->RecursionCount
;
464 /***********************************************************************
465 * RtlLeaveCriticalSection (NTDLL.@)
467 * Leaves a critical section.
470 * crit [I/O] Critical section to leave.
476 * RtlInitializeCriticalSectionEx(),
477 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
478 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
479 * RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection()
481 NTSTATUS WINAPI
RtlLeaveCriticalSection( RTL_CRITICAL_SECTION
*crit
)
483 if (--crit
->RecursionCount
)
485 if (crit
->RecursionCount
> 0) InterlockedDecrement( &crit
->LockCount
);
486 else ERR( "section %p is not acquired\n", crit
);
490 crit
->OwningThread
= 0;
491 if (InterlockedDecrement( &crit
->LockCount
) >= 0)
493 /* someone is waiting */
494 RtlpUnWaitCriticalSection( crit
);
497 return STATUS_SUCCESS
;