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 "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
)
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 if ((ULONG_PTR
)crit
->LockSemaphore
> 1) return crit
->LockSemaphore
;
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
);
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
;
234 crit
->DebugInfo
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
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
;
269 /******************************************************************************
270 * RtlDeleteCriticalSection (NTDLL.@)
272 NTSTATUS WINAPI
RtlDeleteCriticalSection( RTL_CRITICAL_SECTION
*crit
)
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
;
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
)
333 HANDLE sem
= get_semaphore( crit
);
335 if (sem
) ret
= NtReleaseSemaphore( sem
, 1, NULL
);
338 LONG
*lock
= (LONG
*)&crit
->LockSemaphore
;
339 InterlockedExchange( lock
, 1 );
340 RtlWakeAddressSingle( lock
);
341 ret
= STATUS_SUCCESS
;
343 if (ret
) RtlRaiseStatus( ret
);
348 /******************************************************************************
349 * RtlEnterCriticalSection (NTDLL.@)
351 NTSTATUS WINAPI
RtlEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
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
;
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
);
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
)
393 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1)
395 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
396 crit
->RecursionCount
= 1;
399 else if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
401 InterlockedIncrement( &crit
->LockCount
);
402 crit
->RecursionCount
++;
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
)));
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
);
471 /* bit 0 - if the lock is held exclusive. bit 1.. - number of exclusive waiters. */
472 short exclusive_waiters
;
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.@)
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
)
504 /***********************************************************************
505 * RtlAcquireSRWLockExclusive (NTDLL.@)
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 );
520 union { struct srw_lock s
; LONG l
; } old
, new;
530 /* Not locked exclusive or shared. We can try to grab it. */
532 new.s
.exclusive_waiters
-= 2;
533 new.s
.exclusive_waiters
|= 1;
540 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
543 RtlWaitOnAddress( &u
.s
->owners
, &new.s
.owners
, sizeof(short), NULL
);
547 /***********************************************************************
548 * RtlAcquireSRWLockShared (NTDLL.@)
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
};
560 union { struct srw_lock s
; LONG l
; } old
, new;
568 if (!old
.s
.exclusive_waiters
)
570 /* Not locked exclusive, and no exclusive waiters.
571 * We can try to grab it. */
579 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
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;
599 if (!(old
.s
.exclusive_waiters
& 1)) ERR("Lock %p is not owned exclusive!\n", lock
);
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
);
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;
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
);
628 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
631 RtlWakeAddressSingle( &u
.s
->owners
);
634 /***********************************************************************
635 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
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;
654 /* Not locked exclusive or shared. We can try to grab it. */
656 new.s
.exclusive_waiters
|= 1;
663 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
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;
682 if (!old
.s
.exclusive_waiters
)
684 /* Not locked exclusive, and no exclusive waiters.
685 * We can try to grab it. */
693 } while (InterlockedCompareExchange( u
.l
, new.l
, old
.l
) != old
.l
);
698 /***********************************************************************
699 * RtlInitializeConditionVariable (NTDLL.@)
701 * Initializes the condition variable with NULL.
704 * variable [O] condition variable
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.
720 * variable [I/O] condition variable to wake up.
726 * The calling thread does not have to own any lock in order to call
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.
754 * variable [I/O] condition variable
755 * crit [I/O] critical section to leave temporarily
756 * timeout [I] timeout
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
;
767 RtlLeaveCriticalSection( crit
);
768 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
769 RtlEnterCriticalSection( crit
);
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.
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)
787 * see NtWaitForKeyedEvent for all possible return values.
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
;
798 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
799 RtlReleaseSRWLockShared( lock
);
801 RtlReleaseSRWLockExclusive( lock
);
803 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
805 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
806 RtlAcquireSRWLockShared( lock
);
808 RtlAcquireSRWLockExclusive( lock
);
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.
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 ))
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
)
861 return (*(const UCHAR
*)addr
== *(const UCHAR
*)cmp
);
863 return (*(const USHORT
*)addr
== *(const USHORT
*)cmp
);
865 return (*(const ULONG
*)addr
== *(const ULONG
*)cmp
);
867 return (*(const ULONG64
*)addr
== *(const ULONG64
*)cmp
);
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
;
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
;
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(). */
912 spin_lock( &queue
->lock
);
914 list_remove( &entry
.entry
);
915 spin_unlock( &queue
->lock
);
918 TRACE("returning %#lx\n", ret
);
920 if (ret
== STATUS_ALERTED
) ret
= STATUS_SUCCESS
;
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
;
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
)
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
;
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
;
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. */
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. */
994 list_remove( &entry
->entry
);
999 spin_unlock( &queue
->lock
);
1001 if (tid
) NtAlertThreadByThreadId( (HANDLE
)(DWORD_PTR
)tid
);
1004 /*************************************************************************
1005 * RtlInitializeSListHead (NTDLL.@)
1007 void WINAPI
RtlInitializeSListHead(PSLIST_HEADER list
)
1010 list
->Alignment
= list
->Region
= 0;
1011 list
->Header16
.HeaderType
= 1; /* we use the 16-byte header */
1013 list
->Alignment
= 0;
1017 /*************************************************************************
1018 * RtlQueryDepthSList (NTDLL.@)
1020 WORD WINAPI
RtlQueryDepthSList(PSLIST_HEADER list
)
1023 return list
->Header16
.Depth
;
1029 /*************************************************************************
1030 * RtlFirstEntrySList (NTDLL.@)
1032 PSLIST_ENTRY WINAPI
RtlFirstEntrySList(const SLIST_HEADER
* list
)
1035 return (SLIST_ENTRY
*)((ULONG_PTR
)list
->Header16
.NextEntry
<< 4);
1037 return list
->Next
.Next
;
1041 /*************************************************************************
1042 * RtlInterlockedFlushSList (NTDLL.@)
1044 PSLIST_ENTRY WINAPI
RtlInterlockedFlushSList(PSLIST_HEADER list
)
1046 SLIST_HEADER old
, new;
1049 if (!list
->Header16
.NextEntry
) return NULL
;
1050 new.Alignment
= new.Region
= 0;
1051 new.Header16
.HeaderType
= 1; /* we use the 16-byte header */
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);
1059 if (!list
->Next
.Next
) return NULL
;
1064 new.Sequence
= old
.Sequence
+ 1;
1065 } while (InterlockedCompareExchange64((__int64
*)&list
->Alignment
, new.Alignment
,
1066 old
.Alignment
) != old
.Alignment
);
1067 return old
.Next
.Next
;
1071 /*************************************************************************
1072 * RtlInterlockedPushEntrySList (NTDLL.@)
1074 PSLIST_ENTRY WINAPI
RtlInterlockedPushEntrySList(PSLIST_HEADER list
, PSLIST_ENTRY entry
)
1076 SLIST_HEADER old
, new;
1079 new.Header16
.NextEntry
= (ULONG_PTR
)entry
>> 4;
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);
1089 new.Next
.Next
= entry
;
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
;
1102 /*************************************************************************
1103 * RtlInterlockedPopEntrySList (NTDLL.@)
1105 PSLIST_ENTRY WINAPI
RtlInterlockedPopEntrySList(PSLIST_HEADER list
)
1107 SLIST_HEADER old
, new;
1114 if (!(entry
= (SLIST_ENTRY
*)((ULONG_PTR
)old
.Header16
.NextEntry
<< 4))) return NULL
;
1115 /* entry could be deleted by another thread */
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;
1126 } while (!InterlockedCompareExchange128((__int64
*)list
, new.Region
, new.Alignment
, (__int64
*)&old
));
1131 if (!(entry
= old
.Next
.Next
)) return NULL
;
1132 /* entry could be deleted by another thread */
1135 new.Next
.Next
= entry
->Next
;
1136 new.Depth
= old
.Depth
- 1;
1137 new.Sequence
= old
.Sequence
+ 1;
1143 } while (InterlockedCompareExchange64((__int64
*)&list
->Alignment
, new.Alignment
,
1144 old
.Alignment
) != old
.Alignment
);
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;
1158 new.Header16
.NextEntry
= (ULONG_PTR
)first
>> 4;
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);
1168 new.Next
.Next
= first
;
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
;
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
)
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
)
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
)
1241 RtlEnterCriticalSection( &rwl
->rtlCS
);
1242 if( rwl
->iNumberActive
== 0 ) /* lock is free */
1244 rwl
->iNumberActive
= -1;
1247 else if( rwl
->iNumberActive
< 0 ) /* exclusive lock in progress */
1249 if( rwl
->hOwningThreadId
== ULongToHandle(GetCurrentThreadId()) )
1252 rwl
->iNumberActive
--;
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 */
1272 if( retVal
== 1 ) rwl
->hOwningThreadId
= ULongToHandle(GetCurrentThreadId());
1275 RtlLeaveCriticalSection( &rwl
->rtlCS
);
1279 /***********************************************************************
1280 * RtlAcquireResourceShared (NTDLL.@)
1282 BYTE WINAPI
RtlAcquireResourceShared(LPRTL_RWLOCK rwl
, BYTE fWait
)
1284 NTSTATUS status
= STATUS_UNSUCCESSFUL
;
1290 RtlEnterCriticalSection( &rwl
->rtlCS
);
1291 if( rwl
->iNumberActive
< 0 )
1293 if( rwl
->hOwningThreadId
== ULongToHandle(GetCurrentThreadId()) )
1295 rwl
->iNumberActive
--;
1302 rwl
->uSharedWaiters
++;
1303 RtlLeaveCriticalSection( &rwl
->rtlCS
);
1304 status
= NtWaitForSingleObject( rwl
->hSharedReleaseSemaphore
, FALSE
, NULL
);
1305 if( HIWORD(status
) ) break;
1311 if( status
!= STATUS_WAIT_0
) /* otherwise RtlReleaseResource() has already done it */
1312 rwl
->iNumberActive
++;
1317 RtlLeaveCriticalSection( &rwl
->rtlCS
);
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
)
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
);