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
32 #define WIN32_NO_STATUS
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
)
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
;
73 ULONG_PTR next
, val
= (ULONG_PTR
)once
->Ptr
;
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
;
83 case 1: /* in progress, wait */
84 if (flags
& RTL_RUN_ONCE_ASYNC
) return STATUS_INVALID_PARAMETER
;
86 if (InterlockedCompareExchangePointer( &once
->Ptr
, (void *)((ULONG_PTR
)&next
| 1),
87 (void *)val
) == (void *)val
)
88 NtWaitForKeyedEvent( 0, &next
, FALSE
, NULL
);
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);
118 ULONG_PTR val
= (ULONG_PTR
)once
->Ptr
;
122 case 1: /* in progress */
123 if (InterlockedCompareExchangePointer( &once
->Ptr
, context
, (void *)val
) != (void *)val
) break;
127 ULONG_PTR next
= *(ULONG_PTR
*)val
;
128 NtReleaseKeyedEvent( 0, (void *)val
, FALSE
, NULL
);
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
;
139 return STATUS_UNSUCCESSFUL
;
145 /***********************************************************************
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];
164 static inline HANDLE
get_semaphore( RTL_CRITICAL_SECTION
*crit
)
166 HANDLE ret
= crit
->LockSemaphore
;
170 if (NtCreateSemaphore( &sem
, SEMAPHORE_ALL_ACCESS
, NULL
, 0, 1 )) return 0;
171 if (!(ret
= InterlockedCompareExchangePointer( &crit
->LockSemaphore
, sem
, 0 )))
174 NtClose(sem
); /* somebody beat us to it */
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
);
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
;
239 crit
->DebugInfo
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
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
;
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
;
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
)
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
);
343 LONG
*lock
= (LONG
*)&crit
->LockSemaphore
;
344 InterlockedExchange( lock
, 1 );
345 RtlWakeAddressSingle( lock
);
346 ret
= STATUS_SUCCESS
;
348 if (ret
) RtlRaiseStatus( ret
);
353 /******************************************************************************
354 * RtlEnterCriticalSection (NTDLL.@)
356 NTSTATUS WINAPI
RtlEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
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
;
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
);
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
)
398 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1)
400 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
401 crit
->RecursionCount
= 1;
404 else if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
406 InterlockedIncrement( &crit
->LockCount
);
407 crit
->RecursionCount
++;
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
)));
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
);
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.
491 C_ASSERT( sizeof(struct srw_lock
) == 4 );
493 /***********************************************************************
494 * RtlInitializeSRWLock (NTDLL.@)
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
)
508 /***********************************************************************
509 * RtlAcquireSRWLockExclusive (NTDLL.@)
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
);
524 union { struct srw_lock s
; LONG l
; } old
, new;
534 /* Not locked exclusive or shared. We can try to grab it. */
536 --new.s
.exclusive_waiters
;
543 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
546 RtlWaitOnAddress( &u
.s
->owners
, &new.s
.owners
, sizeof(short), NULL
);
550 /***********************************************************************
551 * RtlAcquireSRWLockShared (NTDLL.@)
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
};
563 union { struct srw_lock s
; LONG l
; } old
, new;
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. */
582 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
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;
602 if (old
.s
.owners
!= -1) ERR("Lock %p is not owned exclusive!\n", lock
);
605 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
607 if (new.s
.exclusive_waiters
)
608 RtlWakeAddressSingle( &u
.s
->owners
);
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;
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
);
630 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
633 RtlWakeAddressSingle( &u
.s
->owners
);
636 /***********************************************************************
637 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
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;
656 /* Not locked exclusive or shared. We can try to grab it. */
664 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
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;
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. */
694 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
699 /***********************************************************************
700 * RtlInitializeConditionVariable (NTDLL.@)
702 * Initializes the condition variable with NULL.
705 * variable [O] condition variable
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.
721 * variable [I/O] condition variable to wake up.
727 * The calling thread does not have to own any lock in order to call
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.
755 * variable [I/O] condition variable
756 * crit [I/O] critical section to leave temporarily
757 * timeout [I] timeout
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
;
768 RtlLeaveCriticalSection( crit
);
769 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
770 RtlEnterCriticalSection( crit
);
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.
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)
788 * see NtWaitForKeyedEvent for all possible return values.
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
;
799 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
800 RtlReleaseSRWLockShared( lock
);
802 RtlReleaseSRWLockExclusive( lock
);
804 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
806 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
807 RtlAcquireSRWLockShared( lock
);
809 RtlAcquireSRWLockExclusive( lock
);
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.
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 ))
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
)
862 return (*(const UCHAR
*)addr
== *(const UCHAR
*)cmp
);
864 return (*(const USHORT
*)addr
== *(const USHORT
*)cmp
);
866 return (*(const ULONG
*)addr
== *(const ULONG
*)cmp
);
868 return (*(const ULONG64
*)addr
== *(const ULONG64
*)cmp
);
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
;
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
;
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(). */
913 list_remove( &entry
.entry
);
914 spin_unlock( &queue
->lock
);
916 TRACE("returning %#lx\n", ret
);
918 if (ret
== STATUS_ALERTED
) ret
= STATUS_SUCCESS
;
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
;
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
;
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
;
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. */
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. */
990 list_remove( &entry
->entry
);
995 spin_unlock( &queue
->lock
);
997 if (tid
) NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)tid
);