ddraw: Use wined3d_bit_scan() in compute_sphere_visibility().
[wine.git] / dlls / ntdll / sync.c
blobbfb30661864f1de8c4255130c4831a669f73d065
1 /*
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
24 #include <limits.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <time.h>
31 #include "ntstatus.h"
32 #define WIN32_NO_STATUS
33 #define NONAMELESSUNION
34 #include "windef.h"
35 #include "winternl.h"
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 )
47 once->Ptr = NULL;
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;
65 for (;;)
67 ULONG_PTR next, val = (ULONG_PTR)once->Ptr;
69 switch (val & 3)
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;
75 break;
77 case 1: /* in progress, wait */
78 if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
79 next = val & ~3;
80 if (InterlockedCompareExchangePointer( &once->Ptr, (void *)((ULONG_PTR)&next | 1),
81 (void *)val ) == (void *)val)
82 NtWaitForKeyedEvent( 0, &next, FALSE, NULL );
83 break;
85 case 2: /* done */
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);
110 for (;;)
112 ULONG_PTR val = (ULONG_PTR)once->Ptr;
114 switch (val & 3)
116 case 1: /* in progress */
117 if (InterlockedCompareExchangePointer( &once->Ptr, context, (void *)val ) != (void *)val) break;
118 val &= ~3;
119 while (val)
121 ULONG_PTR next = *(ULONG_PTR *)val;
122 NtReleaseKeyedEvent( 0, (void *)val, FALSE, NULL );
123 val = next;
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;
132 default:
133 return STATUS_UNSUCCESSFUL;
139 /***********************************************************************
140 * Critical sections
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;
154 if (!ret)
156 HANDLE sem;
157 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
158 if (!(ret = InterlockedCompareExchangePointer( &crit->LockSemaphore, sem, 0 )))
159 ret = sem;
160 else
161 NtClose(sem); /* somebody beat us to it */
163 return ret;
166 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
168 NTSTATUS ret;
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 );
175 LARGE_INTEGER time;
177 time.QuadPart = timeout * (LONGLONG)-10000000;
178 ret = NtWaitForSingleObject( sem, FALSE, &time );
180 return ret;
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;
217 else
219 crit->DebugInfo = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG ));
220 if (crit->DebugInfo)
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;
250 return oldspincount;
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;
294 for (;;)
296 EXCEPTION_RECORD rec;
297 NTSTATUS status = wait_semaphore( crit, 5 );
298 timeout -= 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 );
308 timeout -= 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 );
315 timeout -= 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 )
344 NTSTATUS ret;
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 );
354 return ret;
358 static inline void small_pause(void)
360 #ifdef __i386__
361 __asm__ __volatile__( "rep;nop" : : : "memory" );
362 #else
363 __asm__ __volatile__( "" : : : "memory" );
364 #endif
367 /******************************************************************************
368 * RtlEnterCriticalSection (NTDLL.@)
370 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
372 if (crit->SpinCount)
374 ULONG count;
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;
384 small_pause();
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 );
399 done:
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 )
411 BOOL ret = FALSE;
412 if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1)
414 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
415 crit->RecursionCount = 1;
416 ret = TRUE;
418 else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
420 InterlockedIncrement( &crit->LockCount );
421 crit->RecursionCount++;
422 ret = TRUE;
424 return ret;
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 );
457 else
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:
493 * 32 31 16 0
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
505 * anymore!
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))
536 #else
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))
539 #endif
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)
558 tmp = val + incr;
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)
563 break;
565 return 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
574 * remain). */
575 for (val = *dest;; val = tmp)
577 tmp = val + incr;
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)
582 break;
584 return 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 );
594 else
596 val &= SRWLOCK_MASK_SHARED_QUEUE; /* remove SRWLOCK_MASK_IN_EXCLUSIVE */
597 while (val--)
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
605 * has left. */
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.@)
613 * NOTES
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 )
622 lock->Ptr = NULL;
625 /***********************************************************************
626 * RtlAcquireSRWLockExclusive (NTDLL.@)
628 * NOTES
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)
636 return;
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.@)
645 * NOTES
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)
654 return;
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;
662 else
663 tmp = val + SRWLOCK_RES_SHARED;
664 if ((tmp = InterlockedCompareExchange( (int *)&lock->Ptr, tmp, val )) == val)
665 break;
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)
687 return;
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)
699 return;
701 srwlock_leave_shared( lock, srwlock_lock_exclusive( (unsigned int *)&lock->Ptr,
702 - SRWLOCK_RES_SHARED ) - SRWLOCK_RES_SHARED );
705 /***********************************************************************
706 * RtlTryAcquireSRWLockExclusive (NTDLL.@)
708 * NOTES
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 )
714 NTSTATUS ret;
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;
729 NTSTATUS ret;
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)
737 return FALSE;
738 if ((tmp = InterlockedCompareExchange( (int *)&lock->Ptr, val + SRWLOCK_RES_SHARED, val )) == val)
739 break;
741 return TRUE;
744 /***********************************************************************
745 * RtlInitializeConditionVariable (NTDLL.@)
747 * Initializes the condition variable with NULL.
749 * PARAMS
750 * variable [O] condition variable
752 * RETURNS
753 * Nothing.
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.
765 * PARAMS
766 * variable [I/O] condition variable to wake up.
768 * RETURNS
769 * Nothing.
771 * NOTES
772 * The calling thread does not have to own any lock in order to call
773 * this function.
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.
805 * PARAMS
806 * variable [I/O] condition variable
807 * crit [I/O] critical section to leave temporarily
808 * timeout [I] timeout
810 * RETURNS
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;
817 NTSTATUS status;
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 );
823 return status;
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.
833 * PARAMS
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)
839 * RETURNS
840 * see NtWaitForKeyedEvent for all possible return values.
842 * NOTES
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;
849 NTSTATUS status;
851 if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
852 RtlReleaseSRWLockShared( lock );
853 else
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 );
861 else
862 RtlAcquireSRWLockExclusive( lock );
863 return status;
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 );