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 "ntdll_misc.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(sync
);
40 WINE_DECLARE_DEBUG_CHANNEL(relay
);
42 /******************************************************************
43 * RtlRunOnceInitialize (NTDLL.@)
45 void WINAPI
RtlRunOnceInitialize( RTL_RUN_ONCE
*once
)
50 /******************************************************************
51 * RtlRunOnceBeginInitialize (NTDLL.@)
53 DWORD WINAPI
RtlRunOnceBeginInitialize( RTL_RUN_ONCE
*once
, ULONG flags
, void **context
)
55 if (flags
& RTL_RUN_ONCE_CHECK_ONLY
)
57 ULONG_PTR val
= (ULONG_PTR
)once
->Ptr
;
59 if (flags
& RTL_RUN_ONCE_ASYNC
) return STATUS_INVALID_PARAMETER
;
60 if ((val
& 3) != 2) return STATUS_UNSUCCESSFUL
;
61 if (context
) *context
= (void *)(val
& ~3);
62 return STATUS_SUCCESS
;
67 ULONG_PTR next
, val
= (ULONG_PTR
)once
->Ptr
;
71 case 0: /* first time */
72 if (!InterlockedCompareExchangePointer( &once
->Ptr
,
73 (flags
& RTL_RUN_ONCE_ASYNC
) ? (void *)3 : (void *)1, 0 ))
74 return STATUS_PENDING
;
77 case 1: /* in progress, wait */
78 if (flags
& RTL_RUN_ONCE_ASYNC
) return STATUS_INVALID_PARAMETER
;
80 if (InterlockedCompareExchangePointer( &once
->Ptr
, (void *)((ULONG_PTR
)&next
| 1),
81 (void *)val
) == (void *)val
)
82 NtWaitForKeyedEvent( 0, &next
, FALSE
, NULL
);
86 if (context
) *context
= (void *)(val
& ~3);
87 return STATUS_SUCCESS
;
89 case 3: /* in progress, async */
90 if (!(flags
& RTL_RUN_ONCE_ASYNC
)) return STATUS_INVALID_PARAMETER
;
91 return STATUS_PENDING
;
96 /******************************************************************
97 * RtlRunOnceComplete (NTDLL.@)
99 DWORD WINAPI
RtlRunOnceComplete( RTL_RUN_ONCE
*once
, ULONG flags
, void *context
)
101 if ((ULONG_PTR
)context
& 3) return STATUS_INVALID_PARAMETER
;
103 if (flags
& RTL_RUN_ONCE_INIT_FAILED
)
105 if (context
) return STATUS_INVALID_PARAMETER
;
106 if (flags
& RTL_RUN_ONCE_ASYNC
) return STATUS_INVALID_PARAMETER
;
108 else context
= (void *)((ULONG_PTR
)context
| 2);
112 ULONG_PTR val
= (ULONG_PTR
)once
->Ptr
;
116 case 1: /* in progress */
117 if (InterlockedCompareExchangePointer( &once
->Ptr
, context
, (void *)val
) != (void *)val
) break;
121 ULONG_PTR next
= *(ULONG_PTR
*)val
;
122 NtReleaseKeyedEvent( 0, (void *)val
, FALSE
, NULL
);
125 return STATUS_SUCCESS
;
127 case 3: /* in progress, async */
128 if (!(flags
& RTL_RUN_ONCE_ASYNC
)) return STATUS_INVALID_PARAMETER
;
129 if (InterlockedCompareExchangePointer( &once
->Ptr
, context
, (void *)val
) != (void *)val
) break;
130 return STATUS_SUCCESS
;
133 return STATUS_UNSUCCESSFUL
;
139 /***********************************************************************
141 ***********************************************************************/
144 static void *no_debug_info_marker
= (void *)(ULONG_PTR
)-1;
146 static BOOL
crit_section_has_debuginfo( const RTL_CRITICAL_SECTION
*crit
)
148 return crit
->DebugInfo
!= NULL
&& crit
->DebugInfo
!= no_debug_info_marker
;
151 static inline HANDLE
get_semaphore( RTL_CRITICAL_SECTION
*crit
)
153 HANDLE ret
= crit
->LockSemaphore
;
157 if (NtCreateSemaphore( &sem
, SEMAPHORE_ALL_ACCESS
, NULL
, 0, 1 )) return 0;
158 if (!(ret
= InterlockedCompareExchangePointer( &crit
->LockSemaphore
, sem
, 0 )))
161 NtClose(sem
); /* somebody beat us to it */
166 static inline NTSTATUS
wait_semaphore( RTL_CRITICAL_SECTION
*crit
, int timeout
)
170 /* debug info is cleared by MakeCriticalSectionGlobal */
171 if (!crit_section_has_debuginfo( crit
) ||
172 ((ret
= unix_funcs
->fast_RtlpWaitForCriticalSection( crit
, timeout
)) == STATUS_NOT_IMPLEMENTED
))
174 HANDLE sem
= get_semaphore( crit
);
177 time
.QuadPart
= timeout
* (LONGLONG
)-10000000;
178 ret
= NtWaitForSingleObject( sem
, FALSE
, &time
);
183 /******************************************************************************
184 * RtlInitializeCriticalSection (NTDLL.@)
186 NTSTATUS WINAPI
RtlInitializeCriticalSection( RTL_CRITICAL_SECTION
*crit
)
188 return RtlInitializeCriticalSectionEx( crit
, 0, 0 );
192 /******************************************************************************
193 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
195 NTSTATUS WINAPI
RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
197 return RtlInitializeCriticalSectionEx( crit
, spincount
, 0 );
201 /******************************************************************************
202 * RtlInitializeCriticalSectionEx (NTDLL.@)
204 NTSTATUS WINAPI
RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
, ULONG flags
)
206 if (flags
& (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN
|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT
))
207 FIXME("(%p,%u,0x%08x) semi-stub\n", crit
, spincount
, flags
);
209 /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
210 * memory from a static pool to hold the debug info. Then heap.c could pass
211 * this flag rather than initialising the process heap CS by hand. If this
212 * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
213 * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
215 if (flags
& RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO
)
216 crit
->DebugInfo
= no_debug_info_marker
;
219 crit
->DebugInfo
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG
));
222 crit
->DebugInfo
->Type
= 0;
223 crit
->DebugInfo
->CreatorBackTraceIndex
= 0;
224 crit
->DebugInfo
->CriticalSection
= crit
;
225 crit
->DebugInfo
->ProcessLocksList
.Blink
= &crit
->DebugInfo
->ProcessLocksList
;
226 crit
->DebugInfo
->ProcessLocksList
.Flink
= &crit
->DebugInfo
->ProcessLocksList
;
227 crit
->DebugInfo
->EntryCount
= 0;
228 crit
->DebugInfo
->ContentionCount
= 0;
229 memset( crit
->DebugInfo
->Spare
, 0, sizeof(crit
->DebugInfo
->Spare
) );
232 crit
->LockCount
= -1;
233 crit
->RecursionCount
= 0;
234 crit
->OwningThread
= 0;
235 crit
->LockSemaphore
= 0;
236 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
237 crit
->SpinCount
= spincount
& ~0x80000000;
238 return STATUS_SUCCESS
;
242 /******************************************************************************
243 * RtlSetCriticalSectionSpinCount (NTDLL.@)
245 ULONG WINAPI
RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION
*crit
, ULONG spincount
)
247 ULONG oldspincount
= crit
->SpinCount
;
248 if (NtCurrentTeb()->Peb
->NumberOfProcessors
<= 1) spincount
= 0;
249 crit
->SpinCount
= spincount
;
254 /******************************************************************************
255 * RtlDeleteCriticalSection (NTDLL.@)
257 NTSTATUS WINAPI
RtlDeleteCriticalSection( RTL_CRITICAL_SECTION
*crit
)
259 crit
->LockCount
= -1;
260 crit
->RecursionCount
= 0;
261 crit
->OwningThread
= 0;
262 if (crit_section_has_debuginfo( crit
))
264 /* only free the ones we made in here */
265 if (!crit
->DebugInfo
->Spare
[0])
267 RtlFreeHeap( GetProcessHeap(), 0, crit
->DebugInfo
);
268 crit
->DebugInfo
= NULL
;
270 if (unix_funcs
->fast_RtlDeleteCriticalSection( crit
) == STATUS_NOT_IMPLEMENTED
)
271 NtClose( crit
->LockSemaphore
);
273 else NtClose( crit
->LockSemaphore
);
274 crit
->LockSemaphore
= 0;
275 return STATUS_SUCCESS
;
279 /******************************************************************************
280 * RtlpWaitForCriticalSection (NTDLL.@)
282 NTSTATUS WINAPI
RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION
*crit
)
284 LONGLONG timeout
= NtCurrentTeb()->Peb
->CriticalSectionTimeout
.QuadPart
/ -10000000;
286 /* Don't allow blocking on a critical section during process termination */
287 if (RtlDllShutdownInProgress())
289 WARN( "process %s is shutting down, returning STATUS_SUCCESS\n",
290 debugstr_w(NtCurrentTeb()->Peb
->ProcessParameters
->ImagePathName
.Buffer
) );
291 return STATUS_SUCCESS
;
296 EXCEPTION_RECORD rec
;
297 NTSTATUS status
= wait_semaphore( crit
, 5 );
300 if ( status
== STATUS_TIMEOUT
)
302 const char *name
= NULL
;
303 if (crit_section_has_debuginfo( crit
)) name
= (char *)crit
->DebugInfo
->Spare
[0];
304 if (!name
) name
= "?";
305 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
306 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
307 status
= wait_semaphore( crit
, 60 );
310 if ( status
== STATUS_TIMEOUT
&& TRACE_ON(relay
) )
312 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
313 crit
, debugstr_a(name
), GetCurrentThreadId(), HandleToULong(crit
->OwningThread
) );
314 status
= wait_semaphore( crit
, 300 );
318 if (status
== STATUS_WAIT_0
) break;
320 /* Throw exception only for Wine internal locks */
321 if (!crit_section_has_debuginfo( crit
) || !crit
->DebugInfo
->Spare
[0]) continue;
323 /* only throw deadlock exception if configured timeout is reached */
324 if (timeout
> 0) continue;
326 rec
.ExceptionCode
= STATUS_POSSIBLE_DEADLOCK
;
327 rec
.ExceptionFlags
= 0;
328 rec
.ExceptionRecord
= NULL
;
329 rec
.ExceptionAddress
= RtlRaiseException
; /* sic */
330 rec
.NumberParameters
= 1;
331 rec
.ExceptionInformation
[0] = (ULONG_PTR
)crit
;
332 RtlRaiseException( &rec
);
334 if (crit_section_has_debuginfo( crit
)) crit
->DebugInfo
->ContentionCount
++;
335 return STATUS_SUCCESS
;
339 /******************************************************************************
340 * RtlpUnWaitCriticalSection (NTDLL.@)
342 NTSTATUS WINAPI
RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION
*crit
)
346 /* debug info is cleared by MakeCriticalSectionGlobal */
347 if (!crit_section_has_debuginfo( crit
) ||
348 ((ret
= unix_funcs
->fast_RtlpUnWaitCriticalSection( crit
)) == STATUS_NOT_IMPLEMENTED
))
350 HANDLE sem
= get_semaphore( crit
);
351 ret
= NtReleaseSemaphore( sem
, 1, NULL
);
353 if (ret
) RtlRaiseStatus( ret
);
358 static inline void small_pause(void)
361 __asm__
__volatile__( "rep;nop" : : : "memory" );
363 __asm__
__volatile__( "" : : : "memory" );
367 /******************************************************************************
368 * RtlEnterCriticalSection (NTDLL.@)
370 NTSTATUS WINAPI
RtlEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
376 if (RtlTryEnterCriticalSection( crit
)) return STATUS_SUCCESS
;
377 for (count
= crit
->SpinCount
; count
> 0; count
--)
379 if (crit
->LockCount
> 0) break; /* more than one waiter, don't bother spinning */
380 if (crit
->LockCount
== -1) /* try again */
382 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1) goto done
;
388 if (InterlockedIncrement( &crit
->LockCount
))
390 if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
392 crit
->RecursionCount
++;
393 return STATUS_SUCCESS
;
396 /* Now wait for it */
397 RtlpWaitForCriticalSection( crit
);
400 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
401 crit
->RecursionCount
= 1;
402 return STATUS_SUCCESS
;
406 /******************************************************************************
407 * RtlTryEnterCriticalSection (NTDLL.@)
409 BOOL WINAPI
RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION
*crit
)
412 if (InterlockedCompareExchange( &crit
->LockCount
, 0, -1 ) == -1)
414 crit
->OwningThread
= ULongToHandle(GetCurrentThreadId());
415 crit
->RecursionCount
= 1;
418 else if (crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()))
420 InterlockedIncrement( &crit
->LockCount
);
421 crit
->RecursionCount
++;
428 /******************************************************************************
429 * RtlIsCriticalSectionLocked (NTDLL.@)
431 BOOL WINAPI
RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION
*crit
)
433 return crit
->RecursionCount
!= 0;
437 /******************************************************************************
438 * RtlIsCriticalSectionLockedByThread (NTDLL.@)
440 BOOL WINAPI
RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION
*crit
)
442 return crit
->OwningThread
== ULongToHandle(GetCurrentThreadId()) &&
443 crit
->RecursionCount
;
447 /******************************************************************************
448 * RtlLeaveCriticalSection (NTDLL.@)
450 NTSTATUS WINAPI
RtlLeaveCriticalSection( RTL_CRITICAL_SECTION
*crit
)
452 if (--crit
->RecursionCount
)
454 if (crit
->RecursionCount
> 0) InterlockedDecrement( &crit
->LockCount
);
455 else ERR( "section %p is not acquired\n", crit
);
459 crit
->OwningThread
= 0;
460 if (InterlockedDecrement( &crit
->LockCount
) >= 0)
462 /* someone is waiting */
463 RtlpUnWaitCriticalSection( crit
);
466 return STATUS_SUCCESS
;
469 /******************************************************************
470 * RtlRunOnceExecuteOnce (NTDLL.@)
472 DWORD WINAPI
RtlRunOnceExecuteOnce( RTL_RUN_ONCE
*once
, PRTL_RUN_ONCE_INIT_FN func
,
473 void *param
, void **context
)
475 DWORD ret
= RtlRunOnceBeginInitialize( once
, 0, context
);
477 if (ret
!= STATUS_PENDING
) return ret
;
479 if (!func( once
, param
, context
))
481 RtlRunOnceComplete( once
, RTL_RUN_ONCE_INIT_FAILED
, NULL
);
482 return STATUS_UNSUCCESSFUL
;
485 return RtlRunOnceComplete( once
, 0, context
? *context
: NULL
);
489 /* SRW locks implementation
491 * The memory layout used by the lock is:
494 * ________________ ________________
495 * | X| #exclusive | #shared |
496 * ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
497 * Since there is no space left for a separate counter of shared access
498 * threads inside the locked section the #shared field is used for multiple
499 * purposes. The following table lists all possible states the lock can be
500 * in, notation: [X, #exclusive, #shared]:
502 * [0, 0, N] -> locked by N shared access threads, if N=0 it's unlocked
503 * [0, >=1, >=1] -> threads are requesting exclusive locks, but there are
504 * still shared access threads inside. #shared should not be incremented
506 * [1, >=1, >=0] -> lock is owned by an exclusive thread and the #shared
507 * counter can be used again to count the number of threads waiting in the
508 * queue for shared access.
510 * the following states are invalid and will never occur:
511 * [0, >=1, 0], [1, 0, >=0]
513 * The main problem arising from the fact that we have no separate counter
514 * of shared access threads inside the locked section is that in the state
515 * [0, >=1, >=1] above we cannot add additional waiting threads to the
516 * shared access queue - it wouldn't be possible to distinguish waiting
517 * threads and those that are still inside. To solve this problem the lock
518 * uses the following approach: a thread that isn't able to allocate a
519 * shared lock just uses the exclusive queue instead. As soon as the thread
520 * is woken up it is in the state [1, >=1, >=0]. In this state it's again
521 * possible to use the shared access queue. The thread atomically moves
522 * itself to the shared access queue and releases the exclusive lock, so
523 * that the "real" exclusive access threads have a chance. As soon as they
524 * are all ready the shared access threads are processed.
527 #define SRWLOCK_MASK_IN_EXCLUSIVE 0x80000000
528 #define SRWLOCK_MASK_EXCLUSIVE_QUEUE 0x7fff0000
529 #define SRWLOCK_MASK_SHARED_QUEUE 0x0000ffff
530 #define SRWLOCK_RES_EXCLUSIVE 0x00010000
531 #define SRWLOCK_RES_SHARED 0x00000001
533 #ifdef WORDS_BIGENDIAN
534 #define srwlock_key_exclusive(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1))
535 #define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1))
537 #define srwlock_key_exclusive(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1))
538 #define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1))
541 static inline void srwlock_check_invalid( unsigned int val
)
543 /* Throw exception if it's impossible to acquire/release this lock. */
544 if ((val
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
) == SRWLOCK_MASK_EXCLUSIVE_QUEUE
||
545 (val
& SRWLOCK_MASK_SHARED_QUEUE
) == SRWLOCK_MASK_SHARED_QUEUE
)
546 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED
);
549 static inline unsigned int srwlock_lock_exclusive( unsigned int *dest
, int incr
)
551 unsigned int val
, tmp
;
552 /* Atomically modifies the value of *dest by adding incr. If the shared
553 * queue is empty and there are threads waiting for exclusive access, then
554 * sets the mark SRWLOCK_MASK_IN_EXCLUSIVE to signal other threads that
555 * they are allowed again to use the shared queue counter. */
556 for (val
= *dest
;; val
= tmp
)
559 srwlock_check_invalid( tmp
);
560 if ((tmp
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
) && !(tmp
& SRWLOCK_MASK_SHARED_QUEUE
))
561 tmp
|= SRWLOCK_MASK_IN_EXCLUSIVE
;
562 if ((tmp
= InterlockedCompareExchange( (int *)dest
, tmp
, val
)) == val
)
568 static inline unsigned int srwlock_unlock_exclusive( unsigned int *dest
, int incr
)
570 unsigned int val
, tmp
;
571 /* Atomically modifies the value of *dest by adding incr. If the queue of
572 * threads waiting for exclusive access is empty, then remove the
573 * SRWLOCK_MASK_IN_EXCLUSIVE flag (only the shared queue counter will
575 for (val
= *dest
;; val
= tmp
)
578 srwlock_check_invalid( tmp
);
579 if (!(tmp
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
))
580 tmp
&= SRWLOCK_MASK_SHARED_QUEUE
;
581 if ((tmp
= InterlockedCompareExchange( (int *)dest
, tmp
, val
)) == val
)
587 static inline void srwlock_leave_exclusive( RTL_SRWLOCK
*lock
, unsigned int val
)
589 /* Used when a thread leaves an exclusive section. If there are other
590 * exclusive access threads they are processed first, followed by
591 * the shared waiters. */
592 if (val
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
)
593 NtReleaseKeyedEvent( 0, srwlock_key_exclusive(lock
), FALSE
, NULL
);
596 val
&= SRWLOCK_MASK_SHARED_QUEUE
; /* remove SRWLOCK_MASK_IN_EXCLUSIVE */
598 NtReleaseKeyedEvent( 0, srwlock_key_shared(lock
), FALSE
, NULL
);
602 static inline void srwlock_leave_shared( RTL_SRWLOCK
*lock
, unsigned int val
)
604 /* Wake up one exclusive thread as soon as the last shared access thread
606 if ((val
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
) && !(val
& SRWLOCK_MASK_SHARED_QUEUE
))
607 NtReleaseKeyedEvent( 0, srwlock_key_exclusive(lock
), FALSE
, NULL
);
610 /***********************************************************************
611 * RtlInitializeSRWLock (NTDLL.@)
614 * Please note that SRWLocks do not keep track of the owner of a lock.
615 * It doesn't make any difference which thread for example unlocks an
616 * SRWLock (see corresponding tests). This implementation uses two
617 * keyed events (one for the exclusive waiters and one for the shared
618 * waiters) and is limited to 2^15-1 waiting threads.
620 void WINAPI
RtlInitializeSRWLock( RTL_SRWLOCK
*lock
)
625 /***********************************************************************
626 * RtlAcquireSRWLockExclusive (NTDLL.@)
629 * Unlike RtlAcquireResourceExclusive this function doesn't allow
630 * nested calls from the same thread. "Upgrading" a shared access lock
631 * to an exclusive access lock also doesn't seem to be supported.
633 void WINAPI
RtlAcquireSRWLockExclusive( RTL_SRWLOCK
*lock
)
635 if (unix_funcs
->fast_RtlAcquireSRWLockExclusive( lock
) != STATUS_NOT_IMPLEMENTED
)
638 if (srwlock_lock_exclusive( (unsigned int *)&lock
->Ptr
, SRWLOCK_RES_EXCLUSIVE
))
639 NtWaitForKeyedEvent( 0, srwlock_key_exclusive(lock
), FALSE
, NULL
);
642 /***********************************************************************
643 * RtlAcquireSRWLockShared (NTDLL.@)
646 * Do not call this function recursively - it will only succeed when
647 * there are no threads waiting for an exclusive lock!
649 void WINAPI
RtlAcquireSRWLockShared( RTL_SRWLOCK
*lock
)
651 unsigned int val
, tmp
;
653 if (unix_funcs
->fast_RtlAcquireSRWLockShared( lock
) != STATUS_NOT_IMPLEMENTED
)
656 /* Acquires a shared lock. If it's currently not possible to add elements to
657 * the shared queue, then request exclusive access instead. */
658 for (val
= *(unsigned int *)&lock
->Ptr
;; val
= tmp
)
660 if ((val
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
) && !(val
& SRWLOCK_MASK_IN_EXCLUSIVE
))
661 tmp
= val
+ SRWLOCK_RES_EXCLUSIVE
;
663 tmp
= val
+ SRWLOCK_RES_SHARED
;
664 if ((tmp
= InterlockedCompareExchange( (int *)&lock
->Ptr
, tmp
, val
)) == val
)
668 /* Drop exclusive access again and instead requeue for shared access. */
669 if ((val
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
) && !(val
& SRWLOCK_MASK_IN_EXCLUSIVE
))
671 NtWaitForKeyedEvent( 0, srwlock_key_exclusive(lock
), FALSE
, NULL
);
672 val
= srwlock_unlock_exclusive( (unsigned int *)&lock
->Ptr
, (SRWLOCK_RES_SHARED
673 - SRWLOCK_RES_EXCLUSIVE
) ) - SRWLOCK_RES_EXCLUSIVE
;
674 srwlock_leave_exclusive( lock
, val
);
677 if (val
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
)
678 NtWaitForKeyedEvent( 0, srwlock_key_shared(lock
), FALSE
, NULL
);
681 /***********************************************************************
682 * RtlReleaseSRWLockExclusive (NTDLL.@)
684 void WINAPI
RtlReleaseSRWLockExclusive( RTL_SRWLOCK
*lock
)
686 if (unix_funcs
->fast_RtlReleaseSRWLockExclusive( lock
) != STATUS_NOT_IMPLEMENTED
)
689 srwlock_leave_exclusive( lock
, srwlock_unlock_exclusive( (unsigned int *)&lock
->Ptr
,
690 - SRWLOCK_RES_EXCLUSIVE
) - SRWLOCK_RES_EXCLUSIVE
);
693 /***********************************************************************
694 * RtlReleaseSRWLockShared (NTDLL.@)
696 void WINAPI
RtlReleaseSRWLockShared( RTL_SRWLOCK
*lock
)
698 if (unix_funcs
->fast_RtlReleaseSRWLockShared( lock
) != STATUS_NOT_IMPLEMENTED
)
701 srwlock_leave_shared( lock
, srwlock_lock_exclusive( (unsigned int *)&lock
->Ptr
,
702 - SRWLOCK_RES_SHARED
) - SRWLOCK_RES_SHARED
);
705 /***********************************************************************
706 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
709 * Similarly to AcquireSRWLockExclusive, recursive calls are not allowed
710 * and will fail with a FALSE return value.
712 BOOLEAN WINAPI
RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK
*lock
)
716 if ((ret
= unix_funcs
->fast_RtlTryAcquireSRWLockExclusive( lock
)) != STATUS_NOT_IMPLEMENTED
)
717 return (ret
== STATUS_SUCCESS
);
719 return InterlockedCompareExchange( (int *)&lock
->Ptr
, SRWLOCK_MASK_IN_EXCLUSIVE
|
720 SRWLOCK_RES_EXCLUSIVE
, 0 ) == 0;
723 /***********************************************************************
724 * RtlTryAcquireSRWLockShared (NTDLL.@)
726 BOOLEAN WINAPI
RtlTryAcquireSRWLockShared( RTL_SRWLOCK
*lock
)
728 unsigned int val
, tmp
;
731 if ((ret
= unix_funcs
->fast_RtlTryAcquireSRWLockShared( lock
)) != STATUS_NOT_IMPLEMENTED
)
732 return (ret
== STATUS_SUCCESS
);
734 for (val
= *(unsigned int *)&lock
->Ptr
;; val
= tmp
)
736 if (val
& SRWLOCK_MASK_EXCLUSIVE_QUEUE
)
738 if ((tmp
= InterlockedCompareExchange( (int *)&lock
->Ptr
, val
+ SRWLOCK_RES_SHARED
, val
)) == val
)
744 /***********************************************************************
745 * RtlInitializeConditionVariable (NTDLL.@)
747 * Initializes the condition variable with NULL.
750 * variable [O] condition variable
755 void WINAPI
RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
757 variable
->Ptr
= NULL
;
760 /***********************************************************************
761 * RtlWakeConditionVariable (NTDLL.@)
763 * Wakes up one thread waiting on the condition variable.
766 * variable [I/O] condition variable to wake up.
772 * The calling thread does not have to own any lock in order to call
775 void WINAPI
RtlWakeConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
777 if (unix_funcs
->fast_RtlWakeConditionVariable( variable
, 1 ) == STATUS_NOT_IMPLEMENTED
)
779 InterlockedIncrement( (int *)&variable
->Ptr
);
780 RtlWakeAddressSingle( variable
);
784 /***********************************************************************
785 * RtlWakeAllConditionVariable (NTDLL.@)
787 * See WakeConditionVariable, wakes up all waiting threads.
789 void WINAPI
RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE
*variable
)
791 if (unix_funcs
->fast_RtlWakeConditionVariable( variable
, INT_MAX
) == STATUS_NOT_IMPLEMENTED
)
793 InterlockedIncrement( (int *)&variable
->Ptr
);
794 RtlWakeAddressAll( variable
);
798 /***********************************************************************
799 * RtlSleepConditionVariableCS (NTDLL.@)
801 * Atomically releases the critical section and suspends the thread,
802 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
803 * the critical section again and returns.
806 * variable [I/O] condition variable
807 * crit [I/O] critical section to leave temporarily
808 * timeout [I] timeout
811 * see NtWaitForKeyedEvent for all possible return values.
813 NTSTATUS WINAPI
RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE
*variable
, RTL_CRITICAL_SECTION
*crit
,
814 const LARGE_INTEGER
*timeout
)
816 const void *value
= variable
->Ptr
;
819 RtlLeaveCriticalSection( crit
);
820 if ((status
= unix_funcs
->fast_wait_cv( variable
, value
, timeout
)) == STATUS_NOT_IMPLEMENTED
)
821 status
= RtlWaitOnAddress( &variable
->Ptr
, &value
, sizeof(value
), timeout
);
822 RtlEnterCriticalSection( crit
);
826 /***********************************************************************
827 * RtlSleepConditionVariableSRW (NTDLL.@)
829 * Atomically releases the SRWLock and suspends the thread,
830 * waiting for a Wake(All)ConditionVariable event. Afterwards it enters
831 * the SRWLock again with the same access rights and returns.
834 * variable [I/O] condition variable
835 * lock [I/O] SRWLock to leave temporarily
836 * timeout [I] timeout
837 * flags [I] type of the current lock (exclusive / shared)
840 * see NtWaitForKeyedEvent for all possible return values.
843 * the behaviour is undefined if the thread doesn't own the lock.
845 NTSTATUS WINAPI
RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE
*variable
, RTL_SRWLOCK
*lock
,
846 const LARGE_INTEGER
*timeout
, ULONG flags
)
848 const void *value
= variable
->Ptr
;
851 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
852 RtlReleaseSRWLockShared( lock
);
854 RtlReleaseSRWLockExclusive( lock
);
856 if ((status
= unix_funcs
->fast_wait_cv( variable
, value
, timeout
)) == STATUS_NOT_IMPLEMENTED
)
857 status
= RtlWaitOnAddress( variable
, &value
, sizeof(value
), timeout
);
859 if (flags
& RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
)
860 RtlAcquireSRWLockShared( lock
);
862 RtlAcquireSRWLockExclusive( lock
);
866 /***********************************************************************
867 * RtlWaitOnAddress (NTDLL.@)
869 NTSTATUS WINAPI
RtlWaitOnAddress( const void *addr
, const void *cmp
, SIZE_T size
,
870 const LARGE_INTEGER
*timeout
)
872 return unix_funcs
->RtlWaitOnAddress( addr
, cmp
, size
, timeout
);
875 /***********************************************************************
876 * RtlWakeAddressAll (NTDLL.@)
878 void WINAPI
RtlWakeAddressAll( const void *addr
)
880 return unix_funcs
->RtlWakeAddressAll( addr
);
883 /***********************************************************************
884 * RtlWakeAddressSingle (NTDLL.@)
886 void WINAPI
RtlWakeAddressSingle( const void *addr
)
888 return unix_funcs
->RtlWakeAddressSingle( addr
);