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 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];
165 static inline HANDLE
get_semaphore( RTL_CRITICAL_SECTION
*crit
)
167 HANDLE ret
= crit
->LockSemaphore
;
171 if (NtCreateSemaphore( &sem
, SEMAPHORE_ALL_ACCESS
, NULL
, 0, 1 )) return 0;
172 if (!(ret
= InterlockedCompareExchangePointer( &crit
->LockSemaphore
, sem
, 0 )))
175 NtClose(sem
); /* somebody beat us to it */
180 static inline NTSTATUS
wait_semaphore( RTL_CRITICAL_SECTION
*crit
, int timeout
)
182 LARGE_INTEGER time
= {.QuadPart
= timeout
* (LONGLONG
)-10000000};
184 /* debug info is cleared by MakeCriticalSectionGlobal */
185 if (!crit_section_has_debuginfo( crit
))
187 HANDLE sem
= get_semaphore( crit
);
188 return NtWaitForSingleObject( sem
, FALSE
, &time
);
192 LONG
*lock
= (LONG
*)&crit
->LockSemaphore
;
193 while (!InterlockedCompareExchange( lock
, 0, 1 ))
195 static const LONG zero
;
196 /* this may wait longer than specified in case of multiple wake-ups */
197 if (RtlWaitOnAddress( lock
, &zero
, sizeof(LONG
), &time
) == STATUS_TIMEOUT
)
198 return STATUS_TIMEOUT
;
200 return STATUS_WAIT_0
;
204 /******************************************************************************
205 * RtlInitializeCriticalSection (NTDLL.@)
207 NTSTATUS WINAPI
RtlInitializeCriticalSection( RTL_CRITICAL_SECTION
*crit
)
209 return RtlInitializeCriticalSectionEx( crit
, 0, 0 );
213 /******************************************************************************
214 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
216 NTSTATUS WINAPI
RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
218 return RtlInitializeCriticalSectionEx( crit
, spincount
, 0 );
222 /******************************************************************************
223 * RtlInitializeCriticalSectionEx (NTDLL.@)
225 NTSTATUS WINAPI
RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
, ULONG flags
)
227 if (flags
& (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN
|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT
))
228 FIXME("(%p,%lu,0x%08lx) semi-stub\n", crit
, spincount
, flags
);
230 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
231 * memory from a static pool to hold the debug info. Then heap.c could pass
232 * this flag rather than initialising the process heap CS by hand. If this
233 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
234 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
236 if (flags
& RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO
)
237 crit
->DebugInfo
= no_debug_info_marker
;
240 crit
->DebugInfo
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
243 crit
->DebugInfo
->Type
= 0;
244 crit
->DebugInfo
->CreatorBackTraceIndex
= 0;
245 crit
->DebugInfo
->CriticalSection
= crit
;
246 crit
->DebugInfo
->ProcessLocksList
.Blink
= &crit
->DebugInfo
->ProcessLocksList
;
247 crit
->DebugInfo
->ProcessLocksList
.Flink
= &crit
->DebugInfo
->ProcessLocksList
;
248 crit
->DebugInfo
->EntryCount
= 0;
249 crit
->DebugInfo
->ContentionCount
= 0;
250 memset( crit
->DebugInfo
->Spare
, 0, sizeof(crit
->DebugInfo
->Spare
) );
253 crit
->LockCount
= -1;
254 crit
->RecursionCount
= 0;
255 crit
->OwningThread
= 0;
256 crit
->LockSemaphore
= 0;
257 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
258 crit
->SpinCount
= spincount
& ~0x80000000;
259 return STATUS_SUCCESS
;
263 /******************************************************************************
264 * RtlSetCriticalSectionSpinCount (NTDLL.@)
266 ULONG WINAPI
RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
268 ULONG oldspincount
= crit
->SpinCount
;
269 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
270 crit
->SpinCount
= spincount
;
275 /******************************************************************************
276 * RtlDeleteCriticalSection (NTDLL.@)
278 NTSTATUS WINAPI
RtlDeleteCriticalSection( RTL_CRITICAL_SECTION
*crit
)
280 crit
->LockCount
= -1;
281 crit
->RecursionCount
= 0;
282 crit
->OwningThread
= 0;
283 if (crit_section_has_debuginfo( crit
))
285 /* only free the ones we made in here */
286 if (!crit
->DebugInfo
->Spare
[0])
288 RtlFreeHeap( GetProcessHeap(), 0, crit
->DebugInfo
);
289 crit
->DebugInfo
= NULL
;
292 else NtClose( crit
->LockSemaphore
);
293 crit
->LockSemaphore
= 0;
294 return STATUS_SUCCESS
;
298 /******************************************************************************
299 * RtlpWaitForCriticalSection (NTDLL.@)
301 NTSTATUS WINAPI
RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION
*crit
)
303 LONGLONG timeout
= NtCurrentTeb()->Peb
->CriticalSectionTimeout
.QuadPart
/ -10000000;
305 /* Don't allow blocking on a critical section during process termination */
306 if (RtlDllShutdownInProgress())
308 WARN( "process %s is shutting down, returning STATUS_SUCCESS\n",
309 debugstr_w(NtCurrentTeb()->Peb
->ProcessParameters
->ImagePathName
.Buffer
) );
310 return STATUS_SUCCESS
;
315 EXCEPTION_RECORD rec
;
316 NTSTATUS status
= wait_semaphore( crit
, 5 );
319 if ( status
== STATUS_TIMEOUT
)
321 const char *name
= crit_section_get_name( crit
);
323 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (60 sec)\n",
324 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
325 status
= wait_semaphore( crit
, 60 );
328 if ( status
== STATUS_TIMEOUT
&& TRACE_ON(relay
) )
330 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (5 min)\n",
331 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
332 status
= wait_semaphore( crit
, 300 );
336 if (status
== STATUS_WAIT_0
) break;
338 /* Throw exception only for Wine internal locks */
339 if (!crit_section_has_debuginfo( crit
) || !crit
->DebugInfo
->Spare
[0]) continue;
341 /* only throw deadlock exception if configured timeout is reached */
342 if (timeout
> 0) continue;
344 rec
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
345 rec
.ExceptionFlags
= 0;
346 rec
.ExceptionRecord
= NULL
;
347 rec
.ExceptionAddress
= RtlRaiseException
; /* sic */
348 rec
.NumberParameters
= 1;
349 rec
.ExceptionInformation
[0] = (ULONG_PTR
)crit
;
350 RtlRaiseException( &rec
);
352 if (crit_section_has_debuginfo( crit
)) crit
->DebugInfo
->ContentionCount
++;
353 return STATUS_SUCCESS
;
357 /******************************************************************************
358 * RtlpUnWaitCriticalSection (NTDLL.@)
360 NTSTATUS WINAPI
RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION
*crit
)
364 /* debug info is cleared by MakeCriticalSectionGlobal */
365 if (!crit_section_has_debuginfo( crit
))
367 HANDLE sem
= get_semaphore( crit
);
368 ret
= NtReleaseSemaphore( sem
, 1, NULL
);
372 LONG
*lock
= (LONG
*)&crit
->LockSemaphore
;
373 InterlockedExchange( lock
, 1 );
374 RtlWakeAddressSingle( lock
);
375 ret
= STATUS_SUCCESS
;
377 if (ret
) RtlRaiseStatus( ret
);
382 /******************************************************************************
383 * RtlEnterCriticalSection (NTDLL.@)
385 NTSTATUS WINAPI
RtlEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
391 if (RtlTryEnterCriticalSection( crit
)) return STATUS_SUCCESS
;
392 for (count
= crit
->SpinCount
; count
> 0; count
--)
394 if (crit
->LockCount
> 0) break; /* more than one waiter, don't bother spinning */
395 if (crit
->LockCount
== -1) /* try again */
397 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1) goto done
;
403 if (InterlockedIncrement( &crit
->LockCount
))
405 if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
407 crit
->RecursionCount
++;
408 return STATUS_SUCCESS
;
411 /* Now wait for it */
412 RtlpWaitForCriticalSection( crit
);
415 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
416 crit
->RecursionCount
= 1;
417 return STATUS_SUCCESS
;
421 /******************************************************************************
422 * RtlTryEnterCriticalSection (NTDLL.@)
424 BOOL WINAPI
RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
427 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1)
429 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
430 crit
->RecursionCount
= 1;
433 else if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
435 InterlockedIncrement( &crit
->LockCount
);
436 crit
->RecursionCount
++;
443 /******************************************************************************
444 * RtlIsCriticalSectionLocked (NTDLL.@)
446 BOOL WINAPI
RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION
*crit
)
448 return crit
->RecursionCount
!= 0;
452 /******************************************************************************
453 * RtlIsCriticalSectionLockedByThread (NTDLL.@)
455 BOOL WINAPI
RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION
*crit
)
457 return crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()) &&
458 crit
->RecursionCount
;
462 /******************************************************************************
463 * RtlLeaveCriticalSection (NTDLL.@)
465 NTSTATUS WINAPI
RtlLeaveCriticalSection( RTL_CRITICAL_SECTION
*crit
)
467 if (--crit
->RecursionCount
)
469 if (crit
->RecursionCount
> 0) InterlockedDecrement( &crit
->LockCount
);
470 else ERR( "section %p %s is not acquired\n", crit
, debugstr_a( crit_section_get_name( crit
)));
474 crit
->OwningThread
= 0;
475 if (InterlockedDecrement( &crit
->LockCount
) >= 0)
477 /* someone is waiting */
478 RtlpUnWaitCriticalSection( crit
);
481 return STATUS_SUCCESS
;
484 /******************************************************************
485 * RtlRunOnceExecuteOnce (NTDLL.@)
487 DWORD WINAPI
RtlRunOnceExecuteOnce( RTL_RUN_ONCE
*once
, PRTL_RUN_ONCE_INIT_FN func
,
488 void *param
, void **context
)
490 DWORD ret
= RtlRunOnceBeginInitialize( once
, 0, context
);
492 if (ret
!= STATUS_PENDING
) return ret
;
494 if (!func( once
, param
, context
))
496 RtlRunOnceComplete( once
, RTL_RUN_ONCE_INIT_FAILED
, NULL
);
497 return STATUS_UNSUCCESSFUL
;
500 return RtlRunOnceComplete( once
, 0, context
? *context
: NULL
);
505 short exclusive_waiters
;
507 /* Number of shared owners, or -1 if owned exclusive.
509 * Sadly Windows has no equivalent to FUTEX_WAIT_BITSET, so in order to wake
510 * up *only* exclusive or *only* shared waiters (and thus avoid spurious
511 * wakeups), we need to wait on two different addresses.
512 * RtlAcquireSRWLockShared() needs to know the values of "exclusive_waiters"
513 * and "owners", but RtlAcquireSRWLockExclusive() only needs to know the
514 * value of "owners", so the former can wait on the entire structure, and
515 * the latter waits only on the "owners" member. Note then that "owners"
516 * must not be the first element in the structure.
520 C_ASSERT( sizeof(struct srw_lock
) == 4 );
522 /***********************************************************************
523 * RtlInitializeSRWLock (NTDLL.@)
526 * Please note that SRWLocks do not keep track of the owner of a lock.
527 * It doesn't make any difference which thread for example unlocks an
528 * SRWLock (see corresponding tests). This implementation uses two
529 * keyed events (one for the exclusive waiters and one for the shared
530 * waiters) and is limited to 2^15-1 waiting threads.
532 void WINAPI
RtlInitializeSRWLock( RTL_SRWLOCK
*lock
)
537 /***********************************************************************
538 * RtlAcquireSRWLockExclusive (NTDLL.@)
541 * Unlike RtlAcquireResourceExclusive this function doesn't allow
542 * nested calls from the same thread. "Upgrading" a shared access lock
543 * to an exclusive access lock also doesn't seem to be supported.
545 void WINAPI
RtlAcquireSRWLockExclusive( RTL_SRWLOCK
*lock
)
547 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
549 InterlockedIncrement16( &u
.s
->exclusive_waiters
);
553 union { struct srw_lock s
; LONG l
; } old
, new;
563 /* Not locked exclusive or shared. We can try to grab it. */
565 --new.s
.exclusive_waiters
;
572 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
575 RtlWaitOnAddress( &u
.s
->owners
, &new.s
.owners
, sizeof(short), NULL
);
579 /***********************************************************************
580 * RtlAcquireSRWLockShared (NTDLL.@)
583 * Do not call this function recursively - it will only succeed when
584 * there are no threads waiting for an exclusive lock!
586 void WINAPI
RtlAcquireSRWLockShared( RTL_SRWLOCK
*lock
)
588 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
592 union { struct srw_lock s
; LONG l
; } old
, new;
600 if (old
.s
.owners
!= -1 && !old
.s
.exclusive_waiters
)
602 /* Not locked exclusive, and no exclusive waiters.
603 * We can try to grab it. */
611 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
614 RtlWaitOnAddress( u
.s
, &new.s
, sizeof(struct srw_lock
), NULL
);
618 /***********************************************************************
619 * RtlReleaseSRWLockExclusive (NTDLL.@)
621 void WINAPI
RtlReleaseSRWLockExclusive( RTL_SRWLOCK
*lock
)
623 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
624 union { struct srw_lock s
; LONG l
; } old
, new;
631 if (old
.s
.owners
!= -1) ERR("Lock %p is not owned exclusive!\n", lock
);
634 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
636 if (new.s
.exclusive_waiters
)
637 RtlWakeAddressSingle( &u
.s
->owners
);
639 RtlWakeAddressAll( u
.s
);
642 /***********************************************************************
643 * RtlReleaseSRWLockShared (NTDLL.@)
645 void WINAPI
RtlReleaseSRWLockShared( RTL_SRWLOCK
*lock
)
647 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
648 union { struct srw_lock s
; LONG l
; } old
, new;
655 if (old
.s
.owners
== -1) ERR("Lock %p is owned exclusive!\n", lock
);
656 else if (!old
.s
.owners
) ERR("Lock %p is not owned shared!\n", lock
);
659 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
662 RtlWakeAddressSingle( &u
.s
->owners
);
665 /***********************************************************************
666 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
669 * Similarly to AcquireSRWLockExclusive, recursive calls are not allowed
670 * and will fail with a FALSE return value.
672 BOOLEAN WINAPI
RtlTryAcquireSRWLockExclusive( 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;
685 /* Not locked exclusive or shared. We can try to grab it. */
693 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
698 /***********************************************************************
699 * RtlTryAcquireSRWLockShared (NTDLL.@)
701 BOOLEAN WINAPI
RtlTryAcquireSRWLockShared( RTL_SRWLOCK
*lock
)
703 union { RTL_SRWLOCK
*rtl
; struct srw_lock
*s
; LONG
*l
; } u
= { lock
};
704 union { struct srw_lock s
; LONG l
; } old
, new;
712 if (old
.s
.owners
!= -1 && !old
.s
.exclusive_waiters
)
714 /* Not locked exclusive, and no exclusive waiters.
715 * We can try to grab it. */
723 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
728 /***********************************************************************
729 * RtlInitializeConditionVariable (NTDLL.@)
731 * Initializes the condition variable with NULL.
734 * variable [O] condition variable
739 void WINAPI
RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
741 variable
->Ptr
= NULL
;
744 /***********************************************************************
745 * RtlWakeConditionVariable (NTDLL.@)
747 * Wakes up one thread waiting on the condition variable.
750 * variable [I/O] condition variable to wake up.
756 * The calling thread does not have to own any lock in order to call
759 void WINAPI
RtlWakeConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
761 InterlockedIncrement( (LONG
*)&variable
->Ptr
);
762 RtlWakeAddressSingle( variable
);
765 /***********************************************************************
766 * RtlWakeAllConditionVariable (NTDLL.@)
768 * See WakeConditionVariable, wakes up all waiting threads.
770 void WINAPI
RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
772 InterlockedIncrement( (LONG
*)&variable
->Ptr
);
773 RtlWakeAddressAll( variable
);
776 /***********************************************************************
777 * RtlSleepConditionVariableCS (NTDLL.@)
779 * Atomically releases the critical section and suspends the thread,
780 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
781 * the critical section again and returns.
784 * variable [I/O] condition variable
785 * crit [I/O] critical section to leave temporarily
786 * timeout [I] timeout
789 * see NtWaitForKeyedEvent for all possible return values.
791 NTSTATUS WINAPI
RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE
*variable
, RTL_CRITICAL_SECTION
*crit
,
792 const LARGE_INTEGER
*timeout
)
794 int value
= *(int *)&variable
->Ptr
;
797 RtlLeaveCriticalSection( crit
);
798 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
799 RtlEnterCriticalSection( crit
);
803 /***********************************************************************
804 * RtlSleepConditionVariableSRW (NTDLL.@)
806 * Atomically releases the SRWLock and suspends the thread,
807 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
808 * the SRWLock again with the same access rights and returns.
811 * variable [I/O] condition variable
812 * lock [I/O] SRWLock to leave temporarily
813 * timeout [I] timeout
814 * flags [I] type of the current lock (exclusive / shared)
817 * see NtWaitForKeyedEvent for all possible return values.
820 * the behaviour is undefined if the thread doesn't own the lock.
822 NTSTATUS WINAPI
RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE
*variable
, RTL_SRWLOCK
*lock
,
823 const LARGE_INTEGER
*timeout
, ULONG flags
)
825 int value
= *(int *)&variable
->Ptr
;
828 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
829 RtlReleaseSRWLockShared( lock
);
831 RtlReleaseSRWLockExclusive( lock
);
833 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
835 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
836 RtlAcquireSRWLockShared( lock
);
838 RtlAcquireSRWLockExclusive( lock
);
842 /* RtlWaitOnAddress() and RtlWakeAddress*(), hereafter referred to as "Win32
843 * futexes", offer futex-like semantics with a variable set of address sizes,
844 * but are limited to a single process. They are also fair—the documentation
845 * specifies this, and tests bear it out.
847 * On Windows they are implemented using NtAlertThreadByThreadId and
848 * NtWaitForAlertByThreadId, which manipulate a single flag (similar to an
849 * auto-reset event) per thread. This can be tested by attempting to wake a
850 * thread waiting in RtlWaitOnAddress() via NtAlertThreadByThreadId.
866 static struct futex_queue futex_queues
[256];
868 static struct futex_queue
*get_futex_queue( const void *addr
)
870 ULONG_PTR val
= (ULONG_PTR
)addr
;
872 return &futex_queues
[(val
>> 4) % ARRAY_SIZE(futex_queues
)];
875 static void spin_lock( LONG
*lock
)
877 while (InterlockedCompareExchange( lock
, -1, 0 ))
881 static void spin_unlock( LONG
*lock
)
883 InterlockedExchange( lock
, 0 );
886 static BOOL
compare_addr( const void *addr
, const void *cmp
, SIZE_T size
)
891 return (*(const UCHAR
*)addr
== *(const UCHAR
*)cmp
);
893 return (*(const USHORT
*)addr
== *(const USHORT
*)cmp
);
895 return (*(const ULONG
*)addr
== *(const ULONG
*)cmp
);
897 return (*(const ULONG64
*)addr
== *(const ULONG64
*)cmp
);
903 /***********************************************************************
904 * RtlWaitOnAddress (NTDLL.@)
906 NTSTATUS WINAPI
RtlWaitOnAddress( const void *addr
, const void *cmp
, SIZE_T size
,
907 const LARGE_INTEGER
*timeout
)
909 struct futex_queue
*queue
= get_futex_queue( addr
);
910 struct futex_entry entry
;
913 TRACE("addr %p cmp %p size %#Ix timeout %s\n", addr
, cmp
, size
, debugstr_timeout( timeout
));
915 if (size
!= 1 && size
!= 2 && size
!= 4 && size
!= 8)
916 return STATUS_INVALID_PARAMETER
;
919 entry
.tid
= GetCurrentThreadId();
921 spin_lock( &queue
->lock
);
923 /* Do the comparison inside of the spinlock, to reduce spurious wakeups. */
925 if (!compare_addr( addr
, cmp
, size
))
927 spin_unlock( &queue
->lock
);
928 return STATUS_SUCCESS
;
931 if (!queue
->queue
.next
)
932 list_init( &queue
->queue
);
933 list_add_tail( &queue
->queue
, &entry
.entry
);
935 spin_unlock( &queue
->lock
);
937 ret
= NtWaitForAlertByThreadId( NULL
, timeout
);
939 spin_lock( &queue
->lock
);
940 /* We may have already been removed by a call to RtlWakeAddressSingle(). */
942 list_remove( &entry
.entry
);
943 spin_unlock( &queue
->lock
);
945 TRACE("returning %#lx\n", ret
);
947 if (ret
== STATUS_ALERTED
) ret
= STATUS_SUCCESS
;
951 /***********************************************************************
952 * RtlWakeAddressAll (NTDLL.@)
954 void WINAPI
RtlWakeAddressAll( const void *addr
)
956 struct futex_queue
*queue
= get_futex_queue( addr
);
957 unsigned int count
= 0, i
;
958 struct futex_entry
*entry
;
965 spin_lock( &queue
->lock
);
967 if (!queue
->queue
.next
)
968 list_init(&queue
->queue
);
970 LIST_FOR_EACH_ENTRY( entry
, &queue
->queue
, struct futex_entry
, entry
)
972 if (entry
->addr
== addr
)
974 /* Try to buffer wakes, so that we don't make a system call while
975 * holding a spinlock. */
976 if (count
< ARRAY_SIZE(tids
))
977 tids
[count
++] = entry
->tid
;
979 NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)entry
->tid
);
983 spin_unlock( &queue
->lock
);
985 for (i
= 0; i
< count
; ++i
)
986 NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)tids
[i
] );
989 /***********************************************************************
990 * RtlWakeAddressSingle (NTDLL.@)
992 void WINAPI
RtlWakeAddressSingle( const void *addr
)
994 struct futex_queue
*queue
= get_futex_queue( addr
);
995 struct futex_entry
*entry
;
1002 spin_lock( &queue
->lock
);
1004 if (!queue
->queue
.next
)
1005 list_init(&queue
->queue
);
1007 LIST_FOR_EACH_ENTRY( entry
, &queue
->queue
, struct futex_entry
, entry
)
1009 if (entry
->addr
== addr
)
1011 /* Try to buffer wakes, so that we don't make a system call while
1012 * holding a spinlock. */
1015 /* Remove this entry from the queue, so that a simultaneous call to
1016 * RtlWakeAddressSingle() will not also wake it—two simultaneous
1017 * calls must wake at least two waiters if they exist. */
1019 list_remove( &entry
->entry
);
1024 spin_unlock( &queue
->lock
);
1026 if (tid
) NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)tid
);