From: Paul Gofman <pgofman(a)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 ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9267