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
22 #include "wine/port.h"
28 #include <sys/types.h>
29 #ifdef HAVE_SYS_SYSCALL_H
30 #include <sys/syscall.h>
34 #define WIN32_NO_STATUS
37 #include "wine/debug.h"
38 #include "ntdll_misc.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
41 WINE_DECLARE_DEBUG_CHANNEL(relay);
43 static inline LONG interlocked_inc( PLONG dest )
45 return interlocked_xchg_add( dest, 1 ) + 1;
48 static inline LONG interlocked_dec( PLONG dest )
50 return interlocked_xchg_add( dest, -1 ) - 1;
53 static inline void small_pause(void)
56 __asm__ __volatile__( "rep;nop" : : : "memory" );
58 __asm__ __volatile__( "" : : : "memory" );
64 static inline int futex_wait( int *addr, int val, struct timespec *timeout )
66 return syscall( SYS_futex, addr, 0/*FUTEX_WAIT*/, val, timeout, 0, 0 );
69 static inline int futex_wake( int *addr, int val )
71 return syscall( SYS_futex, addr, 1/*FUTEX_WAKE*/, val, NULL, 0, 0 );
74 static inline int use_futexes(void)
76 static int supported = -1;
80 futex_wait( &supported, 10, NULL );
81 supported = (errno != ENOSYS);
86 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
89 struct timespec timespec;
91 if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
93 timespec.tv_sec = timeout;
95 while ((val = interlocked_cmpxchg( (int *)&crit->LockSemaphore, 0, 1 )) != 1)
97 /* note: this may wait longer than specified in case of signals or */
98 /* multiple wake-ups, but that shouldn't be a problem */
99 if (futex_wait( (int *)&crit->LockSemaphore, val, ×pec ) == -1 && errno == ETIMEDOUT)
100 return STATUS_TIMEOUT;
102 return STATUS_WAIT_0;
105 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
107 if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
109 *(int *)&crit->LockSemaphore = 1;
110 futex_wake( (int *)&crit->LockSemaphore, 1 );
111 return STATUS_SUCCESS;
114 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
116 if (!use_futexes()) NtClose( crit->LockSemaphore );
119 #elif defined(__APPLE__)
121 #include <mach/mach.h>
122 #include <mach/task.h>
123 #include <mach/semaphore.h>
125 static inline semaphore_t get_mach_semaphore( RTL_CRITICAL_SECTION *crit )
127 semaphore_t ret = *(int *)&crit->LockSemaphore;
131 if (semaphore_create( mach_task_self(), &sem, SYNC_POLICY_FIFO, 0 )) return 0;
132 if (!(ret = interlocked_cmpxchg( (int *)&crit->LockSemaphore, sem, 0 )))
135 semaphore_destroy( mach_task_self(), sem ); /* somebody beat us to it */
140 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
142 mach_timespec_t timespec;
143 semaphore_t sem = get_mach_semaphore( crit );
145 timespec.tv_sec = timeout;
146 timespec.tv_nsec = 0;
149 switch( semaphore_timedwait( sem, timespec ))
152 return STATUS_WAIT_0;
154 continue; /* got a signal, restart */
155 case KERN_OPERATION_TIMED_OUT:
156 return STATUS_TIMEOUT;
158 return STATUS_INVALID_HANDLE;
163 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
165 semaphore_t sem = get_mach_semaphore( crit );
166 semaphore_signal( sem );
167 return STATUS_SUCCESS;
170 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
172 semaphore_destroy( mach_task_self(), *(int *)&crit->LockSemaphore );
175 #else /* __APPLE__ */
177 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
179 return STATUS_NOT_IMPLEMENTED;
182 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
184 return STATUS_NOT_IMPLEMENTED;
187 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
189 NtClose( crit->LockSemaphore );
194 /***********************************************************************
197 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
199 HANDLE ret = crit->LockSemaphore;
203 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
204 if (!(ret = interlocked_cmpxchg_ptr( &crit->LockSemaphore, sem, 0 )))
207 NtClose(sem); /* somebody beat us to it */
212 /***********************************************************************
215 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
219 /* debug info is cleared by MakeCriticalSectionGlobal */
220 if (!crit->DebugInfo || ((ret = fast_wait( crit, timeout )) == STATUS_NOT_IMPLEMENTED))
222 HANDLE sem = get_semaphore( crit );
225 time.QuadPart = timeout * (LONGLONG)-10000000;
226 ret = NTDLL_wait_for_multiple_objects( 1, &sem, 0, &time, 0 );
231 /***********************************************************************
232 * RtlInitializeCriticalSection (NTDLL.@)
234 * Initialises a new critical section.
237 * crit [O] Critical section to initialise
243 * RtlInitializeCriticalSectionEx(),
244 * RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(),
245 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
246 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
248 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
250 return RtlInitializeCriticalSectionEx( crit, 0, 0 );
253 /***********************************************************************
254 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
256 * Initialises a new critical section with a given spin count.
259 * crit [O] Critical section to initialise
260 * spincount [I] Spin count for crit
266 * Available on NT4 SP3 or later.
269 * RtlInitializeCriticalSectionEx(),
270 * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
271 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
272 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
274 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
276 return RtlInitializeCriticalSectionEx( crit, spincount, 0 );
279 /***********************************************************************
280 * RtlInitializeCriticalSectionEx (NTDLL.@)
282 * Initialises a new critical section with a given spin count and flags.
285 * crit [O] Critical section to initialise.
286 * spincount [I] Number of times to spin upon contention.
287 * flags [I] RTL_CRITICAL_SECTION_FLAG_ flags from winnt.h.
293 * Available on Vista or later.
296 * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
297 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
298 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
300 NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags )
302 if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT))
303 FIXME("(%p,%u,0x%08x) semi-stub\n", crit, spincount, flags);
305 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
306 * memory from a static pool to hold the debug info. Then heap.c could pass
307 * this flag rather than initialising the process heap CS by hand. If this
308 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
309 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
311 if (flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO)
312 crit->DebugInfo = NULL;
314 crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG));
318 crit->DebugInfo->Type = 0;
319 crit->DebugInfo->CreatorBackTraceIndex = 0;
320 crit->DebugInfo->CriticalSection = crit;
321 crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList);
322 crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList);
323 crit->DebugInfo->EntryCount = 0;
324 crit->DebugInfo->ContentionCount = 0;
325 memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
327 crit->LockCount = -1;
328 crit->RecursionCount = 0;
329 crit->OwningThread = 0;
330 crit->LockSemaphore = 0;
331 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
332 crit->SpinCount = spincount & ~0x80000000;
333 return STATUS_SUCCESS;
336 /***********************************************************************
337 * RtlSetCriticalSectionSpinCount (NTDLL.@)
339 * Sets the spin count of a critical section.
342 * crit [I/O] Critical section
343 * spincount [I] Spin count for crit
346 * The previous spin count.
349 * If the system is not SMP, spincount is ignored and set to 0.
352 * RtlInitializeCriticalSectionEx(),
353 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
354 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
355 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
357 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
359 ULONG oldspincount = crit->SpinCount;
360 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
361 crit->SpinCount = spincount;
365 /***********************************************************************
366 * RtlDeleteCriticalSection (NTDLL.@)
368 * Frees the resources used by a critical section.
371 * crit [I/O] Critical section to free
377 * RtlInitializeCriticalSectionEx(),
378 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
379 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
380 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
382 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
384 crit->LockCount = -1;
385 crit->RecursionCount = 0;
386 crit->OwningThread = 0;
389 /* only free the ones we made in here */
390 if (!crit->DebugInfo->Spare[0])
392 RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
393 crit->DebugInfo = NULL;
395 close_semaphore( crit );
397 else NtClose( crit->LockSemaphore );
398 crit->LockSemaphore = 0;
399 return STATUS_SUCCESS;
403 /***********************************************************************
404 * RtlpWaitForCriticalSection (NTDLL.@)
406 * Waits for a busy critical section to become free.
409 * crit [I/O] Critical section to wait for
415 * Use RtlEnterCriticalSection() instead of this function as it is often much
419 * RtlInitializeCriticalSectionEx(),
420 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
421 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
422 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
424 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
428 EXCEPTION_RECORD rec;
429 NTSTATUS status = wait_semaphore( crit, 5 );
431 if ( status == STATUS_TIMEOUT )
433 const char *name = NULL;
434 if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[0];
435 if (!name) name = "?";
436 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
437 crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
438 status = wait_semaphore( crit, 60 );
439 if ( status == STATUS_TIMEOUT && TRACE_ON(relay) )
441 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
442 crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
443 status = wait_semaphore( crit, 300 );
446 if (status == STATUS_WAIT_0) break;
448 /* Throw exception only for Wine internal locks */
449 if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[0])) continue;
451 rec.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
452 rec.ExceptionFlags = 0;
453 rec.ExceptionRecord = NULL;
454 rec.ExceptionAddress = RtlRaiseException; /* sic */
455 rec.NumberParameters = 1;
456 rec.ExceptionInformation[0] = (ULONG_PTR)crit;
457 RtlRaiseException( &rec );
459 if (crit->DebugInfo) crit->DebugInfo->ContentionCount++;
460 return STATUS_SUCCESS;
464 /***********************************************************************
465 * RtlpUnWaitCriticalSection (NTDLL.@)
467 * Notifies other threads waiting on the busy critical section that it has
471 * crit [I/O] Critical section
474 * Success: STATUS_SUCCESS.
475 * Failure: Any error returned by NtReleaseSemaphore()
478 * Use RtlLeaveCriticalSection() instead of this function as it is often much
482 * RtlInitializeCriticalSectionEx(),
483 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
484 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
485 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
487 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
491 /* debug info is cleared by MakeCriticalSectionGlobal */
492 if (!crit->DebugInfo || ((ret = fast_wake( crit )) == STATUS_NOT_IMPLEMENTED))
494 HANDLE sem = get_semaphore( crit );
495 ret = NtReleaseSemaphore( sem, 1, NULL );
497 if (ret) RtlRaiseStatus( ret );
502 /***********************************************************************
503 * RtlEnterCriticalSection (NTDLL.@)
505 * Enters a critical section, waiting for it to become available if necessary.
508 * crit [I/O] Critical section to enter
511 * STATUS_SUCCESS. The critical section is held by the caller.
514 * RtlInitializeCriticalSectionEx(),
515 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
516 * RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(),
517 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
519 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
525 if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
526 for (count = crit->SpinCount; count > 0; count--)
528 if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */
529 if (crit->LockCount == -1) /* try again */
531 if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1) goto done;
537 if (interlocked_inc( &crit->LockCount ))
539 if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
541 crit->RecursionCount++;
542 return STATUS_SUCCESS;
545 /* Now wait for it */
546 RtlpWaitForCriticalSection( crit );
549 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
550 crit->RecursionCount = 1;
551 return STATUS_SUCCESS;
555 /***********************************************************************
556 * RtlTryEnterCriticalSection (NTDLL.@)
558 * Tries to enter a critical section without waiting.
561 * crit [I/O] Critical section to enter
564 * Success: TRUE. The critical section is held by the caller.
565 * Failure: FALSE. The critical section is currently held by another thread.
568 * RtlInitializeCriticalSectionEx(),
569 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
570 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
571 * RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount()
573 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
576 if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1)
578 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
579 crit->RecursionCount = 1;
582 else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
584 interlocked_inc( &crit->LockCount );
585 crit->RecursionCount++;
592 /***********************************************************************
593 * RtlLeaveCriticalSection (NTDLL.@)
595 * Leaves a critical section.
598 * crit [I/O] Critical section to leave.
604 * RtlInitializeCriticalSectionEx(),
605 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
606 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
607 * RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection()
609 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
611 if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
614 crit->OwningThread = 0;
615 if (interlocked_dec( &crit->LockCount ) >= 0)
617 /* someone is waiting */
618 RtlpUnWaitCriticalSection( crit );
621 return STATUS_SUCCESS;