gdiplus: In GdipImageSelectActiveFrame rely on codec->select_func() to fail.
[wine.git] / dlls / ntdll / sync.c
blobfa64917029af262d8af414ee09a6cfe5395537b0
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 #include "windef.h"
34 #include "winternl.h"
35 #include "wine/debug.h"
36 #include "wine/list.h"
37 #include "ntdll_misc.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(sync);
40 WINE_DECLARE_DEBUG_CHANNEL(relay);
42 static const char *debugstr_timeout( const LARGE_INTEGER *timeout )
44 if (!timeout) return "(infinite)";
45 return wine_dbgstr_longlong( timeout->QuadPart );
48 /******************************************************************
49 * RtlRunOnceInitialize (NTDLL.@)
51 void WINAPI RtlRunOnceInitialize( RTL_RUN_ONCE *once )
53 once->Ptr = NULL;
56 /******************************************************************
57 * RtlRunOnceBeginInitialize (NTDLL.@)
59 DWORD WINAPI RtlRunOnceBeginInitialize( RTL_RUN_ONCE *once, ULONG flags, void **context )
61 if (flags & RTL_RUN_ONCE_CHECK_ONLY)
63 ULONG_PTR val = (ULONG_PTR)once->Ptr;
65 if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
66 if ((val & 3) != 2) return STATUS_UNSUCCESSFUL;
67 if (context) *context = (void *)(val & ~3);
68 return STATUS_SUCCESS;
71 for (;;)
73 ULONG_PTR next, val = (ULONG_PTR)once->Ptr;
75 switch (val & 3)
77 case 0: /* first time */
78 if (!InterlockedCompareExchangePointer( &once->Ptr,
79 (flags & RTL_RUN_ONCE_ASYNC) ? (void *)3 : (void *)1, 0 ))
80 return STATUS_PENDING;
81 break;
83 case 1: /* in progress, wait */
84 if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
85 next = val & ~3;
86 if (InterlockedCompareExchangePointer( &once->Ptr, (void *)((ULONG_PTR)&next | 1),
87 (void *)val ) == (void *)val)
88 NtWaitForKeyedEvent( 0, &next, FALSE, NULL );
89 break;
91 case 2: /* done */
92 if (context) *context = (void *)(val & ~3);
93 return STATUS_SUCCESS;
95 case 3: /* in progress, async */
96 if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
97 return STATUS_PENDING;
102 /******************************************************************
103 * RtlRunOnceComplete (NTDLL.@)
105 DWORD WINAPI RtlRunOnceComplete( RTL_RUN_ONCE *once, ULONG flags, void *context )
107 if ((ULONG_PTR)context & 3) return STATUS_INVALID_PARAMETER;
109 if (flags & RTL_RUN_ONCE_INIT_FAILED)
111 if (context) return STATUS_INVALID_PARAMETER;
112 if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
114 else context = (void *)((ULONG_PTR)context | 2);
116 for (;;)
118 ULONG_PTR val = (ULONG_PTR)once->Ptr;
120 switch (val & 3)
122 case 1: /* in progress */
123 if (InterlockedCompareExchangePointer( &once->Ptr, context, (void *)val ) != (void *)val) break;
124 val &= ~3;
125 while (val)
127 ULONG_PTR next = *(ULONG_PTR *)val;
128 NtReleaseKeyedEvent( 0, (void *)val, FALSE, NULL );
129 val = next;
131 return STATUS_SUCCESS;
133 case 3: /* in progress, async */
134 if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
135 if (InterlockedCompareExchangePointer( &once->Ptr, context, (void *)val ) != (void *)val) break;
136 return STATUS_SUCCESS;
138 default:
139 return STATUS_UNSUCCESSFUL;
145 /***********************************************************************
146 * Critical sections
147 ***********************************************************************/
150 static void *no_debug_info_marker = (void *)(ULONG_PTR)-1;
152 static BOOL crit_section_has_debuginfo( const RTL_CRITICAL_SECTION *crit )
154 return crit->DebugInfo != NULL && crit->DebugInfo != no_debug_info_marker;
157 static const char *crit_section_get_name( const RTL_CRITICAL_SECTION *crit )
159 if (crit_section_has_debuginfo( crit ))
160 return (char *)crit->DebugInfo->Spare[0];
161 return "?";
164 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
166 HANDLE ret = crit->LockSemaphore;
167 if (!ret)
169 HANDLE sem;
170 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
171 if (!(ret = InterlockedCompareExchangePointer( &crit->LockSemaphore, sem, 0 )))
172 ret = sem;
173 else
174 NtClose(sem); /* somebody beat us to it */
176 return ret;
179 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
181 LARGE_INTEGER time = {.QuadPart = timeout * (LONGLONG)-10000000};
183 /* debug info is cleared by MakeCriticalSectionGlobal */
184 if (!crit_section_has_debuginfo( crit ))
186 HANDLE sem = get_semaphore( crit );
187 return NtWaitForSingleObject( sem, FALSE, &time );
189 else
191 LONG *lock = (LONG *)&crit->LockSemaphore;
192 while (!InterlockedCompareExchange( lock, 0, 1 ))
194 static const LONG zero;
195 /* this may wait longer than specified in case of multiple wake-ups */
196 if (RtlWaitOnAddress( lock, &zero, sizeof(LONG), &time ) == STATUS_TIMEOUT)
197 return STATUS_TIMEOUT;
199 return STATUS_WAIT_0;
203 /******************************************************************************
204 * RtlInitializeCriticalSection (NTDLL.@)
206 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
208 return RtlInitializeCriticalSectionEx( crit, 0, 0 );
212 /******************************************************************************
213 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
215 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
217 return RtlInitializeCriticalSectionEx( crit, spincount, 0 );
221 /******************************************************************************
222 * RtlInitializeCriticalSectionEx (NTDLL.@)
224 NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags )
226 if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT))
227 FIXME("(%p,%lu,0x%08lx) semi-stub\n", crit, spincount, flags);
229 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
230 * memory from a static pool to hold the debug info. Then heap.c could pass
231 * this flag rather than initialising the process heap CS by hand. If this
232 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
233 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
235 if (flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO)
236 crit->DebugInfo = no_debug_info_marker;
237 else
239 crit->DebugInfo = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG ));
240 if (crit->DebugInfo)
242 crit->DebugInfo->Type = 0;
243 crit->DebugInfo->CreatorBackTraceIndex = 0;
244 crit->DebugInfo->CriticalSection = crit;
245 crit->DebugInfo->ProcessLocksList.Blink = &crit->DebugInfo->ProcessLocksList;
246 crit->DebugInfo->ProcessLocksList.Flink = &crit->DebugInfo->ProcessLocksList;
247 crit->DebugInfo->EntryCount = 0;
248 crit->DebugInfo->ContentionCount = 0;
249 memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
252 crit->LockCount = -1;
253 crit->RecursionCount = 0;
254 crit->OwningThread = 0;
255 crit->LockSemaphore = 0;
256 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
257 crit->SpinCount = spincount & ~0x80000000;
258 return STATUS_SUCCESS;
262 /******************************************************************************
263 * RtlSetCriticalSectionSpinCount (NTDLL.@)
265 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
267 ULONG oldspincount = crit->SpinCount;
268 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
269 crit->SpinCount = spincount;
270 return oldspincount;
274 /******************************************************************************
275 * RtlDeleteCriticalSection (NTDLL.@)
277 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
279 crit->LockCount = -1;
280 crit->RecursionCount = 0;
281 crit->OwningThread = 0;
282 if (crit_section_has_debuginfo( crit ))
284 /* only free the ones we made in here */
285 if (!crit->DebugInfo->Spare[0])
287 RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
288 crit->DebugInfo = NULL;
291 else NtClose( crit->LockSemaphore );
292 crit->LockSemaphore = 0;
293 return STATUS_SUCCESS;
297 /******************************************************************************
298 * RtlpWaitForCriticalSection (NTDLL.@)
300 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
302 unsigned int timeout = 5;
304 /* Don't allow blocking on a critical section during process termination */
305 if (RtlDllShutdownInProgress())
307 WARN( "process %s is shutting down, returning STATUS_SUCCESS\n",
308 debugstr_w(NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer) );
309 return STATUS_SUCCESS;
312 for (;;)
314 NTSTATUS status = wait_semaphore( crit, timeout );
316 if (status == STATUS_WAIT_0) break;
318 timeout = (TRACE_ON(relay) ? 300 : 60);
320 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (%u sec)\n",
321 crit, debugstr_a(crit_section_get_name(crit)), GetCurrentThreadId(), HandleToULong(crit->OwningThread), timeout );
323 if (crit_section_has_debuginfo( crit )) crit->DebugInfo->ContentionCount++;
324 return STATUS_SUCCESS;
328 /******************************************************************************
329 * RtlpUnWaitCriticalSection (NTDLL.@)
331 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
333 NTSTATUS ret;
335 /* debug info is cleared by MakeCriticalSectionGlobal */
336 if (!crit_section_has_debuginfo( crit ))
338 HANDLE sem = get_semaphore( crit );
339 ret = NtReleaseSemaphore( sem, 1, NULL );
341 else
343 LONG *lock = (LONG *)&crit->LockSemaphore;
344 InterlockedExchange( lock, 1 );
345 RtlWakeAddressSingle( lock );
346 ret = STATUS_SUCCESS;
348 if (ret) RtlRaiseStatus( ret );
349 return ret;
353 /******************************************************************************
354 * RtlEnterCriticalSection (NTDLL.@)
356 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
358 if (crit->SpinCount)
360 ULONG count;
362 if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
363 for (count = crit->SpinCount; count > 0; count--)
365 if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */
366 if (crit->LockCount == -1) /* try again */
368 if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1) goto done;
370 YieldProcessor();
374 if (InterlockedIncrement( &crit->LockCount ))
376 if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
378 crit->RecursionCount++;
379 return STATUS_SUCCESS;
382 /* Now wait for it */
383 RtlpWaitForCriticalSection( crit );
385 done:
386 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
387 crit->RecursionCount = 1;
388 return STATUS_SUCCESS;
392 /******************************************************************************
393 * RtlTryEnterCriticalSection (NTDLL.@)
395 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
397 BOOL ret = FALSE;
398 if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1)
400 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
401 crit->RecursionCount = 1;
402 ret = TRUE;
404 else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
406 InterlockedIncrement( &crit->LockCount );
407 crit->RecursionCount++;
408 ret = TRUE;
410 return ret;
414 /******************************************************************************
415 * RtlIsCriticalSectionLocked (NTDLL.@)
417 BOOL WINAPI RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION *crit )
419 return crit->RecursionCount != 0;
423 /******************************************************************************
424 * RtlIsCriticalSectionLockedByThread (NTDLL.@)
426 BOOL WINAPI RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION *crit )
428 return crit->OwningThread == ULongToHandle(GetCurrentThreadId()) &&
429 crit->RecursionCount;
433 /******************************************************************************
434 * RtlLeaveCriticalSection (NTDLL.@)
436 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
438 if (--crit->RecursionCount)
440 if (crit->RecursionCount > 0) InterlockedDecrement( &crit->LockCount );
441 else ERR( "section %p %s is not acquired\n", crit, debugstr_a( crit_section_get_name( crit )));
443 else
445 crit->OwningThread = 0;
446 if (InterlockedDecrement( &crit->LockCount ) >= 0)
448 /* someone is waiting */
449 RtlpUnWaitCriticalSection( crit );
452 return STATUS_SUCCESS;
455 /******************************************************************
456 * RtlRunOnceExecuteOnce (NTDLL.@)
458 DWORD WINAPI RtlRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN func,
459 void *param, void **context )
461 DWORD ret = RtlRunOnceBeginInitialize( once, 0, context );
463 if (ret != STATUS_PENDING) return ret;
465 if (!func( once, param, context ))
467 RtlRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
468 return STATUS_UNSUCCESSFUL;
471 return RtlRunOnceComplete( once, 0, context ? *context : NULL );
474 struct srw_lock
476 short exclusive_waiters;
478 /* Number of shared owners, or -1 if owned exclusive.
480 * Sadly Windows has no equivalent to FUTEX_WAIT_BITSET, so in order to wake
481 * up *only* exclusive or *only* shared waiters (and thus avoid spurious
482 * wakeups), we need to wait on two different addresses.
483 * RtlAcquireSRWLockShared() needs to know the values of "exclusive_waiters"
484 * and "owners", but RtlAcquireSRWLockExclusive() only needs to know the
485 * value of "owners", so the former can wait on the entire structure, and
486 * the latter waits only on the "owners" member. Note then that "owners"
487 * must not be the first element in the structure.
489 short owners;
491 C_ASSERT( sizeof(struct srw_lock) == 4 );
493 /***********************************************************************
494 * RtlInitializeSRWLock (NTDLL.@)
496 * NOTES
497 * Please note that SRWLocks do not keep track of the owner of a lock.
498 * It doesn't make any difference which thread for example unlocks an
499 * SRWLock (see corresponding tests). This implementation uses two
500 * keyed events (one for the exclusive waiters and one for the shared
501 * waiters) and is limited to 2^15-1 waiting threads.
503 void WINAPI RtlInitializeSRWLock( RTL_SRWLOCK *lock )
505 lock->Ptr = NULL;
508 /***********************************************************************
509 * RtlAcquireSRWLockExclusive (NTDLL.@)
511 * NOTES
512 * Unlike RtlAcquireResourceExclusive this function doesn't allow
513 * nested calls from the same thread. "Upgrading" a shared access lock
514 * to an exclusive access lock also doesn't seem to be supported.
516 void WINAPI RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
518 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
520 InterlockedIncrement16( &u.s->exclusive_waiters );
522 for (;;)
524 union { struct srw_lock s; LONG l; } old, new;
525 BOOL wait;
529 old.s = *u.s;
530 new.s = old.s;
532 if (!old.s.owners)
534 /* Not locked exclusive or shared. We can try to grab it. */
535 new.s.owners = -1;
536 --new.s.exclusive_waiters;
537 wait = FALSE;
539 else
541 wait = TRUE;
543 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
545 if (!wait) return;
546 RtlWaitOnAddress( &u.s->owners, &new.s.owners, sizeof(short), NULL );
550 /***********************************************************************
551 * RtlAcquireSRWLockShared (NTDLL.@)
553 * NOTES
554 * Do not call this function recursively - it will only succeed when
555 * there are no threads waiting for an exclusive lock!
557 void WINAPI RtlAcquireSRWLockShared( RTL_SRWLOCK *lock )
559 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
561 for (;;)
563 union { struct srw_lock s; LONG l; } old, new;
564 BOOL wait;
568 old.s = *u.s;
569 new = old;
571 if (old.s.owners != -1 && !old.s.exclusive_waiters)
573 /* Not locked exclusive, and no exclusive waiters.
574 * We can try to grab it. */
575 ++new.s.owners;
576 wait = FALSE;
578 else
580 wait = TRUE;
582 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
584 if (!wait) return;
585 RtlWaitOnAddress( u.s, &new.s, sizeof(struct srw_lock), NULL );
589 /***********************************************************************
590 * RtlReleaseSRWLockExclusive (NTDLL.@)
592 void WINAPI RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock )
594 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
595 union { struct srw_lock s; LONG l; } old, new;
599 old.s = *u.s;
600 new = old;
602 if (old.s.owners != -1) ERR("Lock %p is not owned exclusive!\n", lock);
604 new.s.owners = 0;
605 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
607 if (new.s.exclusive_waiters)
608 RtlWakeAddressSingle( &u.s->owners );
609 else
610 RtlWakeAddressAll( u.s );
613 /***********************************************************************
614 * RtlReleaseSRWLockShared (NTDLL.@)
616 void WINAPI RtlReleaseSRWLockShared( RTL_SRWLOCK *lock )
618 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
619 union { struct srw_lock s; LONG l; } old, new;
623 old.s = *u.s;
624 new = old;
626 if (old.s.owners == -1) ERR("Lock %p is owned exclusive!\n", lock);
627 else if (!old.s.owners) ERR("Lock %p is not owned shared!\n", lock);
629 --new.s.owners;
630 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
632 if (!new.s.owners)
633 RtlWakeAddressSingle( &u.s->owners );
636 /***********************************************************************
637 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
639 * NOTES
640 * Similarly to AcquireSRWLockExclusive, recursive calls are not allowed
641 * and will fail with a FALSE return value.
643 BOOLEAN WINAPI RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
645 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
646 union { struct srw_lock s; LONG l; } old, new;
647 BOOLEAN ret;
651 old.s = *u.s;
652 new.s = old.s;
654 if (!old.s.owners)
656 /* Not locked exclusive or shared. We can try to grab it. */
657 new.s.owners = -1;
658 ret = TRUE;
660 else
662 ret = FALSE;
664 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
666 return ret;
669 /***********************************************************************
670 * RtlTryAcquireSRWLockShared (NTDLL.@)
672 BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock )
674 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
675 union { struct srw_lock s; LONG l; } old, new;
676 BOOLEAN ret;
680 old.s = *u.s;
681 new.s = old.s;
683 if (old.s.owners != -1 && !old.s.exclusive_waiters)
685 /* Not locked exclusive, and no exclusive waiters.
686 * We can try to grab it. */
687 ++new.s.owners;
688 ret = TRUE;
690 else
692 ret = FALSE;
694 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
696 return ret;
699 /***********************************************************************
700 * RtlInitializeConditionVariable (NTDLL.@)
702 * Initializes the condition variable with NULL.
704 * PARAMS
705 * variable [O] condition variable
707 * RETURNS
708 * Nothing.
710 void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable )
712 variable->Ptr = NULL;
715 /***********************************************************************
716 * RtlWakeConditionVariable (NTDLL.@)
718 * Wakes up one thread waiting on the condition variable.
720 * PARAMS
721 * variable [I/O] condition variable to wake up.
723 * RETURNS
724 * Nothing.
726 * NOTES
727 * The calling thread does not have to own any lock in order to call
728 * this function.
730 void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
732 InterlockedIncrement( (LONG *)&variable->Ptr );
733 RtlWakeAddressSingle( variable );
736 /***********************************************************************
737 * RtlWakeAllConditionVariable (NTDLL.@)
739 * See WakeConditionVariable, wakes up all waiting threads.
741 void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
743 InterlockedIncrement( (LONG *)&variable->Ptr );
744 RtlWakeAddressAll( variable );
747 /***********************************************************************
748 * RtlSleepConditionVariableCS (NTDLL.@)
750 * Atomically releases the critical section and suspends the thread,
751 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
752 * the critical section again and returns.
754 * PARAMS
755 * variable [I/O] condition variable
756 * crit [I/O] critical section to leave temporarily
757 * timeout [I] timeout
759 * RETURNS
760 * see NtWaitForKeyedEvent for all possible return values.
762 NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, RTL_CRITICAL_SECTION *crit,
763 const LARGE_INTEGER *timeout )
765 int value = *(int *)&variable->Ptr;
766 NTSTATUS status;
768 RtlLeaveCriticalSection( crit );
769 status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout );
770 RtlEnterCriticalSection( crit );
771 return status;
774 /***********************************************************************
775 * RtlSleepConditionVariableSRW (NTDLL.@)
777 * Atomically releases the SRWLock and suspends the thread,
778 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
779 * the SRWLock again with the same access rights and returns.
781 * PARAMS
782 * variable [I/O] condition variable
783 * lock [I/O] SRWLock to leave temporarily
784 * timeout [I] timeout
785 * flags [I] type of the current lock (exclusive / shared)
787 * RETURNS
788 * see NtWaitForKeyedEvent for all possible return values.
790 * NOTES
791 * the behaviour is undefined if the thread doesn't own the lock.
793 NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, RTL_SRWLOCK *lock,
794 const LARGE_INTEGER *timeout, ULONG flags )
796 int value = *(int *)&variable->Ptr;
797 NTSTATUS status;
799 if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
800 RtlReleaseSRWLockShared( lock );
801 else
802 RtlReleaseSRWLockExclusive( lock );
804 status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout );
806 if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
807 RtlAcquireSRWLockShared( lock );
808 else
809 RtlAcquireSRWLockExclusive( lock );
810 return status;
813 /* RtlWaitOnAddress() and RtlWakeAddress*(), hereafter referred to as "Win32
814 * futexes", offer futex-like semantics with a variable set of address sizes,
815 * but are limited to a single process. They are also fair—the documentation
816 * specifies this, and tests bear it out.
818 * On Windows they are implemented using NtAlertThreadByThreadId and
819 * NtWaitForAlertByThreadId, which manipulate a single flag (similar to an
820 * auto-reset event) per thread. This can be tested by attempting to wake a
821 * thread waiting in RtlWaitOnAddress() via NtAlertThreadByThreadId.
824 struct futex_entry
826 struct list entry;
827 const void *addr;
828 DWORD tid;
831 struct futex_queue
833 struct list queue;
834 LONG lock;
837 static struct futex_queue futex_queues[256];
839 static struct futex_queue *get_futex_queue( const void *addr )
841 ULONG_PTR val = (ULONG_PTR)addr;
843 return &futex_queues[(val >> 4) % ARRAY_SIZE(futex_queues)];
846 static void spin_lock( LONG *lock )
848 while (InterlockedCompareExchange( lock, -1, 0 ))
849 YieldProcessor();
852 static void spin_unlock( LONG *lock )
854 InterlockedExchange( lock, 0 );
857 static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size )
859 switch (size)
861 case 1:
862 return (*(const UCHAR *)addr == *(const UCHAR *)cmp);
863 case 2:
864 return (*(const USHORT *)addr == *(const USHORT *)cmp);
865 case 4:
866 return (*(const ULONG *)addr == *(const ULONG *)cmp);
867 case 8:
868 return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp);
871 return FALSE;
874 /***********************************************************************
875 * RtlWaitOnAddress (NTDLL.@)
877 NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size,
878 const LARGE_INTEGER *timeout )
880 struct futex_queue *queue = get_futex_queue( addr );
881 struct futex_entry entry;
882 NTSTATUS ret;
884 TRACE("addr %p cmp %p size %#Ix timeout %s\n", addr, cmp, size, debugstr_timeout( timeout ));
886 if (size != 1 && size != 2 && size != 4 && size != 8)
887 return STATUS_INVALID_PARAMETER;
889 entry.addr = addr;
890 entry.tid = GetCurrentThreadId();
892 spin_lock( &queue->lock );
894 /* Do the comparison inside of the spinlock, to reduce spurious wakeups. */
896 if (!compare_addr( addr, cmp, size ))
898 spin_unlock( &queue->lock );
899 return STATUS_SUCCESS;
902 if (!queue->queue.next)
903 list_init( &queue->queue );
904 list_add_tail( &queue->queue, &entry.entry );
906 spin_unlock( &queue->lock );
908 ret = NtWaitForAlertByThreadId( NULL, timeout );
910 spin_lock( &queue->lock );
911 /* We may have already been removed by a call to RtlWakeAddressSingle(). */
912 if (entry.addr)
913 list_remove( &entry.entry );
914 spin_unlock( &queue->lock );
916 TRACE("returning %#lx\n", ret);
918 if (ret == STATUS_ALERTED) ret = STATUS_SUCCESS;
919 return ret;
922 /***********************************************************************
923 * RtlWakeAddressAll (NTDLL.@)
925 void WINAPI RtlWakeAddressAll( const void *addr )
927 struct futex_queue *queue = get_futex_queue( addr );
928 unsigned int count = 0, i;
929 struct futex_entry *entry;
930 DWORD tids[256];
932 TRACE("%p\n", addr);
934 if (!addr) return;
936 spin_lock( &queue->lock );
938 if (!queue->queue.next)
939 list_init(&queue->queue);
941 LIST_FOR_EACH_ENTRY( entry, &queue->queue, struct futex_entry, entry )
943 if (entry->addr == addr)
945 /* Try to buffer wakes, so that we don't make a system call while
946 * holding a spinlock. */
947 if (count < ARRAY_SIZE(tids))
948 tids[count++] = entry->tid;
949 else
950 NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)entry->tid );
954 spin_unlock( &queue->lock );
956 for (i = 0; i < count; ++i)
957 NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tids[i] );
960 /***********************************************************************
961 * RtlWakeAddressSingle (NTDLL.@)
963 void WINAPI RtlWakeAddressSingle( const void *addr )
965 struct futex_queue *queue = get_futex_queue( addr );
966 struct futex_entry *entry;
967 DWORD tid = 0;
969 TRACE("%p\n", addr);
971 if (!addr) return;
973 spin_lock( &queue->lock );
975 if (!queue->queue.next)
976 list_init(&queue->queue);
978 LIST_FOR_EACH_ENTRY( entry, &queue->queue, struct futex_entry, entry )
980 if (entry->addr == addr)
982 /* Try to buffer wakes, so that we don't make a system call while
983 * holding a spinlock. */
984 tid = entry->tid;
986 /* Remove this entry from the queue, so that a simultaneous call to
987 * RtlWakeAddressSingle() will not also wake it—two simultaneous
988 * calls must wake at least two waiters if they exist. */
989 entry->addr = NULL;
990 list_remove( &entry->entry );
991 break;
995 spin_unlock( &queue->lock );
997 if (tid) NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tid );