From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/kernel32.spec | 1 + dlls/kernel32/tests/sync.c | 40 +++++++++++++++++++++++++++++++++ dlls/kernelbase/kernelbase.spec | 6 ++--- dlls/kernelbase/sync.c | 35 +++++++++++++++++++++++++++++ include/synchapi.h | 12 ++++++++++ include/winnt.h | 8 +++++++ 6 files changed, 99 insertions(+), 3 deletions(-)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 3bb49206c6b..31ed292a6c6 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -376,6 +376,7 @@ # @ stub DisableThreadProfiling @ stdcall DisassociateCurrentThreadFromCallback(ptr) NTDLL.TpDisassociateCallback @ stdcall DiscardVirtualMemory(ptr long) kernelbase.DiscardVirtualMemory +@ stdcall -import DeleteSynchronizationBarrier(ptr) @ stdcall DeleteTimerQueue(long) @ stdcall -import DeleteTimerQueueEx(long long) @ stdcall -import DeleteTimerQueueTimer(long long long) diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c index faaaa381623..9571567647a 100644 --- a/dlls/kernel32/tests/sync.c +++ b/dlls/kernel32/tests/sync.c @@ -69,6 +69,10 @@ static PSLIST_ENTRY (WINAPI *pRtlInterlockedPushListSListEx)(PSLIST_HEADER list, static NTSTATUS (WINAPI *pNtQueueApcThread)(HANDLE,PNTAPCFUNC,ULONG_PTR,ULONG_PTR,ULONG_PTR); static NTSTATUS (WINAPI *pNtTestAlert)(void);
+BOOL (WINAPI *pInitializeSynchronizationBarrier)(SYNCHRONIZATION_BARRIER *,LONG, LONG); +BOOL (WINAPI *pDeleteSynchronizationBarrier)(SYNCHRONIZATION_BARRIER *); +BOOL (WINAPI *pEnterSynchronizationBarrier)(SYNCHRONIZATION_BARRIER*, DWORD); + #ifdef __i386__
#pragma pack(push,1) @@ -2992,6 +2996,38 @@ static void test_QueueUserAPC(void) } }
+static void test_barrier(void) +{ + SYNCHRONIZATION_BARRIER barrier; + BOOL bval; + + if (!pInitializeSynchronizationBarrier) + { + win_skip("InitializeSynchronizationBarrier is not available.\n"); + return; + } + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, 1, -1 ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pEnterSynchronizationBarrier( &barrier, 0 ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pEnterSynchronizationBarrier( &barrier, 0 ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pDeleteSynchronizationBarrier( &barrier ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); +} + START_TEST(sync) { char **argv; @@ -3017,6 +3053,9 @@ START_TEST(sync) pTryAcquireSRWLockExclusive = (void *)GetProcAddress(hdll, "TryAcquireSRWLockExclusive"); pTryAcquireSRWLockShared = (void *)GetProcAddress(hdll, "TryAcquireSRWLockShared"); pQueueUserAPC2 = (void *)GetProcAddress(hdll, "QueueUserAPC2"); + pInitializeSynchronizationBarrier = (void *)GetProcAddress(hdll, "InitializeSynchronizationBarrier"); + pDeleteSynchronizationBarrier = (void *)GetProcAddress(hdll, "DeleteSynchronizationBarrier"); + pEnterSynchronizationBarrier = (void *)GetProcAddress(hdll, "EnterSynchronizationBarrier"); pNtAllocateVirtualMemory = (void *)GetProcAddress(hntdll, "NtAllocateVirtualMemory"); pNtFreeVirtualMemory = (void *)GetProcAddress(hntdll, "NtFreeVirtualMemory"); pNtWaitForSingleObject = (void *)GetProcAddress(hntdll, "NtWaitForSingleObject"); @@ -3064,4 +3103,5 @@ START_TEST(sync) test_alertable_wait(); test_apc_deadlock(); test_crit_section(); + test_barrier(); } diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index d16e2e0e2d4..28c5376e4cd 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -264,7 +264,7 @@ # @ stub DeleteStateAtomValue # @ stub DeleteStateContainer # @ stub DeleteStateContainerValue -# @ stub DeleteSynchronizationBarrier +@ stdcall DeleteSynchronizationBarrier(ptr) @ stdcall DeleteTimerQueueEx(long long) @ stdcall DeleteTimerQueueTimer(long long long) @ stdcall DeleteVolumeMountPointW(wstr) @@ -297,7 +297,7 @@ @ stdcall EncodeSystemPointer(ptr) ntdll.RtlEncodeSystemPointer # @ stub EnterCriticalPolicySectionInternal @ stdcall EnterCriticalSection(ptr) ntdll.RtlEnterCriticalSection -# @ stub EnterSynchronizationBarrier +@ stdcall EnterSynchronizationBarrier(ptr long) @ stdcall EnumCalendarInfoExEx(ptr wstr long wstr long long) @ stdcall EnumCalendarInfoExW(ptr long long long) @ stdcall EnumCalendarInfoW(ptr long long long) @@ -851,7 +851,7 @@ @ stdcall InitializeSRWLock(ptr) ntdll.RtlInitializeSRWLock @ stdcall InitializeSecurityDescriptor(ptr long) @ stdcall InitializeSid(ptr ptr long) -# @ stub InitializeSynchronizationBarrier +@ stdcall InitializeSynchronizationBarrier(ptr long long) # @ stub InstallELAMCertificateInfo @ stdcall -arch=i386 InterlockedCompareExchange(ptr long long) @ stdcall -arch=i386 -ret64 InterlockedCompareExchange64(ptr int64 int64) ntdll.RtlInterlockedCompareExchange64 diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c index 36e29a3cab0..99fa0516013 100644 --- a/dlls/kernelbase/sync.c +++ b/dlls/kernelbase/sync.c @@ -1824,3 +1824,38 @@ __ASM_STDCALL_FUNC(InterlockedDecrement, 4, "ret $4")
#endif /* __i386__ */ + + +/*********************************************************************** + * Synchronization barrier functions + ***********************************************************************/ + + +/*********************************************************************** + * InitializeSynchronizationBarrier (kernelbase.@) + */ +BOOL WINAPI InitializeSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier, LONG thread_count, LONG spin_count ) +{ + FIXME( "%p %ld %ld stub.\n", barrier, thread_count, spin_count ); + return TRUE; +} + + +/*********************************************************************** + * DeleteSynchronizationBarrier (kernelbase.@) + */ +BOOL WINAPI DeleteSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier ) +{ + FIXME( "%p stub.\n", barrier ); + return TRUE; +} + + +/*********************************************************************** + * EnterSynchronizationBarrier (kernelbase.@) + */ +BOOL WINAPI EnterSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier, DWORD flags ) +{ + FIXME( "%p %#lx stub.\n", barrier, flags ); + return TRUE; +} diff --git a/include/synchapi.h b/include/synchapi.h index 0b3dcb8ff74..aabb44c5a53 100644 --- a/include/synchapi.h +++ b/include/synchapi.h @@ -27,6 +27,18 @@ BOOL WINAPI WaitOnAddress(volatile void*, void*, SIZE_T, DWORD); void WINAPI WakeByAddressAll(void*); void WINAPI WakeByAddressSingle(void*);
+typedef RTL_BARRIER SYNCHRONIZATION_BARRIER; +typedef PRTL_BARRIER PSYNCHRONIZATION_BARRIER; +typedef PRTL_BARRIER LPSYNCHRONIZATION_BARRIER; + +#define SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY 0x1 +#define SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY 0x2 +#define SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE 0x4 + +BOOL WINAPI InitializeSynchronizationBarrier(SYNCHRONIZATION_BARRIER *,LONG, LONG); +BOOL WINAPI DeleteSynchronizationBarrier(SYNCHRONIZATION_BARRIER *); +BOOL WINAPI EnterSynchronizationBarrier(SYNCHRONIZATION_BARRIER*, DWORD); + #ifdef __cplusplus } #endif diff --git a/include/winnt.h b/include/winnt.h index d360f5d0178..e112740e0e1 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -6237,6 +6237,14 @@ NTSYSAPI DWORD WINAPI RtlRunOnceBeginInitialize(PRTL_RUN_ONCE, DWORD, PVOID*); NTSYSAPI DWORD WINAPI RtlRunOnceComplete(PRTL_RUN_ONCE, DWORD, PVOID); NTSYSAPI WORD WINAPI RtlCaptureStackBackTrace(DWORD,DWORD,void**,DWORD*);
+typedef struct _RTL_BARRIER { + DWORD Reserved1; + DWORD Reserved2; + ULONG_PTR Reserved3[2]; + DWORD Reserved4; + DWORD Reserved5; +} RTL_BARRIER, *PRTL_BARRIER; + #pragma pack(push,8) typedef struct _IO_COUNTERS { ULONGLONG ReadOperationCount;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/kernel32.spec | 2 ++ dlls/ntdll/ntdll.spec | 3 +++ dlls/ntdll/sync.c | 31 +++++++++++++++++++++++++++++++ dlls/ntdll/tests/sync.c | 32 ++++++++++++++++++++++++++++++++ include/winternl.h | 3 +++ 5 files changed, 71 insertions(+)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 31ed292a6c6..f5c8247636a 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -425,6 +425,7 @@ @ stdcall -import EnumResourceTypesExA(long ptr long long long) @ stdcall -import EnumResourceTypesExW(long ptr long long long) @ stdcall EnumResourceTypesW(long ptr long) +@ stdcall -import EnterSynchronizationBarrier(ptr long) @ stdcall EnumSystemCodePagesA(ptr long) @ stdcall -import EnumSystemCodePagesW(ptr long) @ stdcall -import EnumSystemFirmwareTables(long ptr long) @@ -978,6 +979,7 @@ @ stdcall -import InitializeCriticalSectionAndSpinCount(ptr long) @ stdcall -import InitializeCriticalSectionEx(ptr long long) @ stdcall -import InitializeProcThreadAttributeList(ptr long long ptr) +@ stdcall -import InitializeSynchronizationBarrier(ptr long long) @ stdcall InitializeSListHead(ptr) NTDLL.RtlInitializeSListHead @ stdcall InitializeSRWLock(ptr) NTDLL.RtlInitializeSRWLock @ stdcall -arch=i386 InterlockedCompareExchange (ptr long long) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index ac4dff7ff40..7ff4f75f55d 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -525,6 +525,7 @@ @ stdcall RtlAreBitsSet(ptr long long) # @ stub RtlAssert2 @ stdcall RtlAssert(ptr ptr long str) +@ stdcall RtlBarrier(ptr long) # @ stub RtlCancelTimer @ stdcall -norelay RtlCaptureContext(ptr) @ stdcall RtlCaptureStackBackTrace(long long ptr ptr) @@ -611,6 +612,7 @@ @ stdcall RtlDelete(ptr) @ stdcall RtlDeleteAce(ptr long) @ stdcall RtlDeleteAtomFromAtomTable(ptr long) +@ stdcall RtlDeleteBarrier(ptr) @ stdcall RtlDeleteCriticalSection(ptr) @ stdcall -arch=!i386 RtlDeleteGrowableFunctionTable(ptr) @ stdcall RtlDeleteElementGenericTable(ptr ptr) @@ -792,6 +794,7 @@ @ stdcall RtlImpersonateSelf(long) @ stdcall RtlInitAnsiString(ptr str) @ stdcall RtlInitAnsiStringEx(ptr str) +@ stdcall RtlInitBarrier(ptr long long) @ stdcall RtlInitCodePageTable(ptr ptr) # @ stub RtlInitMemoryStream @ stdcall RtlInitNlsTables(ptr ptr ptr ptr) diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 1bd62612efe..f9deaf5ce3d 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -1374,3 +1374,34 @@ void WINAPI RtlDumpResource(LPRTL_RWLOCK rwl) rwl, rwl->iNumberActive, rwl->uSharedWaiters, rwl->uExclusiveWaiters, rwl->hOwningThreadId ); ERR( "\n" ); } + + +/*********************************************************************** + * RtlInitBarrier (NTDLL.@) + */ +NTSTATUS WINAPI RtlInitBarrier( RTL_BARRIER *barrier, LONG thread_count, LONG spin_count ) +{ + FIXME( "barrier %p, thread_count %ld, spin_count %ld stub.\n", barrier, thread_count, spin_count ); + + return STATUS_SUCCESS; +} + + +/*********************************************************************** + * RtlDeleteBarrier (NTDLL.@) + */ +void WINAPI RtlDeleteBarrier( RTL_BARRIER *barrier ) +{ + FIXME( "barrier %p stub.\n", barrier ); +} + + +/*********************************************************************** + * RtlBarrier (NTDLL.@) + */ +BOOLEAN WINAPI RtlBarrier( RTL_BARRIER *barrier, ULONG flags ) +{ + FIXME( "barrier %p, flags %#lx stub.\n", barrier, flags ); + + return TRUE; +} diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c index a443c581bca..22aaed5405f 100644 --- a/dlls/ntdll/tests/sync.c +++ b/dlls/ntdll/tests/sync.c @@ -58,6 +58,10 @@ static NTSTATUS (WINAPI *pRtlWaitOnAddress)( const void *, const void *, SIZE_T, static void (WINAPI *pRtlWakeAddressAll)( const void * ); static void (WINAPI *pRtlWakeAddressSingle)( const void * );
+static NTSTATUS (WINAPI *pRtlInitBarrier)(RTL_BARRIER*,LONG,LONG); +static void (WINAPI *pRtlDeleteBarrier)(RTL_BARRIER*); +static BOOLEAN (WINAPI *pRtlBarrier)(RTL_BARRIER*,ULONG); + #define KEYEDEVENT_WAIT 0x0001 #define KEYEDEVENT_WAKE 0x0002 #define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003) @@ -1134,6 +1138,30 @@ static void test_delayexecution(void) } }
+static void test_barrier(void) +{ + RTL_BARRIER barrier; + NTSTATUS status; + BOOLEAN bval; + + if (!pRtlInitBarrier) + { + win_skip("RtlInitBarrier is not available.\n"); + return; + } + + status = pRtlInitBarrier( &barrier, 1, -1 ); + ok( !status, "got %#lx.\n", status ); + + bval = pRtlBarrier( &barrier, 0 ); + ok( bval == 1, "got %#x.\n", bval ); + + bval = pRtlBarrier( &barrier, 0 ); + ok( bval == 1, "got %#x.\n", bval ); + + pRtlDeleteBarrier( &barrier ); +} + START_TEST(sync) { HMODULE module = GetModuleHandleA("ntdll.dll"); @@ -1175,6 +1203,9 @@ START_TEST(sync) pRtlWaitOnAddress = (void *)GetProcAddress(module, "RtlWaitOnAddress"); pRtlWakeAddressAll = (void *)GetProcAddress(module, "RtlWakeAddressAll"); pRtlWakeAddressSingle = (void *)GetProcAddress(module, "RtlWakeAddressSingle"); + pRtlInitBarrier = (void *)GetProcAddress(module, "RtlInitBarrier"); + pRtlDeleteBarrier = (void *)GetProcAddress(module, "RtlDeleteBarrier"); + pRtlBarrier = (void *)GetProcAddress(module, "RtlBarrier");
test_wait_on_address(); test_event(); @@ -1185,4 +1216,5 @@ START_TEST(sync) test_tid_alert( argv ); test_completion_port_scheduling(); test_delayexecution(); + test_barrier(); } diff --git a/include/winternl.h b/include/winternl.h index 2c985465096..d94b9911c74 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4823,6 +4823,7 @@ NTSYSAPI BOOLEAN WINAPI RtlAreAllAccessesGranted(ACCESS_MASK,ACCESS_MASK); NTSYSAPI BOOLEAN WINAPI RtlAreAnyAccessesGranted(ACCESS_MASK,ACCESS_MASK); NTSYSAPI BOOLEAN WINAPI RtlAreBitsSet(PCRTL_BITMAP,ULONG,ULONG); NTSYSAPI BOOLEAN WINAPI RtlAreBitsClear(PCRTL_BITMAP,ULONG,ULONG); +NTSYSAPI BOOLEAN WINAPI RtlBarrier(RTL_BARRIER*,ULONG); NTSYSAPI NTSTATUS WINAPI RtlCharToInteger(PCSZ,ULONG,PULONG); NTSYSAPI NTSTATUS WINAPI RtlCheckRegistryKey(ULONG, PWSTR); NTSYSAPI void WINAPI RtlClearAllBits(PRTL_BITMAP); @@ -4868,6 +4869,7 @@ NTSYSAPI NTSTATUS WINAPI RtlDecompressFragment(USHORT,PUCHAR,ULONG,PUCHAR,ULONG NTSYSAPI NTSTATUS WINAPI RtlDefaultNpAcl(PACL*); NTSYSAPI NTSTATUS WINAPI RtlDeleteAce(PACL,DWORD); NTSYSAPI NTSTATUS WINAPI RtlDeleteAtomFromAtomTable(RTL_ATOM_TABLE,RTL_ATOM); +NTSYSAPI void WINAPI RtlDeleteBarrier(RTL_BARRIER*); NTSYSAPI NTSTATUS WINAPI RtlDeleteCriticalSection(RTL_CRITICAL_SECTION *); NTSYSAPI NTSTATUS WINAPI RtlDeleteRegistryValue(ULONG, PCWSTR, PCWSTR); NTSYSAPI void WINAPI RtlDeleteResource(LPRTL_RWLOCK); @@ -5000,6 +5002,7 @@ NTSYSAPI PVOID WINAPI RtlImageRvaToVa(const IMAGE_NT_HEADERS *,HMODULE,DWORD NTSYSAPI NTSTATUS WINAPI RtlImpersonateSelf(SECURITY_IMPERSONATION_LEVEL); NTSYSAPI void WINAPI RtlInitAnsiString(PANSI_STRING,PCSZ); NTSYSAPI NTSTATUS WINAPI RtlInitAnsiStringEx(PANSI_STRING,PCSZ); +NTSYSAPI NTSTATUS WINAPI RtlInitBarrier(RTL_BARRIER*,LONG,LONG); NTSYSAPI void WINAPI RtlInitCodePageTable(USHORT*,CPTABLEINFO*); NTSYSAPI void WINAPI RtlInitNlsTables(USHORT*,USHORT*,USHORT*,NLSTABLEINFO*); NTSYSAPI void WINAPI RtlInitString(PSTRING,PCSZ);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/sync.c | 85 ++++++++++++++++- dlls/ntdll/tests/sync.c | 198 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 279 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index f9deaf5ce3d..58d4bd9d708 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -1376,13 +1376,32 @@ void WINAPI RtlDumpResource(LPRTL_RWLOCK rwl) }
+struct barrier_impl +{ + LONG spin_count; + LONG total_thread_count; + volatile LONG reached_thread_count; + volatile LONG waiting_thread_count; + volatile LONG wait_barrier_complete; +}; + +C_ASSERT( sizeof(struct barrier_impl) <= sizeof(RTL_BARRIER) ); + /*********************************************************************** * RtlInitBarrier (NTDLL.@) */ NTSTATUS WINAPI RtlInitBarrier( RTL_BARRIER *barrier, LONG thread_count, LONG spin_count ) { - FIXME( "barrier %p, thread_count %ld, spin_count %ld stub.\n", barrier, thread_count, spin_count ); + struct barrier_impl *b = (struct barrier_impl *)barrier;
+ TRACE( "barrier %p, thread_count %ld, spin_count %ld.\n", barrier, thread_count, spin_count ); + + if (!barrier) return STATUS_INVALID_PARAMETER; + b->total_thread_count = thread_count; + b->spin_count = spin_count; + b->reached_thread_count = 0; + b->waiting_thread_count = 0; + b->wait_barrier_complete = 0; return STATUS_SUCCESS; }
@@ -1392,7 +1411,20 @@ NTSTATUS WINAPI RtlInitBarrier( RTL_BARRIER *barrier, LONG thread_count, LONG sp */ void WINAPI RtlDeleteBarrier( RTL_BARRIER *barrier ) { - FIXME( "barrier %p stub.\n", barrier ); + struct barrier_impl *b = (struct barrier_impl *)barrier; + LONG count; + + TRACE( "barrier %p.\n", barrier ); + + if (!barrier) return; + if (ReadAcquire( &b->reached_thread_count ) < b->total_thread_count && b->waiting_thread_count) + { + /* On Windows this case will make RtlDeleteBarrier and the threads joining after wait forever, + * unless the threads joining after will have SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE. */ + ERR( "called before the barrier wait is satisfied.\n" ); + } + while ((count = ReadAcquire( &b->waiting_thread_count ))) + RtlWaitOnAddress( (void *)&b->waiting_thread_count, &count, sizeof(b->waiting_thread_count), NULL ); }
@@ -1401,7 +1433,52 @@ void WINAPI RtlDeleteBarrier( RTL_BARRIER *barrier ) */ BOOLEAN WINAPI RtlBarrier( RTL_BARRIER *barrier, ULONG flags ) { - FIXME( "barrier %p, flags %#lx stub.\n", barrier, flags ); + static unsigned int once; + static const LONG zero; + + struct barrier_impl *b = (struct barrier_impl *)barrier; + unsigned int spin_count, count; + BOOL ret = FALSE; + + TRACE( "barrier %p, flags %#lx.\n", barrier, flags ); + + if (flags & ~0x10000 && !once++) FIXME( "Unknown flags %#lx.\n", flags ); + if (!barrier) return FALSE; + + if (ReadAcquire( &b->reached_thread_count ) >= b->total_thread_count) return TRUE;
- return TRUE; + /* Incrementing reached_thread_count may trigger RTL_BARRIER data desrtuction from another thread, + * so lock RtlDeleteBarrier with waiting_thread_count before that. */ + if (flags & 0x10000) InterlockedIncrement( &b->waiting_thread_count ); + if (InterlockedIncrement( &b->reached_thread_count ) == b->total_thread_count) + { + WriteRelease( &b->wait_barrier_complete, 1 ); + RtlWakeAddressAll( (const void *)&b->wait_barrier_complete ); + ret = TRUE; + goto done; + } + /* On Windows the long wait doesn't consume CPU with any spin count, so probably the spin count + * is limited or not used at all. */ + spin_count = min( 2000, (unsigned int)b->spin_count ); + count = 0; + while (ReadAcquire( &b->reached_thread_count ) < b->total_thread_count ) + { + if (count < spin_count) + { + ++count; + YieldProcessor(); + continue; + } + RtlWaitOnAddress( (void *)&b->wait_barrier_complete, &zero, sizeof(b->wait_barrier_complete), NULL ); + } + +done: + if (flags & 0x10000 && !InterlockedDecrement( &b->waiting_thread_count )) + { + /* Now RTL_BARRIER structure contents may become invalid. Signaling on its address should be fine, the worst + * (unlikely) case it will wake something unrelated on reused address but that should be a legitimate spurious + * wakeup case. */ + RtlWakeAddressAll( (const void *)&b->waiting_thread_count ); + } + return ret; } diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c index 22aaed5405f..f584dd9539d 100644 --- a/dlls/ntdll/tests/sync.c +++ b/dlls/ntdll/tests/sync.c @@ -24,6 +24,7 @@ #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" +#include "setjmp.h" #include "wine/test.h"
static NTSTATUS (WINAPI *pNtAlertThreadByThreadId)( HANDLE ); @@ -1138,11 +1139,75 @@ static void test_delayexecution(void) } }
+static jmp_buf call_exception_jmpbuf; + +static LONG WINAPI call_exception_handler( EXCEPTION_POINTERS *eptr ) +{ + EXCEPTION_RECORD *rec = eptr->ExceptionRecord; + + longjmp(call_exception_jmpbuf, rec->ExceptionCode); + return EXCEPTION_CONTINUE_SEARCH; +} + +struct test_barrier_thread_param +{ + RTL_BARRIER *barrier; + ULONG flags; + LONG thread_count; + BOOL wait_skipped; + volatile LONG *count; + volatile LONG *true_ret_count; +}; + +static DWORD WINAPI test_barrier_thread(void *param) +{ + struct test_barrier_thread_param *p = param; + + InterlockedIncrement( p->count ); + if (pRtlBarrier( p->barrier, p->flags )) + InterlockedIncrement( p->true_ret_count ); + if (!p->wait_skipped) + ok( *p->count == p->thread_count, "got %ld.\n", *p->count ); + return 0; +} + +static DWORD WINAPI test_barrier_delete_thread(void *param) +{ + struct test_barrier_thread_param *p = param; + + pRtlDeleteBarrier( p->barrier ); + if (p->flags & 0x10000) + { + ok( *p->count == p->thread_count, "got %ld.\n", *p->count ); + } + else + { + /* No wait was performed. */ + ok( *p->count <= p->thread_count, "got %ld.\n", *p->count ); + } + + return 0; +} + static void test_barrier(void) { + static const ULONG rtl_barrier_flags[] = + { + 0, + 0x10000, + ~0u, + 1, + }; + struct test_barrier_thread_param p, p2; + volatile LONG count, true_ret_count; + HANDLE threads[8], delete_thread; + void *vectored_handler; + unsigned int i, test; RTL_BARRIER barrier; NTSTATUS status; + int exc_code; BOOLEAN bval; + DWORD ret;
if (!pRtlInitBarrier) { @@ -1150,16 +1215,149 @@ static void test_barrier(void) return; }
+ + vectored_handler = AddVectoredExceptionHandler(TRUE, call_exception_handler); + status = 0; + if (!(exc_code = setjmp(call_exception_jmpbuf))) + status = pRtlInitBarrier( NULL, 1, -1 ); + if (exc_code == STATUS_ACCESS_VIOLATION) + { + /* Crashes before Win10 1607; the other behaviour details are also different. */ + win_skip( "Old synchronization barriers implementation, skipping tests.\n" ); + return; + } + ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + RemoveVectoredExceptionHandler(vectored_handler); + + status = pRtlInitBarrier( &barrier, -2, -1 ); + ok( !status, "got %#lx.\n", status ); + + status = pRtlInitBarrier( &barrier, INT_MAX, -1 ); + ok( !status, "got %#lx.\n", status ); + + status = pRtlInitBarrier( &barrier, 0, -1 ); + ok( !status, "got %#lx.\n", status ); + + status = pRtlInitBarrier( &barrier, 1, -2 ); + ok( !status, "got %#lx.\n", status ); + + status = pRtlInitBarrier( &barrier, 1, INT_MAX ); + ok( !status, "got %#lx.\n", status ); + status = pRtlInitBarrier( &barrier, 1, -1 ); ok( !status, "got %#lx.\n", status );
bval = pRtlBarrier( &barrier, 0 ); ok( bval == 1, "got %#x.\n", bval );
+ bval = pRtlBarrier( NULL, 0 ); + ok( !bval, "got %#x.\n", bval ); + bval = pRtlBarrier( &barrier, 0 ); ok( bval == 1, "got %#x.\n", bval );
+ pRtlDeleteBarrier( NULL ); + pRtlDeleteBarrier( &barrier ); + + bval = pRtlBarrier( &barrier, 0 ); + ok( bval == 1, "got %#x.\n", bval ); + + /* Previously completed barrier. */ + p.barrier = &barrier; + p.flags = 0; + p.thread_count = ARRAY_SIZE(threads) + 1; + p.count = &count; + p.true_ret_count = &true_ret_count; + p.wait_skipped = TRUE; + count = 0; + true_ret_count = 0; + for (i = 0; i < ARRAY_SIZE(threads); ++i) + threads[i] = CreateThread( NULL, 0, test_barrier_thread, &p, 0, NULL ); + InterlockedIncrement( p.count ); + if (pRtlBarrier( p.barrier, p.flags )) + InterlockedIncrement( p.true_ret_count ); + for (i = 0; i < ARRAY_SIZE(threads); ++i) + { + WaitForSingleObject( threads[i], INFINITE ); + CloseHandle( threads[i] ); + } + ok( true_ret_count == p.thread_count, "got %ld.\n", true_ret_count ); + + /* Normal case. */ + status = pRtlInitBarrier( &barrier, p.thread_count, -1 ); + ok( !status, "got %#lx.\n", status ); + /* RtlDeleteBarrier doesn't seem to do anything unless there are threads already waiting on the barrier with + * flag 0x10000 (and then it will wait for those to finish). */ + pRtlDeleteBarrier( &barrier ); + p.flags = 0x10000; + p.wait_skipped = FALSE; + count = 0; + true_ret_count = 0; + for (i = 0; i < ARRAY_SIZE(threads); ++i) + threads[i] = CreateThread( NULL, 0, test_barrier_thread, &p, 0, NULL ); + InterlockedIncrement( p.count ); + if (pRtlBarrier( p.barrier, p.flags )) + InterlockedIncrement( p.true_ret_count ); + /* RtlDeleteBarrier (with 0x10000 flag passed to RtlBarrier) will wait for the waiters to be actually woken + * before returning. Without calling RtlDeleteBarrier or setting 0x10000 flag here the test will randomly + * fire an exception or hang (because RtlInitBarrier will break the not yet woken waiters wake up. */ + pRtlDeleteBarrier( &barrier ); + pRtlInitBarrier( &barrier, p.thread_count, -1 ); + /* p.count is incremented before barrier wait, check at once. */ + ok( *p.count == p.thread_count, "got %ld.\n", *p.count ); + for (i = 0; i < ARRAY_SIZE(threads); ++i) + { + WaitForSingleObject( threads[i], INFINITE ); + CloseHandle( threads[i] ); + } + /* Only check after all the threads are finished and thus guaranteed to exit RtlBarrier() and increment true_ret_count. */ + ok( true_ret_count == 1, "got %ld.\n", true_ret_count ); + + /* Test wait in RtlDeleteBarrier(). */ + for (test = 0; test < ARRAY_SIZE(rtl_barrier_flags); ++test) + { + winetest_push_context( "flags %#lx", rtl_barrier_flags[test] ); + p.thread_count = ARRAY_SIZE(threads); + status = pRtlInitBarrier( &barrier, p.thread_count, -1 ); + ok( !status, "got %#lx.\n", status ); + true_ret_count = 0; + p.wait_skipped = FALSE; + count = 0; + true_ret_count = 0; + p.flags = rtl_barrier_flags[test]; + threads[0] = CreateThread( NULL, 0, test_barrier_thread, &p, 0, NULL ); + /* Now try to make sure the thread has entered barrier wait before spawning test_barrier_delete_thread. */ + while (!ReadAcquire( p.count )) + Sleep(1); + Sleep(16); + + delete_thread = CreateThread( NULL, 0, test_barrier_delete_thread, &p, 0, NULL ); + ret = WaitForSingleObject( delete_thread, 100 ); + if (p.flags & 0x10000) + ok( ret == WAIT_TIMEOUT, "got %#lx.\n", ret ); + else + ok( !ret, "got %#lx.\n", ret ); + + /* If barrier waiters joined with flag 0x10000 after RtlDeleteBarrier started the wait, all the barrier + * waiting threads and RtlDeleteBarrier will hang forever on Windows for some reason. So create the rest of + * the waiters without the flag. */ + p2 = p; + p2.flags = 0; + for (i = 1; i < ARRAY_SIZE(threads); ++i) + threads[i] = CreateThread( NULL, 0, test_barrier_thread, &p2, 0, NULL ); + + WaitForSingleObject( delete_thread, INFINITE ); + CloseHandle( delete_thread ); + for (i = 0; i < ARRAY_SIZE(threads); ++i) + { + WaitForSingleObject( threads[i], INFINITE ); + CloseHandle( threads[i] ); + } + ok( *p.count == p.thread_count, "got %ld.\n", *p.count ); + ok( true_ret_count == 1, "got %ld.\n", true_ret_count ); + winetest_pop_context(); + } }
START_TEST(sync)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/tests/sync.c | 250 +++++++++++++++++++++++++++++++++++-- dlls/kernelbase/sync.c | 22 +++- 2 files changed, 258 insertions(+), 14 deletions(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c index 9571567647a..466bbc8908b 100644 --- a/dlls/kernel32/tests/sync.c +++ b/dlls/kernel32/tests/sync.c @@ -2796,17 +2796,17 @@ static void test_apc_deadlock(void) CloseHandle(pi.hProcess); }
-static jmp_buf bad_cs_jmpbuf; +static jmp_buf call_exception_jmpbuf; +static DWORD call_exception_number_parameters; +static DWORD call_exception_flags;
-static LONG WINAPI bad_cs_handler( EXCEPTION_POINTERS *eptr ) +static LONG WINAPI call_exception_handler( EXCEPTION_POINTERS *eptr ) { EXCEPTION_RECORD *rec = eptr->ExceptionRecord;
- ok(!rec->NumberParameters, "got %lu.\n", rec->NumberParameters); - ok(rec->ExceptionFlags == EXCEPTION_NONCONTINUABLE - || rec->ExceptionFlags == (EXCEPTION_NONCONTINUABLE | EXCEPTION_SOFTWARE_ORIGINATE), - "got %#lx.\n", rec->ExceptionFlags); - longjmp(bad_cs_jmpbuf, rec->ExceptionCode); + call_exception_number_parameters = rec->NumberParameters; + call_exception_flags = rec->ExceptionFlags; + longjmp(call_exception_jmpbuf, rec->ExceptionCode); return EXCEPTION_CONTINUE_SEARCH; }
@@ -2886,11 +2886,16 @@ static void test_crit_section(void) cs.LockSemaphore = (HANDLE)0xdeadbeef;
cs.LockCount = 0; - vectored_handler = AddVectoredExceptionHandler(TRUE, bad_cs_handler); - if (!(exc_code = setjmp(bad_cs_jmpbuf))) + vectored_handler = AddVectoredExceptionHandler(TRUE, call_exception_handler); + if (!(exc_code = setjmp(call_exception_jmpbuf))) EnterCriticalSection(&cs); ok(cs.LockCount, "got %ld.\n", cs.LockCount); ok(exc_code == STATUS_INVALID_HANDLE, "got %#x.\n", exc_code); + ok(!call_exception_number_parameters, "got %lu.\n", call_exception_number_parameters); + ok(call_exception_flags == EXCEPTION_NONCONTINUABLE + || call_exception_flags == (EXCEPTION_NONCONTINUABLE | EXCEPTION_SOFTWARE_ORIGINATE), + "got %#lx.\n", call_exception_flags); + RemoveVectoredExceptionHandler(vectored_handler); cs.LockSemaphore = old; DeleteCriticalSection(&cs); @@ -2996,9 +3001,64 @@ static void test_QueueUserAPC(void) } }
+struct test_barrier_thread_param +{ + RTL_BARRIER *barrier; + ULONG flags; + LONG thread_count; + BOOL wait_skipped; + volatile LONG *count; + volatile LONG *true_ret_count; +}; + +static DWORD WINAPI test_barrier_thread(void *param) +{ + struct test_barrier_thread_param *p = param; + + InterlockedIncrement( p->count ); + if (pEnterSynchronizationBarrier( p->barrier, p->flags )) + InterlockedIncrement( p->true_ret_count ); + if (!p->wait_skipped) + ok( *p->count == p->thread_count, "got %ld.\n", *p->count ); + return 0; +} + +static DWORD WINAPI test_barrier_delete_thread(void *param) +{ + struct test_barrier_thread_param *p = param; + + pDeleteSynchronizationBarrier( p->barrier ); + if (!(p->flags & SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE)) + { + ok( *p->count == p->thread_count, "got %ld.\n", *p->count ); + } + else + { + /* No wait was performed. */ + ok( *p->count <= p->thread_count, "got %ld.\n", *p->count ); + } + + return 0; +} + static void test_barrier(void) { + static const DWORD test_flags[] = + { + SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE, + 0, + SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY, + }; + static const DWORD valid_flags = SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY | SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY + | SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE; + struct test_barrier_thread_param p, p2; + volatile LONG count, true_ret_count; + HANDLE threads[8], delete_thread; SYNCHRONIZATION_BARRIER barrier; + void *vectored_handler; + unsigned int i, test; + DWORD flags, ret; + int exc_code; BOOL bval;
if (!pInitializeSynchronizationBarrier) @@ -3012,6 +3072,53 @@ static void test_barrier(void) ok( bval == TRUE, "got %#x.\n", bval ); ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() );
+ vectored_handler = AddVectoredExceptionHandler(TRUE, call_exception_handler); + + SetLastError( 0xdeadbeef ); + bval = 0xdeadbeef; + if (!(exc_code = setjmp(call_exception_jmpbuf))) + bval = pInitializeSynchronizationBarrier( NULL, 1, -1 ); + if (exc_code == STATUS_ACCESS_VIOLATION) + { + /* Crashes before Win10 1607; the other behaviour details are also different. */ + RemoveVectoredExceptionHandler(vectored_handler); + win_skip( "Old synchronization barriers implementation, skipping tests.\n" ); + return; + } + ok( !bval && GetLastError() == ERROR_INVALID_PARAMETER, "got bval %d, error %lu.\n", bval, GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, -2, -1 ); + ok( !bval && GetLastError() == ERROR_INVALID_PARAMETER, "got bval %d, error %lu.\n", bval, GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, 1, -2 ); + ok( !bval && GetLastError() == ERROR_INVALID_PARAMETER, "got bval %d, error %lu.\n", bval, GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, 0, -1 ); + ok( !bval && GetLastError() == ERROR_INVALID_PARAMETER, "got bval %d, error %lu.\n", bval, GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, 1, INT_MAX ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, INT_MAX, 0 ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, 1, INT_MAX ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + bval = pInitializeSynchronizationBarrier( &barrier, 1, -1 ); + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + SetLastError( 0xdeadbeef ); bval = pEnterSynchronizationBarrier( &barrier, 0 ); ok( bval == TRUE, "got %#x.\n", bval ); @@ -3026,6 +3133,131 @@ static void test_barrier(void) bval = pDeleteSynchronizationBarrier( &barrier ); ok( bval == TRUE, "got %#x.\n", bval ); ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + + for (i = 0; i < 32; ++i) + { + flags = 1u << i; + + if (!(exc_code = setjmp(call_exception_jmpbuf))) + { + bval = pEnterSynchronizationBarrier( &barrier, flags ); + if (flags & ~valid_flags) + { + ok( 0, "expected exception.\n" ); + } + else + { + ok( bval == TRUE, "got %#x.\n", bval ); + ok( GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError() ); + } + } + if (flags & ~valid_flags) + ok(exc_code == STATUS_INVALID_PARAMETER, "got %#x.\n", exc_code); + else + ok(!exc_code, "got %#x.\n", exc_code); + } + bval = pEnterSynchronizationBarrier( &barrier, valid_flags ); + ok( bval == TRUE, "got %#x.\n", bval ); + RemoveVectoredExceptionHandler(vectored_handler); + + /* Previously completed barrier. */ + p.barrier = &barrier; + p.flags = 0; + p.thread_count = ARRAY_SIZE(threads) + 1; + p.count = &count; + p.true_ret_count = &true_ret_count; + p.wait_skipped = TRUE; + count = 0; + true_ret_count = 0; + for (i = 0; i < ARRAY_SIZE(threads); ++i) + threads[i] = CreateThread( NULL, 0, test_barrier_thread, &p, 0, NULL ); + InterlockedIncrement( p.count ); + if (pEnterSynchronizationBarrier( p.barrier, p.flags )) + InterlockedIncrement( p.true_ret_count ); + for (i = 0; i < ARRAY_SIZE(threads); ++i) + { + WaitForSingleObject( threads[i], INFINITE ); + CloseHandle( threads[i] ); + } + ok( true_ret_count == p.thread_count, "got %ld.\n", true_ret_count ); + + /* Normal case. */ + bval = pInitializeSynchronizationBarrier( &barrier, p.thread_count, INT_MAX ); + ok( bval == TRUE, "got %#x.\n", bval ); + /* DeleteSynchronizationBarrier doesn't seem to do anything unless there are threads already waiting on the barrier + * without SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE flag (and then it will wait for those to finish). */ + bval = pDeleteSynchronizationBarrier( &barrier ); + ok( bval == TRUE, "got %#x.\n", bval ); + p.flags = 0; + p.wait_skipped = FALSE; + count = 0; + true_ret_count = 0; + for (i = 0; i < ARRAY_SIZE(threads); ++i) + threads[i] = CreateThread( NULL, 0, test_barrier_thread, &p, 0, NULL ); + InterlockedIncrement( p.count ); + if (pEnterSynchronizationBarrier( p.barrier, p.flags )) + InterlockedIncrement( p.true_ret_count ); + /* DeleteSynchronizationBarrier (without SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE flag passed to EnterSynchronizationBarrier) + * will wait for the waiters to be actually woken before returning. Without calling DeleteSynchronizationBarrier or + * when setting SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE flag here the test will randomly fire an exception or hang + * (because InitializeSynchronizationBarrier will break the not yet woken waiters wake up. */ + pDeleteSynchronizationBarrier( &barrier ); + pInitializeSynchronizationBarrier( &barrier, p.thread_count, -1 ); + /* p.count is incremented before barrier wait, check at once. */ + ok( *p.count == p.thread_count, "got %ld.\n", *p.count ); + for (i = 0; i < ARRAY_SIZE(threads); ++i) + { + WaitForSingleObject( threads[i], INFINITE ); + CloseHandle( threads[i] ); + } + /* Only check after all the threads are finished and thus guaranteed to exit EnterSynchronizationBarrier() + * and increment true_ret_count. */ + ok( true_ret_count == 1, "got %ld.\n", true_ret_count ); + + /* Test wait in DeleteSynchronizationBarrier(). */ + for (test = 0; test < ARRAY_SIZE(test_flags); ++test) + { + winetest_push_context( "flags %#lx", test_flags[test] ); + p.thread_count = ARRAY_SIZE(threads); + bval = pInitializeSynchronizationBarrier( &barrier, p.thread_count, -1 ); + ok( bval == TRUE, "got %#x.\n", bval ); + true_ret_count = 0; + p.wait_skipped = FALSE; + count = 0; + true_ret_count = 0; + p.flags = test_flags[test]; + threads[0] = CreateThread( NULL, 0, test_barrier_thread, &p, 0, NULL ); + /* Now try to make sure the thread has entered barrier wait before spawning test_barrier_delete_thread. */ + while (!ReadAcquire( p.count )) + Sleep(1); + Sleep(16); + + delete_thread = CreateThread( NULL, 0, test_barrier_delete_thread, &p, 0, NULL ); + ret = WaitForSingleObject( delete_thread, 100 ); + if (!(p.flags & SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE)) + ok( ret == WAIT_TIMEOUT, "got %#lx.\n", ret ); + else + ok( !ret, "got %#lx.\n", ret ); + + /* If barrier waiters joined without SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE after RtlDeleteBarrier started the + * wait, all the barrier waiting threads and RtlDeleteBarrier will hang forever on Windows for some reason. + * So create the rest of the waiters with the flag. */ + p2 = p; + p2.flags = SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE; + for (i = 1; i < ARRAY_SIZE(threads); ++i) + threads[i] = CreateThread( NULL, 0, test_barrier_thread, &p2, 0, NULL ); + + WaitForSingleObject( delete_thread, INFINITE ); + CloseHandle( delete_thread ); + for (i = 0; i < ARRAY_SIZE(threads); ++i) + { + WaitForSingleObject( threads[i], INFINITE ); + CloseHandle( threads[i] ); + } + ok( *p.count == p.thread_count, "got %ld.\n", *p.count ); + ok( true_ret_count == 1, "got %ld.\n", true_ret_count ); + winetest_pop_context(); + } }
START_TEST(sync) diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c index 99fa0516013..40aec41e5c8 100644 --- a/dlls/kernelbase/sync.c +++ b/dlls/kernelbase/sync.c @@ -1836,8 +1836,12 @@ __ASM_STDCALL_FUNC(InterlockedDecrement, 4, */ BOOL WINAPI InitializeSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier, LONG thread_count, LONG spin_count ) { - FIXME( "%p %ld %ld stub.\n", barrier, thread_count, spin_count ); - return TRUE; + if (thread_count <= 0 || spin_count < -1) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return set_ntstatus( RtlInitBarrier( barrier, thread_count, spin_count )); }
@@ -1846,7 +1850,7 @@ BOOL WINAPI InitializeSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier, */ BOOL WINAPI DeleteSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier ) { - FIXME( "%p stub.\n", barrier ); + RtlDeleteBarrier( barrier ); return TRUE; }
@@ -1856,6 +1860,14 @@ BOOL WINAPI DeleteSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier ) */ BOOL WINAPI EnterSynchronizationBarrier( SYNCHRONIZATION_BARRIER *barrier, DWORD flags ) { - FIXME( "%p %#lx stub.\n", barrier, flags ); - return TRUE; + static const DWORD valid_flags = SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY | SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY + | SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE; + static unsigned int once; + + if (flags & ~valid_flags) RtlRaiseStatus( STATUS_INVALID_PARAMETER ); + + if (flags & ~SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE && !once++) + FIXME( "flags %#lx are not implemented.\n", flags ); + + return RtlBarrier( barrier, flags & SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE ? 0 : 0x10000 ); }
These are used by The Obsessive Shadow.
I didn't try to figure out RTL_BARRIER structure layout / usage on Windows, just used the fields needed for what I think is reasonable implementation matching the tests.
I'd like to put some notes on DeleteSynchronizationBarrier() logic (as it seems to me the name and the description in MS Docs is a bit misleading).
As stated in [1], "To release a synchronization barrier when it is no longer needed, call DeleteSynchronizationBarrier. It is safe to call this function immediately after calling EnterSynchronizationBarrier because that function ensures that all threads have finished using the barrier before it is released.". As far as my testing goes, DeleteSynchronizationBarrier() does not actually delete or release anything. The purpose of it is that it can perform a wait. Apparently the supposed usage of DeleteSynchronizationBarrier() in an app is calling it after EnterSynchronizationBarrier() has returned and before invalidating (freeing, popping stack or reusing, e. g., with new InitializeSynchronizationBarrier) the memory behind RTL_BARRIER. The problem is, when EnterSynchronizationBarrier has returned to a thread that guarantees that all the participating threads have reached the barrier. But it is not guaranteed that all of them were already woken and won't need the data from RTL_BARRIER any more. So there is no way to know when the structure can be discarded (by app) without some extra synchronization. DeleteSynchronizationBarrier() waits for all the threads which entered RtlBarrier() to be woken and be done with accessing RTL_BARRIER structure (unless all of those entered with SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE in which case DeleteSynchronizationBarrier doesn't seem to do anything).
1. https://learn.microsoft.com/en-us/windows/win32/sync/synchronization-barrier...