win32u: Use the dummy surface for empty layered window surfaces.
[wine.git] / dlls / ntdll / sync.c
blob139931a595e4306174e1df05b7bcf56cfbda84da
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 "wine/exception.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 const char *crit_section_get_name( const RTL_CRITICAL_SECTION *crit )
160 if (crit_section_has_debuginfo( crit ))
161 return (char *)crit->DebugInfo->Spare[0];
162 return "?";
165 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
167 if ((ULONG_PTR)crit->LockSemaphore > 1) return crit->LockSemaphore;
168 return NULL;
171 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
173 LARGE_INTEGER time = {.QuadPart = timeout * (LONGLONG)-10000000};
174 HANDLE sem = get_semaphore( crit );
176 if (sem) return NtWaitForSingleObject( sem, FALSE, &time );
177 else
179 LONG *lock = (LONG *)&crit->LockSemaphore;
180 while (!InterlockedCompareExchange( lock, 0, 1 ))
182 static const LONG zero;
183 /* this may wait longer than specified in case of multiple wake-ups */
184 if (RtlWaitOnAddress( lock, &zero, sizeof(LONG), &time ) == STATUS_TIMEOUT)
185 return STATUS_TIMEOUT;
187 return STATUS_WAIT_0;
191 static ULONG crit_sect_default_flags(void)
193 if (NtCurrentTeb()->Peb->OSMajorVersion > 6 ||
194 (NtCurrentTeb()->Peb->OSMajorVersion == 6 && NtCurrentTeb()->Peb->OSMinorVersion >= 2)) return 0;
195 return RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO;
198 /******************************************************************************
199 * RtlInitializeCriticalSection (NTDLL.@)
201 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
203 return RtlInitializeCriticalSectionEx( crit, 0, crit_sect_default_flags() );
207 /******************************************************************************
208 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
210 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
212 return RtlInitializeCriticalSectionEx( crit, spincount, crit_sect_default_flags() );
216 /******************************************************************************
217 * RtlInitializeCriticalSectionEx (NTDLL.@)
219 NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags )
221 if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT))
222 FIXME("(%p,%lu,0x%08lx) semi-stub\n", crit, spincount, flags);
224 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
225 * memory from a static pool to hold the debug info. Then heap.c could pass
226 * this flag rather than initialising the process heap CS by hand. If this
227 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
228 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
230 if (!(flags & RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO))
231 crit->DebugInfo = no_debug_info_marker;
232 else
234 crit->DebugInfo = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG ));
235 if (crit->DebugInfo)
237 crit->DebugInfo->Type = 0;
238 crit->DebugInfo->CreatorBackTraceIndex = 0;
239 crit->DebugInfo->CriticalSection = crit;
240 crit->DebugInfo->ProcessLocksList.Blink = &crit->DebugInfo->ProcessLocksList;
241 crit->DebugInfo->ProcessLocksList.Flink = &crit->DebugInfo->ProcessLocksList;
242 crit->DebugInfo->EntryCount = 0;
243 crit->DebugInfo->ContentionCount = 0;
244 memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
247 crit->LockCount = -1;
248 crit->RecursionCount = 0;
249 crit->OwningThread = 0;
250 crit->LockSemaphore = 0;
251 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
252 crit->SpinCount = spincount & ~0x80000000;
253 return STATUS_SUCCESS;
257 /******************************************************************************
258 * RtlSetCriticalSectionSpinCount (NTDLL.@)
260 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
262 ULONG oldspincount = crit->SpinCount;
263 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
264 crit->SpinCount = spincount;
265 return oldspincount;
269 /******************************************************************************
270 * RtlDeleteCriticalSection (NTDLL.@)
272 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
274 HANDLE sem;
276 crit->LockCount = -1;
277 crit->RecursionCount = 0;
278 crit->OwningThread = 0;
279 if (crit_section_has_debuginfo( crit ))
281 /* only free the ones we made in here */
282 if (!crit->DebugInfo->Spare[0])
284 RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
285 crit->DebugInfo = NULL;
288 else crit->DebugInfo = NULL;
290 if ((sem = get_semaphore( crit ))) NtClose( sem );
291 crit->LockSemaphore = 0;
292 return STATUS_SUCCESS;
296 /******************************************************************************
297 * RtlpWaitForCriticalSection (NTDLL.@)
299 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
301 unsigned int timeout = 5;
303 /* Don't allow blocking on a critical section during process termination */
304 if (RtlDllShutdownInProgress())
306 WARN( "process %s is shutting down, returning STATUS_SUCCESS\n",
307 debugstr_w(NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer) );
308 return STATUS_SUCCESS;
311 for (;;)
313 NTSTATUS status = wait_semaphore( crit, timeout );
315 if (status == STATUS_WAIT_0) break;
317 timeout = (TRACE_ON(relay) ? 300 : 60);
319 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (%u sec)\n",
320 crit, debugstr_a(crit_section_get_name(crit)), GetCurrentThreadId(), HandleToULong(crit->OwningThread), timeout );
322 if (crit_section_has_debuginfo( crit )) crit->DebugInfo->ContentionCount++;
323 return STATUS_SUCCESS;
327 /******************************************************************************
328 * RtlpUnWaitCriticalSection (NTDLL.@)
330 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
332 NTSTATUS ret;
333 HANDLE sem = get_semaphore( crit );
335 if (sem) ret = NtReleaseSemaphore( sem, 1, NULL );
336 else
338 LONG *lock = (LONG *)&crit->LockSemaphore;
339 InterlockedExchange( lock, 1 );
340 RtlWakeAddressSingle( lock );
341 ret = STATUS_SUCCESS;
343 if (ret) RtlRaiseStatus( ret );
344 return ret;
348 /******************************************************************************
349 * RtlEnterCriticalSection (NTDLL.@)
351 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
353 if (crit->SpinCount)
355 ULONG count;
357 if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
358 for (count = crit->SpinCount; count > 0; count--)
360 if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */
361 if (crit->LockCount == -1) /* try again */
363 if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1) goto done;
365 YieldProcessor();
369 if (InterlockedIncrement( &crit->LockCount ))
371 if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
373 crit->RecursionCount++;
374 return STATUS_SUCCESS;
377 /* Now wait for it */
378 RtlpWaitForCriticalSection( crit );
380 done:
381 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
382 crit->RecursionCount = 1;
383 return STATUS_SUCCESS;
387 /******************************************************************************
388 * RtlTryEnterCriticalSection (NTDLL.@)
390 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
392 BOOL ret = FALSE;
393 if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1)
395 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
396 crit->RecursionCount = 1;
397 ret = TRUE;
399 else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
401 InterlockedIncrement( &crit->LockCount );
402 crit->RecursionCount++;
403 ret = TRUE;
405 return ret;
409 /******************************************************************************
410 * RtlIsCriticalSectionLocked (NTDLL.@)
412 BOOL WINAPI RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION *crit )
414 return crit->RecursionCount != 0;
418 /******************************************************************************
419 * RtlIsCriticalSectionLockedByThread (NTDLL.@)
421 BOOL WINAPI RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION *crit )
423 return crit->OwningThread == ULongToHandle(GetCurrentThreadId()) &&
424 crit->RecursionCount;
428 /******************************************************************************
429 * RtlLeaveCriticalSection (NTDLL.@)
431 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
433 if (--crit->RecursionCount)
435 if (crit->RecursionCount > 0) InterlockedDecrement( &crit->LockCount );
436 else ERR( "section %p %s is not acquired\n", crit, debugstr_a( crit_section_get_name( crit )));
438 else
440 crit->OwningThread = 0;
441 if (InterlockedDecrement( &crit->LockCount ) >= 0)
443 /* someone is waiting */
444 RtlpUnWaitCriticalSection( crit );
447 return STATUS_SUCCESS;
450 /******************************************************************
451 * RtlRunOnceExecuteOnce (NTDLL.@)
453 DWORD WINAPI RtlRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN func,
454 void *param, void **context )
456 DWORD ret = RtlRunOnceBeginInitialize( once, 0, context );
458 if (ret != STATUS_PENDING) return ret;
460 if (!func( once, param, context ))
462 RtlRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
463 return STATUS_UNSUCCESSFUL;
466 return RtlRunOnceComplete( once, 0, context ? *context : NULL );
469 struct srw_lock
471 /* bit 0 - if the lock is held exclusive. bit 1.. - number of exclusive waiters. */
472 short exclusive_waiters;
474 /* Number of owners.
476 * Sadly Windows has no equivalent to FUTEX_WAIT_BITSET, so in order to wake
477 * up *only* exclusive or *only* shared waiters (and thus avoid spurious
478 * wakeups), we need to wait on two different addresses.
479 * RtlAcquireSRWLockShared() needs to know the values of "exclusive_waiters"
480 * and "owners", but RtlAcquireSRWLockExclusive() only needs to know the
481 * value of "owners", so the former can wait on the entire structure, and
482 * the latter waits only on the "owners" member. Note then that "owners"
483 * must not be the first element in the structure.
485 unsigned short owners;
487 C_ASSERT( sizeof(struct srw_lock) == 4 );
489 /***********************************************************************
490 * RtlInitializeSRWLock (NTDLL.@)
492 * NOTES
493 * Please note that SRWLocks do not keep track of the owner of a lock.
494 * It doesn't make any difference which thread for example unlocks an
495 * SRWLock (see corresponding tests). This implementation uses two
496 * keyed events (one for the exclusive waiters and one for the shared
497 * waiters) and is limited to 2^15-1 waiting threads.
499 void WINAPI RtlInitializeSRWLock( RTL_SRWLOCK *lock )
501 lock->Ptr = NULL;
504 /***********************************************************************
505 * RtlAcquireSRWLockExclusive (NTDLL.@)
507 * NOTES
508 * Unlike RtlAcquireResourceExclusive this function doesn't allow
509 * nested calls from the same thread. "Upgrading" a shared access lock
510 * to an exclusive access lock also doesn't seem to be supported.
512 void WINAPI RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
514 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
516 InterlockedExchangeAdd16( &u.s->exclusive_waiters, 2 );
518 for (;;)
520 union { struct srw_lock s; LONG l; } old, new;
521 BOOL wait;
525 old.s = *u.s;
526 new.s = old.s;
528 if (!old.s.owners)
530 /* Not locked exclusive or shared. We can try to grab it. */
531 new.s.owners = 1;
532 new.s.exclusive_waiters -= 2;
533 new.s.exclusive_waiters |= 1;
534 wait = FALSE;
536 else
538 wait = TRUE;
540 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
542 if (!wait) return;
543 RtlWaitOnAddress( &u.s->owners, &new.s.owners, sizeof(short), NULL );
547 /***********************************************************************
548 * RtlAcquireSRWLockShared (NTDLL.@)
550 * NOTES
551 * Do not call this function recursively - it will only succeed when
552 * there are no threads waiting for an exclusive lock!
554 void WINAPI RtlAcquireSRWLockShared( RTL_SRWLOCK *lock )
556 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
558 for (;;)
560 union { struct srw_lock s; LONG l; } old, new;
561 BOOL wait;
565 old.s = *u.s;
566 new = old;
568 if (!old.s.exclusive_waiters)
570 /* Not locked exclusive, and no exclusive waiters.
571 * We can try to grab it. */
572 ++new.s.owners;
573 wait = FALSE;
575 else
577 wait = TRUE;
579 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
581 if (!wait) return;
582 RtlWaitOnAddress( u.s, &new.s, sizeof(struct srw_lock), NULL );
586 /***********************************************************************
587 * RtlReleaseSRWLockExclusive (NTDLL.@)
589 void WINAPI RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock )
591 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
592 union { struct srw_lock s; LONG l; } old, new;
596 old.s = *u.s;
597 new = old;
599 if (!(old.s.exclusive_waiters & 1)) ERR("Lock %p is not owned exclusive!\n", lock);
601 new.s.owners = 0;
602 new.s.exclusive_waiters &= ~1;
603 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
605 if (new.s.exclusive_waiters)
606 RtlWakeAddressSingle( &u.s->owners );
607 else
608 RtlWakeAddressAll( u.s );
611 /***********************************************************************
612 * RtlReleaseSRWLockShared (NTDLL.@)
614 void WINAPI RtlReleaseSRWLockShared( RTL_SRWLOCK *lock )
616 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
617 union { struct srw_lock s; LONG l; } old, new;
621 old.s = *u.s;
622 new = old;
624 if (old.s.exclusive_waiters & 1) ERR("Lock %p is owned exclusive!\n", lock);
625 else if (!old.s.owners) ERR("Lock %p is not owned shared!\n", lock);
627 --new.s.owners;
628 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
630 if (!new.s.owners)
631 RtlWakeAddressSingle( &u.s->owners );
634 /***********************************************************************
635 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
637 * NOTES
638 * Similarly to AcquireSRWLockExclusive, recursive calls are not allowed
639 * and will fail with a FALSE return value.
641 BOOLEAN WINAPI RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
643 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
644 union { struct srw_lock s; LONG l; } old, new;
645 BOOLEAN ret;
649 old.s = *u.s;
650 new.s = old.s;
652 if (!old.s.owners)
654 /* Not locked exclusive or shared. We can try to grab it. */
655 new.s.owners = 1;
656 new.s.exclusive_waiters |= 1;
657 ret = TRUE;
659 else
661 ret = FALSE;
663 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
665 return ret;
668 /***********************************************************************
669 * RtlTryAcquireSRWLockShared (NTDLL.@)
671 BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock )
673 union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock };
674 union { struct srw_lock s; LONG l; } old, new;
675 BOOLEAN ret;
679 old.s = *u.s;
680 new.s = old.s;
682 if (!old.s.exclusive_waiters)
684 /* Not locked exclusive, and no exclusive waiters.
685 * We can try to grab it. */
686 ++new.s.owners;
687 ret = TRUE;
689 else
691 ret = FALSE;
693 } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
695 return ret;
698 /***********************************************************************
699 * RtlInitializeConditionVariable (NTDLL.@)
701 * Initializes the condition variable with NULL.
703 * PARAMS
704 * variable [O] condition variable
706 * RETURNS
707 * Nothing.
709 void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable )
711 variable->Ptr = NULL;
714 /***********************************************************************
715 * RtlWakeConditionVariable (NTDLL.@)
717 * Wakes up one thread waiting on the condition variable.
719 * PARAMS
720 * variable [I/O] condition variable to wake up.
722 * RETURNS
723 * Nothing.
725 * NOTES
726 * The calling thread does not have to own any lock in order to call
727 * this function.
729 void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
731 InterlockedIncrement( (LONG *)&variable->Ptr );
732 RtlWakeAddressSingle( variable );
735 /***********************************************************************
736 * RtlWakeAllConditionVariable (NTDLL.@)
738 * See WakeConditionVariable, wakes up all waiting threads.
740 void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
742 InterlockedIncrement( (LONG *)&variable->Ptr );
743 RtlWakeAddressAll( variable );
746 /***********************************************************************
747 * RtlSleepConditionVariableCS (NTDLL.@)
749 * Atomically releases the critical section and suspends the thread,
750 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
751 * the critical section again and returns.
753 * PARAMS
754 * variable [I/O] condition variable
755 * crit [I/O] critical section to leave temporarily
756 * timeout [I] timeout
758 * RETURNS
759 * see NtWaitForKeyedEvent for all possible return values.
761 NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, RTL_CRITICAL_SECTION *crit,
762 const LARGE_INTEGER *timeout )
764 int value = *(int *)&variable->Ptr;
765 NTSTATUS status;
767 RtlLeaveCriticalSection( crit );
768 status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout );
769 RtlEnterCriticalSection( crit );
770 return status;
773 /***********************************************************************
774 * RtlSleepConditionVariableSRW (NTDLL.@)
776 * Atomically releases the SRWLock and suspends the thread,
777 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
778 * the SRWLock again with the same access rights and returns.
780 * PARAMS
781 * variable [I/O] condition variable
782 * lock [I/O] SRWLock to leave temporarily
783 * timeout [I] timeout
784 * flags [I] type of the current lock (exclusive / shared)
786 * RETURNS
787 * see NtWaitForKeyedEvent for all possible return values.
789 * NOTES
790 * the behaviour is undefined if the thread doesn't own the lock.
792 NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, RTL_SRWLOCK *lock,
793 const LARGE_INTEGER *timeout, ULONG flags )
795 int value = *(int *)&variable->Ptr;
796 NTSTATUS status;
798 if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
799 RtlReleaseSRWLockShared( lock );
800 else
801 RtlReleaseSRWLockExclusive( lock );
803 status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout );
805 if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
806 RtlAcquireSRWLockShared( lock );
807 else
808 RtlAcquireSRWLockExclusive( lock );
809 return status;
812 /* RtlWaitOnAddress() and RtlWakeAddress*(), hereafter referred to as "Win32
813 * futexes", offer futex-like semantics with a variable set of address sizes,
814 * but are limited to a single process. They are also fair—the documentation
815 * specifies this, and tests bear it out.
817 * On Windows they are implemented using NtAlertThreadByThreadId and
818 * NtWaitForAlertByThreadId, which manipulate a single flag (similar to an
819 * auto-reset event) per thread. This can be tested by attempting to wake a
820 * thread waiting in RtlWaitOnAddress() via NtAlertThreadByThreadId.
823 struct futex_entry
825 struct list entry;
826 const void *addr;
827 DWORD tid;
830 struct futex_queue
832 struct list queue;
833 LONG lock;
836 static struct futex_queue futex_queues[256];
838 static struct futex_queue *get_futex_queue( const void *addr )
840 ULONG_PTR val = (ULONG_PTR)addr;
842 return &futex_queues[(val >> 4) % ARRAY_SIZE(futex_queues)];
845 static void spin_lock( LONG *lock )
847 while (InterlockedCompareExchange( lock, -1, 0 ))
848 YieldProcessor();
851 static void spin_unlock( LONG *lock )
853 InterlockedExchange( lock, 0 );
856 static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size )
858 switch (size)
860 case 1:
861 return (*(const UCHAR *)addr == *(const UCHAR *)cmp);
862 case 2:
863 return (*(const USHORT *)addr == *(const USHORT *)cmp);
864 case 4:
865 return (*(const ULONG *)addr == *(const ULONG *)cmp);
866 case 8:
867 return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp);
870 return FALSE;
873 /***********************************************************************
874 * RtlWaitOnAddress (NTDLL.@)
876 NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size,
877 const LARGE_INTEGER *timeout )
879 struct futex_queue *queue = get_futex_queue( addr );
880 struct futex_entry entry;
881 NTSTATUS ret;
883 TRACE("addr %p cmp %p size %#Ix timeout %s\n", addr, cmp, size, debugstr_timeout( timeout ));
885 if (size != 1 && size != 2 && size != 4 && size != 8)
886 return STATUS_INVALID_PARAMETER;
888 entry.addr = addr;
889 entry.tid = GetCurrentThreadId();
891 spin_lock( &queue->lock );
893 /* Do the comparison inside of the spinlock, to reduce spurious wakeups. */
895 if (!compare_addr( addr, cmp, size ))
897 spin_unlock( &queue->lock );
898 return STATUS_SUCCESS;
901 if (!queue->queue.next)
902 list_init( &queue->queue );
903 list_add_tail( &queue->queue, &entry.entry );
905 spin_unlock( &queue->lock );
907 ret = NtWaitForAlertByThreadId( NULL, timeout );
909 /* We may have already been removed by a call to RtlWakeAddressSingle() or RtlWakeAddressAll(). */
910 if (entry.addr)
912 spin_lock( &queue->lock );
913 if (entry.addr)
914 list_remove( &entry.entry );
915 spin_unlock( &queue->lock );
918 TRACE("returning %#lx\n", ret);
920 if (ret == STATUS_ALERTED) ret = STATUS_SUCCESS;
921 return ret;
924 /***********************************************************************
925 * RtlWakeAddressAll (NTDLL.@)
927 void WINAPI RtlWakeAddressAll( const void *addr )
929 struct futex_queue *queue = get_futex_queue( addr );
930 struct futex_entry *entry, *next;
931 unsigned int count = 0, i;
932 DWORD tids[256];
934 TRACE("%p\n", addr);
936 if (!addr) return;
938 spin_lock( &queue->lock );
940 if (!queue->queue.next)
941 list_init(&queue->queue);
943 LIST_FOR_EACH_ENTRY_SAFE( entry, next, &queue->queue, struct futex_entry, entry )
945 if (entry->addr == addr)
947 entry->addr = NULL;
948 list_remove( &entry->entry );
949 /* Try to buffer wakes, so that we don't make a system call while
950 * holding a spinlock. */
951 if (count < ARRAY_SIZE(tids))
952 tids[count++] = entry->tid;
953 else
954 NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)entry->tid );
958 spin_unlock( &queue->lock );
960 for (i = 0; i < count; ++i)
961 NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tids[i] );
964 /***********************************************************************
965 * RtlWakeAddressSingle (NTDLL.@)
967 void WINAPI RtlWakeAddressSingle( const void *addr )
969 struct futex_queue *queue = get_futex_queue( addr );
970 struct futex_entry *entry;
971 DWORD tid = 0;
973 TRACE("%p\n", addr);
975 if (!addr) return;
977 spin_lock( &queue->lock );
979 if (!queue->queue.next)
980 list_init(&queue->queue);
982 LIST_FOR_EACH_ENTRY( entry, &queue->queue, struct futex_entry, entry )
984 if (entry->addr == addr)
986 /* Try to buffer wakes, so that we don't make a system call while
987 * holding a spinlock. */
988 tid = entry->tid;
990 /* Remove this entry from the queue, so that a simultaneous call to
991 * RtlWakeAddressSingle() will not also wake it—two simultaneous
992 * calls must wake at least two waiters if they exist. */
993 entry->addr = NULL;
994 list_remove( &entry->entry );
995 break;
999 spin_unlock( &queue->lock );
1001 if (tid) NtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tid );
1004 /*************************************************************************
1005 * RtlInitializeSListHead (NTDLL.@)
1007 void WINAPI RtlInitializeSListHead(PSLIST_HEADER list)
1009 #ifdef _WIN64
1010 list->Alignment = list->Region = 0;
1011 list->Header16.HeaderType = 1; /* we use the 16-byte header */
1012 #else
1013 list->Alignment = 0;
1014 #endif
1017 /*************************************************************************
1018 * RtlQueryDepthSList (NTDLL.@)
1020 WORD WINAPI RtlQueryDepthSList(PSLIST_HEADER list)
1022 #ifdef _WIN64
1023 return list->Header16.Depth;
1024 #else
1025 return list->Depth;
1026 #endif
1029 /*************************************************************************
1030 * RtlFirstEntrySList (NTDLL.@)
1032 PSLIST_ENTRY WINAPI RtlFirstEntrySList(const SLIST_HEADER* list)
1034 #ifdef _WIN64
1035 return (SLIST_ENTRY *)((ULONG_PTR)list->Header16.NextEntry << 4);
1036 #else
1037 return list->Next.Next;
1038 #endif
1041 /*************************************************************************
1042 * RtlInterlockedFlushSList (NTDLL.@)
1044 PSLIST_ENTRY WINAPI RtlInterlockedFlushSList(PSLIST_HEADER list)
1046 SLIST_HEADER old, new;
1048 #ifdef _WIN64
1049 if (!list->Header16.NextEntry) return NULL;
1050 new.Alignment = new.Region = 0;
1051 new.Header16.HeaderType = 1; /* we use the 16-byte header */
1054 old = *list;
1055 new.Header16.Sequence = old.Header16.Sequence + 1;
1056 } while (!InterlockedCompareExchange128((__int64 *)list, new.Region, new.Alignment, (__int64 *)&old));
1057 return (SLIST_ENTRY *)((ULONG_PTR)old.Header16.NextEntry << 4);
1058 #else
1059 if (!list->Next.Next) return NULL;
1060 new.Alignment = 0;
1063 old = *list;
1064 new.Sequence = old.Sequence + 1;
1065 } while (InterlockedCompareExchange64((__int64 *)&list->Alignment, new.Alignment,
1066 old.Alignment) != old.Alignment);
1067 return old.Next.Next;
1068 #endif
1071 /*************************************************************************
1072 * RtlInterlockedPushEntrySList (NTDLL.@)
1074 PSLIST_ENTRY WINAPI RtlInterlockedPushEntrySList(PSLIST_HEADER list, PSLIST_ENTRY entry)
1076 SLIST_HEADER old, new;
1078 #ifdef _WIN64
1079 new.Header16.NextEntry = (ULONG_PTR)entry >> 4;
1082 old = *list;
1083 entry->Next = (SLIST_ENTRY *)((ULONG_PTR)old.Header16.NextEntry << 4);
1084 new.Header16.Depth = old.Header16.Depth + 1;
1085 new.Header16.Sequence = old.Header16.Sequence + 1;
1086 } while (!InterlockedCompareExchange128((__int64 *)list, new.Region, new.Alignment, (__int64 *)&old));
1087 return (SLIST_ENTRY *)((ULONG_PTR)old.Header16.NextEntry << 4);
1088 #else
1089 new.Next.Next = entry;
1092 old = *list;
1093 entry->Next = old.Next.Next;
1094 new.Depth = old.Depth + 1;
1095 new.Sequence = old.Sequence + 1;
1096 } while (InterlockedCompareExchange64((__int64 *)&list->Alignment, new.Alignment,
1097 old.Alignment) != old.Alignment);
1098 return old.Next.Next;
1099 #endif
1102 /*************************************************************************
1103 * RtlInterlockedPopEntrySList (NTDLL.@)
1105 PSLIST_ENTRY WINAPI RtlInterlockedPopEntrySList(PSLIST_HEADER list)
1107 SLIST_HEADER old, new;
1108 PSLIST_ENTRY entry;
1110 #ifdef _WIN64
1113 old = *list;
1114 if (!(entry = (SLIST_ENTRY *)((ULONG_PTR)old.Header16.NextEntry << 4))) return NULL;
1115 /* entry could be deleted by another thread */
1116 __TRY
1118 new.Header16.NextEntry = (ULONG_PTR)entry->Next >> 4;
1119 new.Header16.Depth = old.Header16.Depth - 1;
1120 new.Header16.Sequence = old.Header16.Sequence + 1;
1122 __EXCEPT_PAGE_FAULT
1125 __ENDTRY
1126 } while (!InterlockedCompareExchange128((__int64 *)list, new.Region, new.Alignment, (__int64 *)&old));
1127 #else
1130 old = *list;
1131 if (!(entry = old.Next.Next)) return NULL;
1132 /* entry could be deleted by another thread */
1133 __TRY
1135 new.Next.Next = entry->Next;
1136 new.Depth = old.Depth - 1;
1137 new.Sequence = old.Sequence + 1;
1139 __EXCEPT_PAGE_FAULT
1142 __ENDTRY
1143 } while (InterlockedCompareExchange64((__int64 *)&list->Alignment, new.Alignment,
1144 old.Alignment) != old.Alignment);
1145 #endif
1146 return entry;
1149 /*************************************************************************
1150 * RtlInterlockedPushListSListEx (NTDLL.@)
1152 PSLIST_ENTRY WINAPI RtlInterlockedPushListSListEx(PSLIST_HEADER list, PSLIST_ENTRY first,
1153 PSLIST_ENTRY last, ULONG count)
1155 SLIST_HEADER old, new;
1157 #ifdef _WIN64
1158 new.Header16.NextEntry = (ULONG_PTR)first >> 4;
1161 old = *list;
1162 new.Header16.Depth = old.Header16.Depth + count;
1163 new.Header16.Sequence = old.Header16.Sequence + 1;
1164 last->Next = (SLIST_ENTRY *)((ULONG_PTR)old.Header16.NextEntry << 4);
1165 } while (!InterlockedCompareExchange128((__int64 *)list, new.Region, new.Alignment, (__int64 *)&old));
1166 return (SLIST_ENTRY *)((ULONG_PTR)old.Header16.NextEntry << 4);
1167 #else
1168 new.Next.Next = first;
1171 old = *list;
1172 new.Depth = old.Depth + count;
1173 new.Sequence = old.Sequence + 1;
1174 last->Next = old.Next.Next;
1175 } while (InterlockedCompareExchange64((__int64 *)&list->Alignment, new.Alignment,
1176 old.Alignment) != old.Alignment);
1177 return old.Next.Next;
1178 #endif
1181 /*************************************************************************
1182 * RtlInterlockedPushListSList (NTDLL.@)
1184 DEFINE_FASTCALL_WRAPPER(RtlInterlockedPushListSList, 16)
1185 PSLIST_ENTRY FASTCALL RtlInterlockedPushListSList(PSLIST_HEADER list, PSLIST_ENTRY first,
1186 PSLIST_ENTRY last, ULONG count)
1188 return RtlInterlockedPushListSListEx(list, first, last, count);
1191 /***********************************************************************
1192 * RtlInitializeResource (NTDLL.@)
1194 * xxxResource() functions implement multiple-reader-single-writer lock.
1195 * The code is based on information published in WDJ January 1999 issue.
1197 void WINAPI RtlInitializeResource(LPRTL_RWLOCK rwl)
1199 if (!rwl) return;
1200 rwl->iNumberActive = 0;
1201 rwl->uExclusiveWaiters = 0;
1202 rwl->uSharedWaiters = 0;
1203 rwl->hOwningThreadId = 0;
1204 rwl->dwTimeoutBoost = 0; /* no info on this one, default value is 0 */
1205 RtlInitializeCriticalSectionEx( &rwl->rtlCS, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO );
1206 rwl->rtlCS.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RTL_RWLOCK.rtlCS");
1207 NtCreateSemaphore( &rwl->hExclusiveReleaseSemaphore, SEMAPHORE_ALL_ACCESS, NULL, 0, 65535 );
1208 NtCreateSemaphore( &rwl->hSharedReleaseSemaphore, SEMAPHORE_ALL_ACCESS, NULL, 0, 65535 );
1211 /***********************************************************************
1212 * RtlDeleteResource (NTDLL.@)
1214 void WINAPI RtlDeleteResource(LPRTL_RWLOCK rwl)
1216 if (!rwl) return;
1217 RtlEnterCriticalSection( &rwl->rtlCS );
1218 if( rwl->iNumberActive || rwl->uExclusiveWaiters || rwl->uSharedWaiters )
1219 ERR("Deleting active MRSW lock (%p), expect failure\n", rwl );
1220 rwl->hOwningThreadId = 0;
1221 rwl->uExclusiveWaiters = rwl->uSharedWaiters = 0;
1222 rwl->iNumberActive = 0;
1223 NtClose( rwl->hExclusiveReleaseSemaphore );
1224 NtClose( rwl->hSharedReleaseSemaphore );
1225 RtlLeaveCriticalSection( &rwl->rtlCS );
1226 rwl->rtlCS.DebugInfo->Spare[0] = 0;
1227 RtlDeleteCriticalSection( &rwl->rtlCS );
1230 /***********************************************************************
1231 * RtlAcquireResourceExclusive (NTDLL.@)
1233 BYTE WINAPI RtlAcquireResourceExclusive(LPRTL_RWLOCK rwl, BYTE fWait)
1235 BYTE retVal = 0;
1237 if (!rwl) return 0;
1239 for (;;)
1241 RtlEnterCriticalSection( &rwl->rtlCS );
1242 if( rwl->iNumberActive == 0 ) /* lock is free */
1244 rwl->iNumberActive = -1;
1245 retVal = 1;
1247 else if( rwl->iNumberActive < 0 ) /* exclusive lock in progress */
1249 if( rwl->hOwningThreadId == ULongToHandle(GetCurrentThreadId()) )
1251 retVal = 1;
1252 rwl->iNumberActive--;
1253 break;
1255 wait:
1256 if( fWait )
1258 NTSTATUS status;
1260 rwl->uExclusiveWaiters++;
1262 RtlLeaveCriticalSection( &rwl->rtlCS );
1263 status = NtWaitForSingleObject( rwl->hExclusiveReleaseSemaphore, FALSE, NULL );
1264 if( HIWORD(status) ) break;
1265 continue; /* restart the acquisition to avoid deadlocks */
1268 else /* one or more shared locks are in progress */
1269 if( fWait )
1270 goto wait;
1272 if( retVal == 1 ) rwl->hOwningThreadId = ULongToHandle(GetCurrentThreadId());
1273 break;
1275 RtlLeaveCriticalSection( &rwl->rtlCS );
1276 return retVal;
1279 /***********************************************************************
1280 * RtlAcquireResourceShared (NTDLL.@)
1282 BYTE WINAPI RtlAcquireResourceShared(LPRTL_RWLOCK rwl, BYTE fWait)
1284 NTSTATUS status = STATUS_UNSUCCESSFUL;
1285 BYTE retVal = 0;
1287 if (!rwl) return 0;
1288 for (;;)
1290 RtlEnterCriticalSection( &rwl->rtlCS );
1291 if( rwl->iNumberActive < 0 )
1293 if( rwl->hOwningThreadId == ULongToHandle(GetCurrentThreadId()) )
1295 rwl->iNumberActive--;
1296 retVal = 1;
1297 break;
1300 if( fWait )
1302 rwl->uSharedWaiters++;
1303 RtlLeaveCriticalSection( &rwl->rtlCS );
1304 status = NtWaitForSingleObject( rwl->hSharedReleaseSemaphore, FALSE, NULL );
1305 if( HIWORD(status) ) break;
1306 continue;
1309 else
1311 if( status != STATUS_WAIT_0 ) /* otherwise RtlReleaseResource() has already done it */
1312 rwl->iNumberActive++;
1313 retVal = 1;
1315 break;
1317 RtlLeaveCriticalSection( &rwl->rtlCS );
1318 return retVal;
1322 /***********************************************************************
1323 * RtlReleaseResource (NTDLL.@)
1325 void WINAPI RtlReleaseResource(LPRTL_RWLOCK rwl)
1327 RtlEnterCriticalSection( &rwl->rtlCS );
1329 if( rwl->iNumberActive > 0 ) /* have one or more readers */
1331 if( --rwl->iNumberActive == 0 )
1333 if( rwl->uExclusiveWaiters )
1335 rwl->uExclusiveWaiters--;
1336 NtReleaseSemaphore( rwl->hExclusiveReleaseSemaphore, 1, NULL );
1340 else if( rwl->iNumberActive < 0 ) /* have a writer, possibly recursive */
1342 if( ++rwl->iNumberActive == 0 )
1344 rwl->hOwningThreadId = 0;
1345 if( rwl->uExclusiveWaiters )
1347 rwl->uExclusiveWaiters--;
1348 NtReleaseSemaphore( rwl->hExclusiveReleaseSemaphore, 1, NULL );
1350 else if( rwl->uSharedWaiters )
1352 UINT n = rwl->uSharedWaiters;
1353 rwl->iNumberActive = rwl->uSharedWaiters; /* prevent new writers from joining until
1354 * all queued readers have done their thing */
1355 rwl->uSharedWaiters = 0;
1356 NtReleaseSemaphore( rwl->hSharedReleaseSemaphore, n, NULL );
1360 RtlLeaveCriticalSection( &rwl->rtlCS );
1364 /***********************************************************************
1365 * RtlDumpResource (NTDLL.@)
1367 void WINAPI RtlDumpResource(LPRTL_RWLOCK rwl)
1369 if (!rwl) return;
1370 ERR( "%p: active count = %i waiting readers = %i waiting writers = %i owner thread = %p",
1371 rwl, rwl->iNumberActive, rwl->uSharedWaiters, rwl->uExclusiveWaiters, rwl->hOwningThreadId );
1372 ERR( "\n" );