dbghelp/dwarf: Don't unmap the fmap of a DWZ module twice.
[wine.git] / dlls / ntdll / sync.c
blobbdb39e59d6dac8220ea5fd0d1db1e755f637079e
1 /*
2 * Process synchronisation
4 * Copyright 1996, 1997, 1998 Marcus Meissner
5 * Copyright 1997, 1998, 1999 Alexandre Julliard
6 * Copyright 1999, 2000 Juergen Schmied
7 * Copyright 2003 Eric Pouech
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include <limits.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <time.h>
31 #include "ntstatus.h"
32 #define WIN32_NO_STATUS
33 #define NONAMELESSUNION
34 #include "windef.h"
35 #include "winternl.h"
36 #include "wine/debug.h"
37 #include "wine/list.h"
38 #include "ntdll_misc.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(sync);
41 WINE_DECLARE_DEBUG_CHANNEL(relay);
43 static const char *debugstr_timeout( const LARGE_INTEGER *timeout )
45 if (!timeout) return "(infinite)";
46 return wine_dbgstr_longlong( timeout->QuadPart );
49 /******************************************************************
50 * RtlRunOnceInitialize (NTDLL.@)
52 void WINAPI RtlRunOnceInitialize( RTL_RUN_ONCE *once )
54 once->Ptr = NULL;
57 /******************************************************************
58 * RtlRunOnceBeginInitialize (NTDLL.@)
60 DWORD WINAPI RtlRunOnceBeginInitialize( RTL_RUN_ONCE *once, ULONG flags, void **context )
62 if (flags & RTL_RUN_ONCE_CHECK_ONLY)
64 ULONG_PTR val = (ULONG_PTR)once->Ptr;
66 if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
67 if ((val & 3) != 2) return STATUS_UNSUCCESSFUL;
68 if (context) *context = (void *)(val & ~3);
69 return STATUS_SUCCESS;
72 for (;;)
74 ULONG_PTR next, val = (ULONG_PTR)once->Ptr;
76 switch (val & 3)
78 case 0: /* first time */
79 if (!InterlockedCompareExchangePointer( &once->Ptr,
80 (flags & RTL_RUN_ONCE_ASYNC) ? (void *)3 : (void *)1, 0 ))
81 return STATUS_PENDING;
82 break;
84 case 1: /* in progress, wait */
85 if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
86 next = val & ~3;
87 if (InterlockedCompareExchangePointer( &once->Ptr, (void *)((ULONG_PTR)&next | 1),
88 (void *)val ) == (void *)val)
89 NtWaitForKeyedEvent( 0, &next, FALSE, NULL );
90 break;
92 case 2: /* done */
93 if (context) *context = (void *)(val & ~3);
94 return STATUS_SUCCESS;
96 case 3: /* in progress, async */
97 if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
98 return STATUS_PENDING;
103 /******************************************************************
104 * RtlRunOnceComplete (NTDLL.@)
106 DWORD WINAPI RtlRunOnceComplete( RTL_RUN_ONCE *once, ULONG flags, void *context )
108 if ((ULONG_PTR)context & 3) return STATUS_INVALID_PARAMETER;
110 if (flags & RTL_RUN_ONCE_INIT_FAILED)
112 if (context) return STATUS_INVALID_PARAMETER;
113 if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
115 else context = (void *)((ULONG_PTR)context | 2);
117 for (;;)
119 ULONG_PTR val = (ULONG_PTR)once->Ptr;
121 switch (val & 3)
123 case 1: /* in progress */
124 if (InterlockedCompareExchangePointer( &once->Ptr, context, (void *)val ) != (void *)val) break;
125 val &= ~3;
126 while (val)
128 ULONG_PTR next = *(ULONG_PTR *)val;
129 NtReleaseKeyedEvent( 0, (void *)val, FALSE, NULL );
130 val = next;
132 return STATUS_SUCCESS;
134 case 3: /* in progress, async */
135 if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
136 if (InterlockedCompareExchangePointer( &once->Ptr, context, (void *)val ) != (void *)val) break;
137 return STATUS_SUCCESS;
139 default:
140 return STATUS_UNSUCCESSFUL;
146 /***********************************************************************
147 * Critical sections
148 ***********************************************************************/
151 static void *no_debug_info_marker = (void *)(ULONG_PTR)-1;
153 static BOOL crit_section_has_debuginfo( const RTL_CRITICAL_SECTION *crit )
155 return crit->DebugInfo != NULL && crit->DebugInfo != no_debug_info_marker;
158 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
160 HANDLE ret = crit->LockSemaphore;
161 if (!ret)
163 HANDLE sem;
164 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
165 if (!(ret = InterlockedCompareExchangePointer( &crit->LockSemaphore, sem, 0 )))
166 ret = sem;
167 else
168 NtClose(sem); /* somebody beat us to it */
170 return ret;
173 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
175 LARGE_INTEGER time = {.QuadPart = timeout * (LONGLONG)-10000000};
177 /* debug info is cleared by MakeCriticalSectionGlobal */
178 if (!crit_section_has_debuginfo( crit ))
180 HANDLE sem = get_semaphore( crit );
181 return NtWaitForSingleObject( sem, FALSE, &time );
183 else
185 int *lock = (int *)&crit->LockSemaphore;
186 while (!InterlockedCompareExchange( lock, 0, 1 ))
188 static const int zero;
189 /* this may wait longer than specified in case of multiple wake-ups */
190 if (RtlWaitOnAddress( lock, &zero, sizeof(int), &time ) == STATUS_TIMEOUT)
191 return STATUS_TIMEOUT;
193 return STATUS_WAIT_0;
197 /******************************************************************************
198 * RtlInitializeCriticalSection (NTDLL.@)
200 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
202 return RtlInitializeCriticalSectionEx( crit, 0, 0 );
206 /******************************************************************************
207 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
209 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
211 return RtlInitializeCriticalSectionEx( crit, spincount, 0 );
215 /******************************************************************************
216 * RtlInitializeCriticalSectionEx (NTDLL.@)
218 NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags )
220 if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT))
221 FIXME("(%p,%u,0x%08x) semi-stub\n", crit, spincount, flags);
223 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
224 * memory from a static pool to hold the debug info. Then heap.c could pass
225 * this flag rather than initialising the process heap CS by hand. If this
226 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
227 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
229 if (flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO)
230 crit->DebugInfo = no_debug_info_marker;
231 else
233 crit->DebugInfo = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG ));
234 if (crit->DebugInfo)
236 crit->DebugInfo->Type = 0;
237 crit->DebugInfo->CreatorBackTraceIndex = 0;
238 crit->DebugInfo->CriticalSection = crit;
239 crit->DebugInfo->ProcessLocksList.Blink = &crit->DebugInfo->ProcessLocksList;
240 crit->DebugInfo->ProcessLocksList.Flink = &crit->DebugInfo->ProcessLocksList;
241 crit->DebugInfo->EntryCount = 0;
242 crit->DebugInfo->ContentionCount = 0;
243 memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
246 crit->LockCount = -1;
247 crit->RecursionCount = 0;
248 crit->OwningThread = 0;
249 crit->LockSemaphore = 0;
250 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
251 crit->SpinCount = spincount & ~0x80000000;
252 return STATUS_SUCCESS;
256 /******************************************************************************
257 * RtlSetCriticalSectionSpinCount (NTDLL.@)
259 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
261 ULONG oldspincount = crit->SpinCount;
262 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
263 crit->SpinCount = spincount;
264 return oldspincount;
268 /******************************************************************************
269 * RtlDeleteCriticalSection (NTDLL.@)
271 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
273 crit->LockCount = -1;
274 crit->RecursionCount = 0;
275 crit->OwningThread = 0;
276 if (crit_section_has_debuginfo( crit ))
278 /* only free the ones we made in here */
279 if (!crit->DebugInfo->Spare[0])
281 RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
282 crit->DebugInfo = NULL;
285 else NtClose( crit->LockSemaphore );
286 crit->LockSemaphore = 0;
287 return STATUS_SUCCESS;
291 /******************************************************************************
292 * RtlpWaitForCriticalSection (NTDLL.@)
294 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
296 LONGLONG timeout = NtCurrentTeb()->Peb->CriticalSectionTimeout.QuadPart / -10000000;
298 /* Don't allow blocking on a critical section during process termination */
299 if (RtlDllShutdownInProgress())
301 WARN( "process %s is shutting down, returning STATUS_SUCCESS\n",
302 debugstr_w(NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer) );
303 return STATUS_SUCCESS;
306 for (;;)
308 EXCEPTION_RECORD rec;
309 NTSTATUS status = wait_semaphore( crit, 5 );
310 timeout -= 5;
312 if ( status == STATUS_TIMEOUT )
314 const char *name = NULL;
315 if (crit_section_has_debuginfo( crit )) name = (char *)crit->DebugInfo->Spare[0];
316 if (!name) name = "?";
317 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
318 crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
319 status = wait_semaphore( crit, 60 );
320 timeout -= 60;
322 if ( status == STATUS_TIMEOUT && TRACE_ON(relay) )
324 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
325 crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
326 status = wait_semaphore( crit, 300 );
327 timeout -= 300;
330 if (status == STATUS_WAIT_0) break;
332 /* Throw exception only for Wine internal locks */
333 if (!crit_section_has_debuginfo( crit ) || !crit->DebugInfo->Spare[0]) continue;
335 /* only throw deadlock exception if configured timeout is reached */
336 if (timeout > 0) continue;
338 rec.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
339 rec.ExceptionFlags = 0;
340 rec.ExceptionRecord = NULL;
341 rec.ExceptionAddress = RtlRaiseException; /* sic */
342 rec.NumberParameters = 1;
343 rec.ExceptionInformation[0] = (ULONG_PTR)crit;
344 RtlRaiseException( &rec );
346 if (crit_section_has_debuginfo( crit )) crit->DebugInfo->ContentionCount++;
347 return STATUS_SUCCESS;
351 /******************************************************************************
352 * RtlpUnWaitCriticalSection (NTDLL.@)
354 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
356 NTSTATUS ret;
358 /* debug info is cleared by MakeCriticalSectionGlobal */
359 if (!crit_section_has_debuginfo( crit ))
361 HANDLE sem = get_semaphore( crit );
362 ret = NtReleaseSemaphore( sem, 1, NULL );
364 else
366 int *lock = (int *)&crit->LockSemaphore;
367 InterlockedExchange( lock, 1 );
368 RtlWakeAddressSingle( lock );
369 ret = STATUS_SUCCESS;
371 if (ret) RtlRaiseStatus( ret );
372 return ret;
376 static inline void small_pause(void)
378 #ifdef __i386__
379 __asm__ __volatile__( "rep;nop" : : : "memory" );
380 #else
381 __asm__ __volatile__( "" : : : "memory" );
382 #endif
385 /******************************************************************************
386 * RtlEnterCriticalSection (NTDLL.@)
388 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
390 if (crit->SpinCount)
392 ULONG count;
394 if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
395 for (count = crit->SpinCount; count > 0; count--)
397 if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */
398 if (crit->LockCount == -1) /* try again */
400 if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1) goto done;
402 small_pause();
406 if (InterlockedIncrement( &crit->LockCount ))
408 if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
410 crit->RecursionCount++;
411 return STATUS_SUCCESS;
414 /* Now wait for it */
415 RtlpWaitForCriticalSection( crit );
417 done:
418 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
419 crit->RecursionCount = 1;
420 return STATUS_SUCCESS;
424 /******************************************************************************
425 * RtlTryEnterCriticalSection (NTDLL.@)
427 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
429 BOOL ret = FALSE;
430 if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1)
432 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
433 crit->RecursionCount = 1;
434 ret = TRUE;
436 else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
438 InterlockedIncrement( &crit->LockCount );
439 crit->RecursionCount++;
440 ret = TRUE;
442 return ret;
446 /******************************************************************************
447 * RtlIsCriticalSectionLocked (NTDLL.@)
449 BOOL WINAPI RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION *crit )
451 return crit->RecursionCount != 0;
455 /******************************************************************************
456 * RtlIsCriticalSectionLockedByThread (NTDLL.@)
458 BOOL WINAPI RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION *crit )
460 return crit->OwningThread == ULongToHandle(GetCurrentThreadId()) &&
461 crit->RecursionCount;
465 /******************************************************************************
466 * RtlLeaveCriticalSection (NTDLL.@)
468 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
470 if (--crit->RecursionCount)
472 if (crit->RecursionCount > 0) InterlockedDecrement( &crit->LockCount );
473 else ERR( "section %p is not acquired\n", crit );
475 else
477 crit->OwningThread = 0;
478 if (InterlockedDecrement( &crit->LockCount ) >= 0)
480 /* someone is waiting */
481 RtlpUnWaitCriticalSection( crit );
484 return STATUS_SUCCESS;
487 /******************************************************************
488 * RtlRunOnceExecuteOnce (NTDLL.@)
490 DWORD WINAPI RtlRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN func,
491 void *param, void **context )
493 DWORD ret = RtlRunOnceBeginInitialize( once, 0, context );
495 if (ret != STATUS_PENDING) return ret;
497 if (!func( once, param, context ))
499 RtlRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
500 return STATUS_UNSUCCESSFUL;
503 return RtlRunOnceComplete( once, 0, context ? *context : NULL );
506 struct srw_lock
508 short exclusive_waiters;
510 /* Number of shared owners, or -1 if owned exclusive.
512 * Sadly Windows has no equivalent to FUTEX_WAIT_BITSET, so in order to wake
513 * up *only* exclusive or *only* shared waiters (and thus avoid spurious
514 * wakeups), we need to wait on two different addresses.
515 * RtlAcquireSRWLockShared() needs to know the values of "exclusive_waiters"
516 * and "owners", but RtlAcquireSRWLockExclusive() only needs to know the
517 * value of "owners", so the former can wait on the entire structure, and
518 * the latter waits only on the "owners" member. Note then that "owners"
519 * must not be the first element in the structure.
521 short owners;
523 C_ASSERT( sizeof(struct srw_lock) == 4 );
525 /***********************************************************************
526 * RtlInitializeSRWLock (NTDLL.@)
528 * NOTES
529 * Please note that SRWLocks do not keep track of the owner of a lock.
530 * It doesn't make any difference which thread for example unlocks an
531 * SRWLock (see corresponding tests). This implementation uses two
532 * keyed events (one for the exclusive waiters and one for the shared
533 * waiters) and is limited to 2^15-1 waiting threads.
535 void WINAPI RtlInitializeSRWLock( RTL_SRWLOCK *lock )
537 lock->Ptr = NULL;
540 /***********************************************************************
541 * RtlAcquireSRWLockExclusive (NTDLL.@)
543 * NOTES
544 * Unlike RtlAcquireResourceExclusive this function doesn't allow
545 * nested calls from the same thread. "Upgrading" a shared access lock
546 * to an exclusive access lock also doesn't seem to be supported.
548 void WINAPI RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
550 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
552 InterlockedIncrement16( &u.s->exclusive_waiters );
554 for (;;)
556 union { struct srw_lock s; LONG l; } old, new;
557 BOOL wait;
561 old.s = *u.s;
562 new.s = old.s;
564 if (!old.s.owners)
566 /* Not locked exclusive or shared. We can try to grab it. */
567 new.s.owners = -1;
568 --new.s.exclusive_waiters;
569 wait = FALSE;
571 else
573 wait = TRUE;
575 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
577 if (!wait) return;
578 RtlWaitOnAddress( &u.s->owners, &new.s.owners, sizeof(short), NULL );
582 /***********************************************************************
583 * RtlAcquireSRWLockShared (NTDLL.@)
585 * NOTES
586 * Do not call this function recursively - it will only succeed when
587 * there are no threads waiting for an exclusive lock!
589 void WINAPI RtlAcquireSRWLockShared( RTL_SRWLOCK *lock )
591 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
593 for (;;)
595 union { struct srw_lock s; LONG l; } old, new;
596 BOOL wait;
600 old.s = *u.s;
601 new = old;
603 if (old.s.owners != -1 && !old.s.exclusive_waiters)
605 /* Not locked exclusive, and no exclusive waiters.
606 * We can try to grab it. */
607 ++new.s.owners;
608 wait = FALSE;
610 else
612 wait = TRUE;
614 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
616 if (!wait) return;
617 RtlWaitOnAddress( u.s, &new.s, sizeof(struct srw_lock), NULL );
621 /***********************************************************************
622 * RtlReleaseSRWLockExclusive (NTDLL.@)
624 void WINAPI RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock )
626 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
627 union { struct srw_lock s; LONG l; } old, new;
631 old.s = *u.s;
632 new = old;
634 if (old.s.owners != -1) ERR("Lock %p is not owned exclusive!\n", lock);
636 new.s.owners = 0;
637 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
639 if (new.s.exclusive_waiters)
640 RtlWakeAddressSingle( &u.s->owners );
641 else
642 RtlWakeAddressAll( u.s );
645 /***********************************************************************
646 * RtlReleaseSRWLockShared (NTDLL.@)
648 void WINAPI RtlReleaseSRWLockShared( RTL_SRWLOCK *lock )
650 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
651 union { struct srw_lock s; LONG l; } old, new;
655 old.s = *u.s;
656 new = old;
658 if (old.s.owners == -1) ERR("Lock %p is owned exclusive!\n", lock);
659 else if (!old.s.owners) ERR("Lock %p is not owned shared!\n", lock);
661 --new.s.owners;
662 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
664 if (!new.s.owners)
665 RtlWakeAddressSingle( &u.s->owners );
668 /***********************************************************************
669 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
671 * NOTES
672 * Similarly to AcquireSRWLockExclusive, recursive calls are not allowed
673 * and will fail with a FALSE return value.
675 BOOLEAN WINAPI RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
677 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
678 union { struct srw_lock s; LONG l; } old, new;
679 BOOLEAN ret;
683 old.s = *u.s;
684 new.s = old.s;
686 if (!old.s.owners)
688 /* Not locked exclusive or shared. We can try to grab it. */
689 new.s.owners = -1;
690 ret = TRUE;
692 else
694 ret = FALSE;
696 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
698 return ret;
701 /***********************************************************************
702 * RtlTryAcquireSRWLockShared (NTDLL.@)
704 BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock )
706 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
707 union { struct srw_lock s; LONG l; } old, new;
708 BOOLEAN ret;
712 old.s = *u.s;
713 new.s = old.s;
715 if (old.s.owners != -1 && !old.s.exclusive_waiters)
717 /* Not locked exclusive, and no exclusive waiters.
718 * We can try to grab it. */
719 ++new.s.owners;
720 ret = TRUE;
722 else
724 ret = FALSE;
726 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
728 return ret;
731 /***********************************************************************
732 * RtlInitializeConditionVariable (NTDLL.@)
734 * Initializes the condition variable with NULL.
736 * PARAMS
737 * variable [O] condition variable
739 * RETURNS
740 * Nothing.
742 void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable )
744 variable->Ptr = NULL;
747 /***********************************************************************
748 * RtlWakeConditionVariable (NTDLL.@)
750 * Wakes up one thread waiting on the condition variable.
752 * PARAMS
753 * variable [I/O] condition variable to wake up.
755 * RETURNS
756 * Nothing.
758 * NOTES
759 * The calling thread does not have to own any lock in order to call
760 * this function.
762 void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
764 InterlockedIncrement( (int *)&variable->Ptr );
765 RtlWakeAddressSingle( variable );
768 /***********************************************************************
769 * RtlWakeAllConditionVariable (NTDLL.@)
771 * See WakeConditionVariable, wakes up all waiting threads.
773 void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
775 InterlockedIncrement( (int *)&variable->Ptr );
776 RtlWakeAddressAll( variable );
779 /***********************************************************************
780 * RtlSleepConditionVariableCS (NTDLL.@)
782 * Atomically releases the critical section and suspends the thread,
783 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
784 * the critical section again and returns.
786 * PARAMS
787 * variable [I/O] condition variable
788 * crit [I/O] critical section to leave temporarily
789 * timeout [I] timeout
791 * RETURNS
792 * see NtWaitForKeyedEvent for all possible return values.
794 NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, RTL_CRITICAL_SECTION *crit,
795 const LARGE_INTEGER *timeout )
797 int value = *(int *)&variable->Ptr;
798 NTSTATUS status;
800 RtlLeaveCriticalSection( crit );
801 status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout );
802 RtlEnterCriticalSection( crit );
803 return status;
806 /***********************************************************************
807 * RtlSleepConditionVariableSRW (NTDLL.@)
809 * Atomically releases the SRWLock and suspends the thread,
810 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
811 * the SRWLock again with the same access rights and returns.
813 * PARAMS
814 * variable [I/O] condition variable
815 * lock [I/O] SRWLock to leave temporarily
816 * timeout [I] timeout
817 * flags [I] type of the current lock (exclusive / shared)
819 * RETURNS
820 * see NtWaitForKeyedEvent for all possible return values.
822 * NOTES
823 * the behaviour is undefined if the thread doesn't own the lock.
825 NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, RTL_SRWLOCK *lock,
826 const LARGE_INTEGER *timeout, ULONG flags )
828 int value = *(int *)&variable->Ptr;
829 NTSTATUS status;
831 if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
832 RtlReleaseSRWLockShared( lock );
833 else
834 RtlReleaseSRWLockExclusive( lock );
836 status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout );
838 if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
839 RtlAcquireSRWLockShared( lock );
840 else
841 RtlAcquireSRWLockExclusive( lock );
842 return status;
845 /* RtlWaitOnAddress() and RtlWakeAddress*(), hereafter referred to as "Win32
846 * futexes", offer futex-like semantics with a variable set of address sizes,
847 * but are limited to a single process. They are also fair—the documentation
848 * specifies this, and tests bear it out.
850 * On Windows they are implemented using NtAlertThreadByThreadId and
851 * NtWaitForAlertByThreadId, which manipulate a single flag (similar to an
852 * auto-reset event) per thread. This can be tested by attempting to wake a
853 * thread waiting in RtlWaitOnAddress() via NtAlertThreadByThreadId.
856 struct futex_entry
858 struct list entry;
859 const void *addr;
860 DWORD tid;
863 struct futex_queue
865 struct list queue;
866 LONG lock;
869 static struct futex_queue futex_queues[256];
871 static struct futex_queue *get_futex_queue( const void *addr )
873 ULONG_PTR val = (ULONG_PTR)addr;
875 return &futex_queues[(val >> 4) % ARRAY_SIZE(futex_queues)];
878 static void spin_lock( LONG *lock )
880 while (InterlockedCompareExchange( lock, -1, 0 ))
881 YieldProcessor();
884 static void spin_unlock( LONG *lock )
886 InterlockedExchange( lock, 0 );
889 static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size )
891 switch (size)
893 case 1:
894 return (*(const UCHAR *)addr == *(const UCHAR *)cmp);
895 case 2:
896 return (*(const USHORT *)addr == *(const USHORT *)cmp);
897 case 4:
898 return (*(const ULONG *)addr == *(const ULONG *)cmp);
899 case 8:
900 return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp);
903 return FALSE;
906 /***********************************************************************
907 * RtlWaitOnAddress (NTDLL.@)
909 NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size,
910 const LARGE_INTEGER *timeout )
912 struct futex_queue *queue = get_futex_queue( addr );
913 struct futex_entry entry;
914 NTSTATUS ret;
916 TRACE("addr %p cmp %p size %#Ix timeout %s\n", addr, cmp, size, debugstr_timeout( timeout ));
918 if (size != 1 && size != 2 && size != 4 && size != 8)
919 return STATUS_INVALID_PARAMETER;
921 entry.addr = addr;
922 entry.tid = GetCurrentThreadId();
924 spin_lock( &queue->lock );
926 /* Do the comparison inside of the spinlock, to reduce spurious wakeups. */
928 if (!compare_addr( addr, cmp, size ))
930 spin_unlock( &queue->lock );
931 return STATUS_SUCCESS;
934 if (!queue->queue.next)
935 list_init( &queue->queue );
936 list_add_tail( &queue->queue, &entry.entry );
938 spin_unlock( &queue->lock );
940 ret = NtWaitForAlertByThreadId( NULL, timeout );
942 spin_lock( &queue->lock );
943 /* We may have already been removed by a call to RtlWakeAddressSingle(). */
944 if (entry.addr)
945 list_remove( &entry.entry );
946 spin_unlock( &queue->lock );
948 TRACE("returning %#x\n", ret);
950 if (ret == STATUS_ALERTED) ret = STATUS_SUCCESS;
951 return ret;
954 /***********************************************************************
955 * RtlWakeAddressAll (NTDLL.@)
957 void WINAPI RtlWakeAddressAll( const void *addr )
959 struct futex_queue *queue = get_futex_queue( addr );
960 unsigned int count = 0, i;
961 struct futex_entry *entry;
962 DWORD tids[256];
964 TRACE("%p\n", addr);
966 if (!addr) return;
968 spin_lock( &queue->lock );
970 if (!queue->queue.next)
971 list_init(&queue->queue);
973 LIST_FOR_EACH_ENTRY( entry, &queue->queue, struct futex_entry, entry )
975 if (entry->addr == addr)
977 /* Try to buffer wakes, so that we don't make a system call while
978 * holding a spinlock. */
979 if (count < ARRAY_SIZE(tids))
980 tids[count++] = entry->tid;
981 else
982 NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)entry->tid );
986 spin_unlock( &queue->lock );
988 for (i = 0; i < count; ++i)
989 NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tids[i] );
992 /***********************************************************************
993 * RtlWakeAddressSingle (NTDLL.@)
995 void WINAPI RtlWakeAddressSingle( const void *addr )
997 struct futex_queue *queue = get_futex_queue( addr );
998 struct futex_entry *entry;
999 DWORD tid = 0;
1001 TRACE("%p\n", addr);
1003 if (!addr) return;
1005 spin_lock( &queue->lock );
1007 if (!queue->queue.next)
1008 list_init(&queue->queue);
1010 LIST_FOR_EACH_ENTRY( entry, &queue->queue, struct futex_entry, entry )
1012 if (entry->addr == addr)
1014 /* Try to buffer wakes, so that we don't make a system call while
1015 * holding a spinlock. */
1016 tid = entry->tid;
1018 /* Remove this entry from the queue, so that a simultaneous call to
1019 * RtlWakeAddressSingle() will not also wake it—two simultaneous
1020 * calls must wake at least two waiters if they exist. */
1021 entry->addr = NULL;
1022 list_remove( &entry->entry );
1023 break;
1027 spin_unlock( &queue->lock );
1029 if (tid) NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tid );