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
33 #define NONAMELESSUNION
36 #include "wine/debug.h"
37 #include "wine/list.h"
38 #include "ntdll_misc.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(sync
);
41 WINE_DECLARE_DEBUG_CHANNEL(relay
);
43 static const char *debugstr_timeout( const LARGE_INTEGER
*timeout
)
45 if (!timeout
) return "(infinite)";
46 return wine_dbgstr_longlong( timeout
->QuadPart
);
49 /******************************************************************
50 * RtlRunOnceInitialize (NTDLL.@)
52 void WINAPI
RtlRunOnceInitialize( RTL_RUN_ONCE
*once
)
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
;
74 ULONG_PTR next
, val
= (ULONG_PTR
)once
->Ptr
;
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
;
84 case 1: /* in progress, wait */
85 if (flags
& RTL_RUN_ONCE_ASYNC
) return STATUS_INVALID_PARAMETER
;
87 if (InterlockedCompareExchangePointer( &once
->Ptr
, (void *)((ULONG_PTR
)&next
| 1),
88 (void *)val
) == (void *)val
)
89 NtWaitForKeyedEvent( 0, &next
, FALSE
, NULL
);
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);
119 ULONG_PTR val
= (ULONG_PTR
)once
->Ptr
;
123 case 1: /* in progress */
124 if (InterlockedCompareExchangePointer( &once
->Ptr
, context
, (void *)val
) != (void *)val
) break;
128 ULONG_PTR next
= *(ULONG_PTR
*)val
;
129 NtReleaseKeyedEvent( 0, (void *)val
, FALSE
, NULL
);
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
;
140 return STATUS_UNSUCCESSFUL
;
146 /***********************************************************************
148 ***********************************************************************/
151 static void *no_debug_info_marker
= (void *)(ULONG_PTR
)-1;
153 static BOOL
crit_section_has_debuginfo( const RTL_CRITICAL_SECTION
*crit
)
155 return crit
->DebugInfo
!= NULL
&& crit
->DebugInfo
!= no_debug_info_marker
;
158 static inline HANDLE
get_semaphore( RTL_CRITICAL_SECTION
*crit
)
160 HANDLE ret
= crit
->LockSemaphore
;
164 if (NtCreateSemaphore( &sem
, SEMAPHORE_ALL_ACCESS
, NULL
, 0, 1 )) return 0;
165 if (!(ret
= InterlockedCompareExchangePointer( &crit
->LockSemaphore
, sem
, 0 )))
168 NtClose(sem
); /* somebody beat us to it */
173 static inline NTSTATUS
wait_semaphore( RTL_CRITICAL_SECTION
*crit
, int timeout
)
175 LARGE_INTEGER time
= {.QuadPart
= timeout
* (LONGLONG
)-10000000};
177 /* debug info is cleared by MakeCriticalSectionGlobal */
178 if (!crit_section_has_debuginfo( crit
))
180 HANDLE sem
= get_semaphore( crit
);
181 return NtWaitForSingleObject( sem
, FALSE
, &time
);
185 int *lock
= (int *)&crit
->LockSemaphore
;
186 while (!InterlockedCompareExchange( lock
, 0, 1 ))
188 static const int zero
;
189 /* this may wait longer than specified in case of multiple wake-ups */
190 if (RtlWaitOnAddress( lock
, &zero
, sizeof(int), &time
) == STATUS_TIMEOUT
)
191 return STATUS_TIMEOUT
;
193 return STATUS_WAIT_0
;
197 /******************************************************************************
198 * RtlInitializeCriticalSection (NTDLL.@)
200 NTSTATUS WINAPI
RtlInitializeCriticalSection( RTL_CRITICAL_SECTION
*crit
)
202 return RtlInitializeCriticalSectionEx( crit
, 0, 0 );
206 /******************************************************************************
207 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
209 NTSTATUS WINAPI
RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
211 return RtlInitializeCriticalSectionEx( crit
, spincount
, 0 );
215 /******************************************************************************
216 * RtlInitializeCriticalSectionEx (NTDLL.@)
218 NTSTATUS WINAPI
RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
, ULONG flags
)
220 if (flags
& (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN
|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT
))
221 FIXME("(%p,%u,0x%08x) semi-stub\n", crit
, spincount
, flags
);
223 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
224 * memory from a static pool to hold the debug info. Then heap.c could pass
225 * this flag rather than initialising the process heap CS by hand. If this
226 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
227 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
229 if (flags
& RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO
)
230 crit
->DebugInfo
= no_debug_info_marker
;
233 crit
->DebugInfo
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
236 crit
->DebugInfo
->Type
= 0;
237 crit
->DebugInfo
->CreatorBackTraceIndex
= 0;
238 crit
->DebugInfo
->CriticalSection
= crit
;
239 crit
->DebugInfo
->ProcessLocksList
.Blink
= &crit
->DebugInfo
->ProcessLocksList
;
240 crit
->DebugInfo
->ProcessLocksList
.Flink
= &crit
->DebugInfo
->ProcessLocksList
;
241 crit
->DebugInfo
->EntryCount
= 0;
242 crit
->DebugInfo
->ContentionCount
= 0;
243 memset( crit
->DebugInfo
->Spare
, 0, sizeof(crit
->DebugInfo
->Spare
) );
246 crit
->LockCount
= -1;
247 crit
->RecursionCount
= 0;
248 crit
->OwningThread
= 0;
249 crit
->LockSemaphore
= 0;
250 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
251 crit
->SpinCount
= spincount
& ~0x80000000;
252 return STATUS_SUCCESS
;
256 /******************************************************************************
257 * RtlSetCriticalSectionSpinCount (NTDLL.@)
259 ULONG WINAPI
RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
261 ULONG oldspincount
= crit
->SpinCount
;
262 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
263 crit
->SpinCount
= spincount
;
268 /******************************************************************************
269 * RtlDeleteCriticalSection (NTDLL.@)
271 NTSTATUS WINAPI
RtlDeleteCriticalSection( RTL_CRITICAL_SECTION
*crit
)
273 crit
->LockCount
= -1;
274 crit
->RecursionCount
= 0;
275 crit
->OwningThread
= 0;
276 if (crit_section_has_debuginfo( crit
))
278 /* only free the ones we made in here */
279 if (!crit
->DebugInfo
->Spare
[0])
281 RtlFreeHeap( GetProcessHeap(), 0, crit
->DebugInfo
);
282 crit
->DebugInfo
= NULL
;
285 else NtClose( crit
->LockSemaphore
);
286 crit
->LockSemaphore
= 0;
287 return STATUS_SUCCESS
;
291 /******************************************************************************
292 * RtlpWaitForCriticalSection (NTDLL.@)
294 NTSTATUS WINAPI
RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION
*crit
)
296 LONGLONG timeout
= NtCurrentTeb()->Peb
->CriticalSectionTimeout
.QuadPart
/ -10000000;
298 /* Don't allow blocking on a critical section during process termination */
299 if (RtlDllShutdownInProgress())
301 WARN( "process %s is shutting down, returning STATUS_SUCCESS\n",
302 debugstr_w(NtCurrentTeb()->Peb
->ProcessParameters
->ImagePathName
.Buffer
) );
303 return STATUS_SUCCESS
;
308 EXCEPTION_RECORD rec
;
309 NTSTATUS status
= wait_semaphore( crit
, 5 );
312 if ( status
== STATUS_TIMEOUT
)
314 const char *name
= NULL
;
315 if (crit_section_has_debuginfo( crit
)) name
= (char *)crit
->DebugInfo
->Spare
[0];
316 if (!name
) name
= "?";
317 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
318 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
319 status
= wait_semaphore( crit
, 60 );
322 if ( status
== STATUS_TIMEOUT
&& TRACE_ON(relay
) )
324 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
325 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
326 status
= wait_semaphore( crit
, 300 );
330 if (status
== STATUS_WAIT_0
) break;
332 /* Throw exception only for Wine internal locks */
333 if (!crit_section_has_debuginfo( crit
) || !crit
->DebugInfo
->Spare
[0]) continue;
335 /* only throw deadlock exception if configured timeout is reached */
336 if (timeout
> 0) continue;
338 rec
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
339 rec
.ExceptionFlags
= 0;
340 rec
.ExceptionRecord
= NULL
;
341 rec
.ExceptionAddress
= RtlRaiseException
; /* sic */
342 rec
.NumberParameters
= 1;
343 rec
.ExceptionInformation
[0] = (ULONG_PTR
)crit
;
344 RtlRaiseException( &rec
);
346 if (crit_section_has_debuginfo( crit
)) crit
->DebugInfo
->ContentionCount
++;
347 return STATUS_SUCCESS
;
351 /******************************************************************************
352 * RtlpUnWaitCriticalSection (NTDLL.@)
354 NTSTATUS WINAPI
RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION
*crit
)
358 /* debug info is cleared by MakeCriticalSectionGlobal */
359 if (!crit_section_has_debuginfo( crit
))
361 HANDLE sem
= get_semaphore( crit
);
362 ret
= NtReleaseSemaphore( sem
, 1, NULL
);
366 int *lock
= (int *)&crit
->LockSemaphore
;
367 InterlockedExchange( lock
, 1 );
368 RtlWakeAddressSingle( lock
);
369 ret
= STATUS_SUCCESS
;
371 if (ret
) RtlRaiseStatus( ret
);
376 static inline void small_pause(void)
379 __asm__
__volatile__( "rep;nop" : : : "memory" );
381 __asm__
__volatile__( "" : : : "memory" );
385 /******************************************************************************
386 * RtlEnterCriticalSection (NTDLL.@)
388 NTSTATUS WINAPI
RtlEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
394 if (RtlTryEnterCriticalSection( crit
)) return STATUS_SUCCESS
;
395 for (count
= crit
->SpinCount
; count
> 0; count
--)
397 if (crit
->LockCount
> 0) break; /* more than one waiter, don't bother spinning */
398 if (crit
->LockCount
== -1) /* try again */
400 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1) goto done
;
406 if (InterlockedIncrement( &crit
->LockCount
))
408 if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
410 crit
->RecursionCount
++;
411 return STATUS_SUCCESS
;
414 /* Now wait for it */
415 RtlpWaitForCriticalSection( crit
);
418 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
419 crit
->RecursionCount
= 1;
420 return STATUS_SUCCESS
;
424 /******************************************************************************
425 * RtlTryEnterCriticalSection (NTDLL.@)
427 BOOL WINAPI
RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
430 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1)
432 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
433 crit
->RecursionCount
= 1;
436 else if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
438 InterlockedIncrement( &crit
->LockCount
);
439 crit
->RecursionCount
++;
446 /******************************************************************************
447 * RtlIsCriticalSectionLocked (NTDLL.@)
449 BOOL WINAPI
RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION
*crit
)
451 return crit
->RecursionCount
!= 0;
455 /******************************************************************************
456 * RtlIsCriticalSectionLockedByThread (NTDLL.@)
458 BOOL WINAPI
RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION
*crit
)
460 return crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()) &&
461 crit
->RecursionCount
;
465 /******************************************************************************
466 * RtlLeaveCriticalSection (NTDLL.@)
468 NTSTATUS WINAPI
RtlLeaveCriticalSection( RTL_CRITICAL_SECTION
*crit
)
470 if (--crit
->RecursionCount
)
472 if (crit
->RecursionCount
> 0) InterlockedDecrement( &crit
->LockCount
);
473 else ERR( "section %p is not acquired\n", crit
);
477 crit
->OwningThread
= 0;
478 if (InterlockedDecrement( &crit
->LockCount
) >= 0)
480 /* someone is waiting */
481 RtlpUnWaitCriticalSection( crit
);
484 return STATUS_SUCCESS
;
487 /******************************************************************
488 * RtlRunOnceExecuteOnce (NTDLL.@)
490 DWORD WINAPI
RtlRunOnceExecuteOnce( RTL_RUN_ONCE
*once
, PRTL_RUN_ONCE_INIT_FN func
,
491 void *param
, void **context
)
493 DWORD ret
= RtlRunOnceBeginInitialize( once
, 0, context
);
495 if (ret
!= STATUS_PENDING
) return ret
;
497 if (!func( once
, param
, context
))
499 RtlRunOnceComplete( once
, RTL_RUN_ONCE_INIT_FAILED
, NULL
);
500 return STATUS_UNSUCCESSFUL
;
503 return RtlRunOnceComplete( once
, 0, context
? *context
: NULL
);
508 short exclusive_waiters
;
510 /* Number of shared owners, or -1 if owned exclusive.
512 * Sadly Windows has no equivalent to FUTEX_WAIT_BITSET, so in order to wake
513 * up *only* exclusive or *only* shared waiters (and thus avoid spurious
514 * wakeups), we need to wait on two different addresses.
515 * RtlAcquireSRWLockShared() needs to know the values of "exclusive_waiters"
516 * and "owners", but RtlAcquireSRWLockExclusive() only needs to know the
517 * value of "owners", so the former can wait on the entire structure, and
518 * the latter waits only on the "owners" member. Note then that "owners"
519 * must not be the first element in the structure.
523 C_ASSERT( sizeof(struct srw_lock
) == 4 );
525 /***********************************************************************
526 * RtlInitializeSRWLock (NTDLL.@)
529 * Please note that SRWLocks do not keep track of the owner of a lock.
530 * It doesn't make any difference which thread for example unlocks an
531 * SRWLock (see corresponding tests). This implementation uses two
532 * keyed events (one for the exclusive waiters and one for the shared
533 * waiters) and is limited to 2^15-1 waiting threads.
535 void WINAPI
RtlInitializeSRWLock( RTL_SRWLOCK
*lock
)
540 /***********************************************************************
541 * RtlAcquireSRWLockExclusive (NTDLL.@)
544 * Unlike RtlAcquireResourceExclusive this function doesn't allow
545 * nested calls from the same thread. "Upgrading" a shared access lock
546 * to an exclusive access lock also doesn't seem to be supported.
548 void WINAPI
RtlAcquireSRWLockExclusive( RTL_SRWLOCK
*lock
)
550 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
552 InterlockedIncrement16( &u
.s
->exclusive_waiters
);
556 union { struct srw_lock s
; LONG l
; } old
, new;
566 /* Not locked exclusive or shared. We can try to grab it. */
568 --new.s
.exclusive_waiters
;
575 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
578 RtlWaitOnAddress( &u
.s
->owners
, &new.s
.owners
, sizeof(short), NULL
);
582 /***********************************************************************
583 * RtlAcquireSRWLockShared (NTDLL.@)
586 * Do not call this function recursively - it will only succeed when
587 * there are no threads waiting for an exclusive lock!
589 void WINAPI
RtlAcquireSRWLockShared( RTL_SRWLOCK
*lock
)
591 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
595 union { struct srw_lock s
; LONG l
; } old
, new;
603 if (old
.s
.owners
!= -1 && !old
.s
.exclusive_waiters
)
605 /* Not locked exclusive, and no exclusive waiters.
606 * We can try to grab it. */
614 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
617 RtlWaitOnAddress( u
.s
, &new.s
, sizeof(struct srw_lock
), NULL
);
621 /***********************************************************************
622 * RtlReleaseSRWLockExclusive (NTDLL.@)
624 void WINAPI
RtlReleaseSRWLockExclusive( RTL_SRWLOCK
*lock
)
626 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
627 union { struct srw_lock s
; LONG l
; } old
, new;
634 if (old
.s
.owners
!= -1) ERR("Lock %p is not owned exclusive!\n", lock
);
637 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
639 if (new.s
.exclusive_waiters
)
640 RtlWakeAddressSingle( &u
.s
->owners
);
642 RtlWakeAddressAll( u
.s
);
645 /***********************************************************************
646 * RtlReleaseSRWLockShared (NTDLL.@)
648 void WINAPI
RtlReleaseSRWLockShared( RTL_SRWLOCK
*lock
)
650 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
651 union { struct srw_lock s
; LONG l
; } old
, new;
658 if (old
.s
.owners
== -1) ERR("Lock %p is owned exclusive!\n", lock
);
659 else if (!old
.s
.owners
) ERR("Lock %p is not owned shared!\n", lock
);
662 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
665 RtlWakeAddressSingle( &u
.s
->owners
);
668 /***********************************************************************
669 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
672 * Similarly to AcquireSRWLockExclusive, recursive calls are not allowed
673 * and will fail with a FALSE return value.
675 BOOLEAN WINAPI
RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK
*lock
)
677 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
678 union { struct srw_lock s
; LONG l
; } old
, new;
688 /* Not locked exclusive or shared. We can try to grab it. */
696 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
701 /***********************************************************************
702 * RtlTryAcquireSRWLockShared (NTDLL.@)
704 BOOLEAN WINAPI
RtlTryAcquireSRWLockShared( RTL_SRWLOCK
*lock
)
706 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
707 union { struct srw_lock s
; LONG l
; } old
, new;
715 if (old
.s
.owners
!= -1 && !old
.s
.exclusive_waiters
)
717 /* Not locked exclusive, and no exclusive waiters.
718 * We can try to grab it. */
726 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
731 /***********************************************************************
732 * RtlInitializeConditionVariable (NTDLL.@)
734 * Initializes the condition variable with NULL.
737 * variable [O] condition variable
742 void WINAPI
RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
744 variable
->Ptr
= NULL
;
747 /***********************************************************************
748 * RtlWakeConditionVariable (NTDLL.@)
750 * Wakes up one thread waiting on the condition variable.
753 * variable [I/O] condition variable to wake up.
759 * The calling thread does not have to own any lock in order to call
762 void WINAPI
RtlWakeConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
764 InterlockedIncrement( (int *)&variable
->Ptr
);
765 RtlWakeAddressSingle( variable
);
768 /***********************************************************************
769 * RtlWakeAllConditionVariable (NTDLL.@)
771 * See WakeConditionVariable, wakes up all waiting threads.
773 void WINAPI
RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
775 InterlockedIncrement( (int *)&variable
->Ptr
);
776 RtlWakeAddressAll( variable
);
779 /***********************************************************************
780 * RtlSleepConditionVariableCS (NTDLL.@)
782 * Atomically releases the critical section and suspends the thread,
783 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
784 * the critical section again and returns.
787 * variable [I/O] condition variable
788 * crit [I/O] critical section to leave temporarily
789 * timeout [I] timeout
792 * see NtWaitForKeyedEvent for all possible return values.
794 NTSTATUS WINAPI
RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE
*variable
, RTL_CRITICAL_SECTION
*crit
,
795 const LARGE_INTEGER
*timeout
)
797 int value
= *(int *)&variable
->Ptr
;
800 RtlLeaveCriticalSection( crit
);
801 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
802 RtlEnterCriticalSection( crit
);
806 /***********************************************************************
807 * RtlSleepConditionVariableSRW (NTDLL.@)
809 * Atomically releases the SRWLock and suspends the thread,
810 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
811 * the SRWLock again with the same access rights and returns.
814 * variable [I/O] condition variable
815 * lock [I/O] SRWLock to leave temporarily
816 * timeout [I] timeout
817 * flags [I] type of the current lock (exclusive / shared)
820 * see NtWaitForKeyedEvent for all possible return values.
823 * the behaviour is undefined if the thread doesn't own the lock.
825 NTSTATUS WINAPI
RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE
*variable
, RTL_SRWLOCK
*lock
,
826 const LARGE_INTEGER
*timeout
, ULONG flags
)
828 int value
= *(int *)&variable
->Ptr
;
831 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
832 RtlReleaseSRWLockShared( lock
);
834 RtlReleaseSRWLockExclusive( lock
);
836 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
838 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
839 RtlAcquireSRWLockShared( lock
);
841 RtlAcquireSRWLockExclusive( lock
);
845 /* RtlWaitOnAddress() and RtlWakeAddress*(), hereafter referred to as "Win32
846 * futexes", offer futex-like semantics with a variable set of address sizes,
847 * but are limited to a single process. They are also fair—the documentation
848 * specifies this, and tests bear it out.
850 * On Windows they are implemented using NtAlertThreadByThreadId and
851 * NtWaitForAlertByThreadId, which manipulate a single flag (similar to an
852 * auto-reset event) per thread. This can be tested by attempting to wake a
853 * thread waiting in RtlWaitOnAddress() via NtAlertThreadByThreadId.
869 static struct futex_queue futex_queues
[256];
871 static struct futex_queue
*get_futex_queue( const void *addr
)
873 ULONG_PTR val
= (ULONG_PTR
)addr
;
875 return &futex_queues
[(val
>> 4) % ARRAY_SIZE(futex_queues
)];
878 static void spin_lock( LONG
*lock
)
880 while (InterlockedCompareExchange( lock
, -1, 0 ))
884 static void spin_unlock( LONG
*lock
)
886 InterlockedExchange( lock
, 0 );
889 static BOOL
compare_addr( const void *addr
, const void *cmp
, SIZE_T size
)
894 return (*(const UCHAR
*)addr
== *(const UCHAR
*)cmp
);
896 return (*(const USHORT
*)addr
== *(const USHORT
*)cmp
);
898 return (*(const ULONG
*)addr
== *(const ULONG
*)cmp
);
900 return (*(const ULONG64
*)addr
== *(const ULONG64
*)cmp
);
906 /***********************************************************************
907 * RtlWaitOnAddress (NTDLL.@)
909 NTSTATUS WINAPI
RtlWaitOnAddress( const void *addr
, const void *cmp
, SIZE_T size
,
910 const LARGE_INTEGER
*timeout
)
912 struct futex_queue
*queue
= get_futex_queue( addr
);
913 struct futex_entry entry
;
916 TRACE("addr %p cmp %p size %#Ix timeout %s\n", addr
, cmp
, size
, debugstr_timeout( timeout
));
918 if (size
!= 1 && size
!= 2 && size
!= 4 && size
!= 8)
919 return STATUS_INVALID_PARAMETER
;
922 entry
.tid
= GetCurrentThreadId();
924 spin_lock( &queue
->lock
);
926 /* Do the comparison inside of the spinlock, to reduce spurious wakeups. */
928 if (!compare_addr( addr
, cmp
, size
))
930 spin_unlock( &queue
->lock
);
931 return STATUS_SUCCESS
;
934 if (!queue
->queue
.next
)
935 list_init( &queue
->queue
);
936 list_add_tail( &queue
->queue
, &entry
.entry
);
938 spin_unlock( &queue
->lock
);
940 ret
= NtWaitForAlertByThreadId( NULL
, timeout
);
942 spin_lock( &queue
->lock
);
943 /* We may have already been removed by a call to RtlWakeAddressSingle(). */
945 list_remove( &entry
.entry
);
946 spin_unlock( &queue
->lock
);
948 TRACE("returning %#x\n", ret
);
950 if (ret
== STATUS_ALERTED
) ret
= STATUS_SUCCESS
;
954 /***********************************************************************
955 * RtlWakeAddressAll (NTDLL.@)
957 void WINAPI
RtlWakeAddressAll( const void *addr
)
959 struct futex_queue
*queue
= get_futex_queue( addr
);
960 unsigned int count
= 0, i
;
961 struct futex_entry
*entry
;
968 spin_lock( &queue
->lock
);
970 if (!queue
->queue
.next
)
971 list_init(&queue
->queue
);
973 LIST_FOR_EACH_ENTRY( entry
, &queue
->queue
, struct futex_entry
, entry
)
975 if (entry
->addr
== addr
)
977 /* Try to buffer wakes, so that we don't make a system call while
978 * holding a spinlock. */
979 if (count
< ARRAY_SIZE(tids
))
980 tids
[count
++] = entry
->tid
;
982 NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)entry
->tid
);
986 spin_unlock( &queue
->lock
);
988 for (i
= 0; i
< count
; ++i
)
989 NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)tids
[i
] );
992 /***********************************************************************
993 * RtlWakeAddressSingle (NTDLL.@)
995 void WINAPI
RtlWakeAddressSingle( const void *addr
)
997 struct futex_queue
*queue
= get_futex_queue( addr
);
998 struct futex_entry
*entry
;
1001 TRACE("%p\n", addr
);
1005 spin_lock( &queue
->lock
);
1007 if (!queue
->queue
.next
)
1008 list_init(&queue
->queue
);
1010 LIST_FOR_EACH_ENTRY( entry
, &queue
->queue
, struct futex_entry
, entry
)
1012 if (entry
->addr
== addr
)
1014 /* Try to buffer wakes, so that we don't make a system call while
1015 * holding a spinlock. */
1018 /* Remove this entry from the queue, so that a simultaneous call to
1019 * RtlWakeAddressSingle() will not also wake it—two simultaneous
1020 * calls must wake at least two waiters if they exist. */
1022 list_remove( &entry
->entry
);
1027 spin_unlock( &queue
->lock
);
1029 if (tid
) NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)tid
);