From 2d41fc495e8886b128f09fd98d6a7506d310f090 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 11 Feb 2015 23:00:06 -0500 Subject: [PATCH] Added a bunch of tests for WaitSynchN and WaitSynch1. --- source/tests/kernel/kernel.cpp | 2 + source/tests/kernel/waitsynch.cpp | 1140 +++++++++++++++++++++++++++++ 2 files changed, 1142 insertions(+) create mode 100644 source/tests/kernel/waitsynch.cpp diff --git a/source/tests/kernel/kernel.cpp b/source/tests/kernel/kernel.cpp index 09e9f78..7976ead 100644 --- a/source/tests/kernel/kernel.cpp +++ b/source/tests/kernel/kernel.cpp @@ -3,9 +3,11 @@ namespace Kernel { namespace Ports { void TestAll(); } +namespace WaitSynch { void TestAll(); } void TestAll() { Ports::TestAll(); + WaitSynch::TestAll(); } } // namespace diff --git a/source/tests/kernel/waitsynch.cpp b/source/tests/kernel/waitsynch.cpp new file mode 100644 index 0000000..7256e41 --- /dev/null +++ b/source/tests/kernel/waitsynch.cpp @@ -0,0 +1,1140 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include <3ds.h> + +#include "output.h" +#include "common/string_funcs.h" +#include "common/scope_exit.h" +#include "tests/test.h" + +namespace Kernel { +namespace WaitSynch { + +const Result ERR_TIMEOUT = 0x09401BFE; +const Result ERR_INVALID_HANDLE = 0xD8E007F7; +const Result ERR_ARG_INVALID_RANGE = 0xE0E01BFD; +const Result ERR_ARG_INVALID_POINTER = 0xD8E007F6; + +static Handle events[4]; +static Handle threads[13]; +static Handle mutexes[4]; +static Handle semaphore; + +static u32 thread0_stack[0x400]; +static u32 thread1_stack[0x400]; +static u32 thread2_stack[0x400]; +static u32 thread3_stack[0x400]; +static u32 thread4_stack[0x400]; +static u32 thread5_stack[0x400]; +static u32 thread6_stack[0x400]; +static u32 thread7_stack[0x400]; +static u32 thread8_stack[0x400]; +static u32 thread9_stack[0x400]; +static u32 thread10_stack[0x400]; +static u32 thread11_stack[0x400]; +static u32 thread12_stack[0x400]; + +void thread0_handler(void*) { + svcSignalEvent(events[0]); + svcExitThread(); +} + +void thread1_handler(void*) { + svcSignalEvent(events[1]); + svcExitThread(); +} + +void thread2_handler(void*) { + svcSignalEvent(events[2]); + svcExitThread(); +} + +void thread3_handler(void*) { + svcSignalEvent(events[3]); + svcExitThread(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#define ONE_SECOND (1000 * 1000 * 1000) + +// Test WaitSynchN with wait_all=0, wait=0 + +bool Test_WaitSynchN_00() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + svcClearEvent(events[3]); + + svcSignalEvent(events[2]); + res = svcWaitSynchronizationN(&output, events, 4, 0, U64_MAX); + SoftAssert(output == 2); + SoftAssert(res == 0); + + svcSignalEvent(events[0]); + res = svcWaitSynchronizationN(&output, events, 4, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with wait_all=0, wait=1 + +bool Test_WaitSynchN_01() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + svcClearEvent(events[3]); + + svcCreateThread(&threads[3], thread3_handler, 0x0, (u32*)(&thread3_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[3]); }); + + res = svcWaitSynchronizationN(&output, events, 4, 0, U64_MAX); + SoftAssert(output == 3); + SoftAssert(res == 0); + + svcClearEvent(events[3]); + + svcCreateThread(&threads[0], thread0_handler, 0x0, (u32*)(&thread0_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[0]); }); + + res = svcWaitSynchronizationN(&output, events, 4, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, &threads[3], 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, &threads[0], 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with wait_all=1, wait=0 + +bool Test_WaitSynchN_02() { + s32 output = -1; + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + svcClearEvent(events[3]); + + svcSignalEvent(events[0]); + svcSignalEvent(events[1]); + svcSignalEvent(events[2]); + svcSignalEvent(events[3]); + + // TODO: Figure out how output is set + res = svcWaitSynchronizationN(&output, events, 4, 1, U64_MAX); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with wait_all=1, wait=1 + +bool Test_WaitSynchN_03() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + svcClearEvent(events[3]); + + svcCreateThread(&threads[0], thread0_handler, 0x0, (u32*)(&thread0_stack[0x400]), 0x2F, 0xfffffffe); + svcCreateThread(&threads[1], thread1_handler, 0x0, (u32*)(&thread1_stack[0x400]), 0x30, 0xfffffffe); + svcCreateThread(&threads[2], thread2_handler, 0x0, (u32*)(&thread2_stack[0x400]), 0x31, 0xfffffffe); + svcCreateThread(&threads[3], thread3_handler, 0x0, (u32*)(&thread3_stack[0x400]), 0x32, 0xfffffffe); + + SCOPE_EXIT({ svcCloseHandle(threads[0]); }); + SCOPE_EXIT({ svcCloseHandle(threads[1]); }); + SCOPE_EXIT({ svcCloseHandle(threads[2]); }); + SCOPE_EXIT({ svcCloseHandle(threads[3]); }); + + res = svcWaitSynchronizationN(&output, threads, 4, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with wait_all=0, wait=1, timeout=1/2 sec + +bool Test_WaitSynchN_04() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + svcClearEvent(events[3]); + + res = svcWaitSynchronizationN(&output, events, 4, 0, ONE_SECOND / 2); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + return true; +} + +// Test WaitSynchN with wait_all=1, wait=1, timeout=1/2 sec + +bool Test_WaitSynchN_05() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + svcClearEvent(events[3]); + + res = svcWaitSynchronizationN(&output, events, 4, 1, ONE_SECOND / 2); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + return true; +} + +// Test WaitSynchN with invalid handle + +bool Test_WaitSynchN_06() { + s32 output = 0; + s32 res = 0; + + res = svcWaitSynchronizationN(&output, &threads[4], 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == ERR_INVALID_HANDLE); + + return true; +} + +// Test WaitSynchN with duplicate handles (two events signaled, no wait) + +bool Test_WaitSynchN_07() { + s32 output = 0; + s32 res = 0; + + Handle handles[] = {events[0], events[1], events[1], events[0], events[1], events[2]}; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + svcClearEvent(events[3]); + + svcSignalEvent(events[1]); + svcSignalEvent(events[2]); + + res = svcWaitSynchronizationN(&output, handles, 6, 0, U64_MAX); + SoftAssert(output == 1); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with duplicate handles (one event signaled, no wait) + +bool Test_WaitSynchN_08() { + s32 output = 0; + s32 res = 0; + Handle handles[] = {events[0], events[1], events[1], events[0], events[1]}; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, handles, 5, 0, U64_MAX); + SoftAssert(output == 1); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with duplicate handles (one event signaled, with a wait) + +bool Test_WaitSynchN_09() { + s32 output = 0; + s32 res = 0; + Handle handles[] = {events[0], events[1], events[1], events[0], events[1]}; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcCreateThread(&threads[1], thread1_handler, 0x0, (u32*)(&thread1_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[1]); }); + + res = svcWaitSynchronizationN(&output, handles, 5, 0, U64_MAX); + SoftAssert(output == 4); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, &threads[1], 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with svcDuplicateHandle (one event signaled, with wait) + +bool Test_WaitSynchN_10() { + s32 output = 0; + s32 res = 0; + Handle duplicate0, duplicate1; + + svcDuplicateHandle(&duplicate0, events[1]); + svcDuplicateHandle(&duplicate1, events[1]); + + SCOPE_EXIT({ svcCloseHandle(duplicate0); }); + SCOPE_EXIT({ svcCloseHandle(duplicate1); }); + + Handle handles[] = {events[0], duplicate0, events[1], events[0], duplicate1}; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcCreateThread(&threads[1], thread1_handler, 0x0, (u32*)(&thread1_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[1]); }); + + res = svcWaitSynchronizationN(&output, handles, 5, 0, U64_MAX); + SoftAssert(output == 4); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, &threads[1], 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with svcDuplicateHandle (one event signaled, with wait) + +bool Test_WaitSynchN_11() { + s32 output = 0; + s32 res = 0; + Handle duplicate0, duplicate1; + + svcDuplicateHandle(&duplicate0, events[1]); + svcDuplicateHandle(&duplicate1, events[1]); + + SCOPE_EXIT({ svcCloseHandle(duplicate0); }); + SCOPE_EXIT({ svcCloseHandle(duplicate1); }); + + Handle handles[] = {events[0], duplicate0, events[1], events[0], duplicate1}; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, handles, 5, 0, U64_MAX); + SoftAssert(output == 1); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with zero handles, wait_all=false + +bool Test_WaitSynchN_12() { + s32 output = 0; + s32 res = 0; + + // Should hang, hence the timeout + res = svcWaitSynchronizationN(&output, events, 0, 0, ONE_SECOND / 2); + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + return true; +} + +// Test WaitSynchN with zero handles, wait_all=true + +bool Test_WaitSynchN_13() { + s32 output = 0; + s32 res = 0; + + // Should pass right through + res = svcWaitSynchronizationN(&output, events, 0, 1, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with invalid handle count + +bool Test_WaitSynchN_14() { + s32 output = 0; + s32 res = 0; + + res = svcWaitSynchronizationN(&output, events, -1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == ERR_ARG_INVALID_RANGE); + + return true; +} + +// Test WaitSynchN with wait_all=true; 2 events; signal one of them in another thread, clear that +// same event in that same thread, then signal the other one + +void thread4_handler(void*) +{ + svcSignalEvent(events[0]); + svcClearEvent(events[0]); + svcSignalEvent(events[1]); + svcExitThread(); +} + +bool Test_WaitSynchN_15() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcCreateThread(&threads[4], thread4_handler, 0x0, (u32*)(&thread4_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[4]); }); + + // Should timeout + res = svcWaitSynchronizationN(&output, events, 2, 1, ONE_SECOND / 2); + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + res = svcWaitSynchronizationN(&output, &threads[4], 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynchN with invalid handles pointer + +bool Test_WaitSynchN_16() { + s32 output = 0; + s32 res = 0; + + res = svcWaitSynchronizationN(&output, nullptr, 0, 0, ONE_SECOND / 2); + //SoftAssert(output == 0); // TODO: Figure out how output is set, same as Test_WaitSynchN_02 + SoftAssert(res == ERR_ARG_INVALID_POINTER); + + res = svcWaitSynchronizationN(&output, nullptr, 0, 1, ONE_SECOND / 2); + SoftAssert(output == 0); + SoftAssert(res == ERR_ARG_INVALID_POINTER); + + res = svcWaitSynchronizationN(&output, nullptr, 1, 0, ONE_SECOND / 2); + SoftAssert(output == 0); + SoftAssert(res == ERR_ARG_INVALID_POINTER); + + res = svcWaitSynchronizationN(&output, nullptr, 1, 1, ONE_SECOND / 2); + SoftAssert(output == 0); + SoftAssert(res == ERR_ARG_INVALID_POINTER); + + return true; +} + +// WaitSynchN wait_all=false; unlocked mutex0, unlocked mutex1; Make sure only mutex0 is locked and +// it does not wait + +void thread5_handler(void*) { + s32 output = 0; + svcWaitSynchronizationN(&output, mutexes, 2, 0, U64_MAX); + svcSignalEvent(events[0]); + svcWaitSynchronization(events[1], U64_MAX); + svcExitThread(); +} + +bool Test_WaitSynchN_17() { + s32 output = 0; + s32 res = 0; + + svcReleaseMutex(mutexes[0]); + svcReleaseMutex(mutexes[1]); + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcCreateThread(&threads[5], thread5_handler, 0x0, (u32*)(&thread5_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[5]); }); + + svcWaitSynchronization(events[0], U64_MAX); + + // Make sure we cant get mutex 0 + res = svcWaitSynchronizationN(&output, mutexes, 1, 0, ONE_SECOND / 2); + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + // Make sure we do get mutex 1 + res = svcWaitSynchronizationN(&output, mutexes, 2, 0, U64_MAX); + SoftAssert(output == 1); + SoftAssert(res == 0); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, &threads[5], 1, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +// WaitSynchN tests that the thread does not wait on a mutex if it already acquired it as well as +// the recursive nature of acquiring a mutex + +void thread5b_handler(void*) { + svcWaitSynchronization(mutexes[0], U64_MAX); + svcExitThread(); +} + +bool Test_WaitSynchN_18() { + s32 output = 0; + s32 res = 0; + + svcReleaseMutex(mutexes[0]); + svcReleaseMutex(mutexes[1]); + + res = svcWaitSynchronizationN(&output, mutexes, 2, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, mutexes, 2, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, mutexes, 2, 1, U64_MAX); + // TODO: Figure out how output is set + SoftAssert(res == 0); + + svcReleaseMutex(mutexes[0]); + svcReleaseMutex(mutexes[0]); + + svcReleaseMutex(mutexes[1]); + svcReleaseMutex(mutexes[1]); + + res = svcReleaseMutex(mutexes[0]); + SoftAssert(res == 0); + + svcCreateThread(&threads[5], thread5b_handler, 0x0, (u32*)(&thread5_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[5]); }); + + res = svcWaitSynchronization(mutexes[0], U64_MAX); + SoftAssert(res == 0); + + res = svcWaitSynchronization(mutexes[0], U64_MAX); + SoftAssert(res == 0); + + res = svcReleaseMutex(mutexes[0]); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, &threads[5], 1, 0, ONE_SECOND/2); // Wait for all threads to complete + SoftAssert(res == ERR_TIMEOUT); + + res = svcReleaseMutex(mutexes[0]); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, &threads[5], 1, 0, U64_MAX); // Wait for all threads to complete + SoftAssert(res == 0); + + return true; +} + +// WaitSynchN wait_all=true; unlocked mutex0, unlocked mutex1; Make sure both mutexes are locked and +// it does not wait + +void thread6_handler(void*) { + s32 output = 0; + + svcWaitSynchronizationN(&output, mutexes, 2, 1, U64_MAX); + + svcSignalEvent(events[0]); + + svcWaitSynchronization(events[1], U64_MAX); + + svcExitThread(); +} + +bool Test_WaitSynchN_19() { + s32 output = 0; + s32 res = 0; + + svcReleaseMutex(mutexes[0]); + svcReleaseMutex(mutexes[1]); + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcCreateThread(&threads[6], thread6_handler, 0x0, (u32*)(&thread6_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[6]); }); + + svcWaitSynchronization(events[0], U64_MAX); + + // Make sure we cant get either mutex + res = svcWaitSynchronizationN(&output, mutexes, 2, 1, ONE_SECOND / 2); + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, &threads[6], 1, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +// WaitSynchN wait_all=false; signaled event0, unlocked mutex0; Make sure mutex0 does not lock and +// it does not wait + +void thread7_handler(void*) { + s32 output = 0; + Handle handles[] = { events[2], mutexes[0] }; + + svcWaitSynchronizationN(&output, handles, 2, 0, U64_MAX); + svcSignalEvent(events[0]); + svcWaitSynchronization(events[1], U64_MAX); + + svcExitThread(); +} + +bool Test_WaitSynchN_20() { + s32 output = 0; + s32 res = 0; + Handle handles[] = { events[2], mutexes[0] }; + + svcReleaseMutex(mutexes[0]); + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcSignalEvent(events[2]); + + svcCreateThread(&threads[7], thread7_handler, 0x0, (u32*)(&thread7_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[7]); }); + + svcWaitSynchronization(events[0], U64_MAX); + svcClearEvent(events[2]); + + // Make sure we can still get the mutex + res = svcWaitSynchronizationN(&output, handles, 2, 0, U64_MAX); + SoftAssert(output == 1); + SoftAssert(res == 0); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, &threads[7], 1, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +void thread8_handler(void*) { + s32 output = 0; + Handle handles[] = { events[2], mutexes[0] }; + + svcWaitSynchronizationN(&output, handles, 2, 1, U64_MAX); + svcSignalEvent(events[0]); + svcWaitSynchronization(events[1], U64_MAX); + + svcExitThread(); +} + +// WaitSynchN wait_all=true; signaled event0, unlocked mutex0; Make sure mutex0 locks and it does +// not wait + +bool Test_WaitSynchN_21() { + s32 output = 0; + s32 res = 0; + Handle handles[] = { events[2], mutexes[0] }; + + svcReleaseMutex(mutexes[0]); + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcSignalEvent(events[2]); + + svcCreateThread(&threads[8], thread8_handler, 0x0, (u32*)(&thread8_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[8]); }); + + svcWaitSynchronization(events[0], U64_MAX); + svcClearEvent(events[2]); + + // Make sure we can't get the mutex + res = svcWaitSynchronizationN(&output, handles, 2, 0, ONE_SECOND/2); + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, &threads[8], 1, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +// WaitSynchN with wait_all=true, 2 objects, 1 ready, 1 not, set the first one not and the second +// one ready in a thread + +void thread9_handler(void*) { + svcSignalEvent(events[0]); + svcClearEvent(events[1]); + svcExitThread(); +} + +bool Test_WaitSynchN_22() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + svcSignalEvent(events[1]); + + svcCreateThread(&threads[9], thread9_handler, 0x0, (u32*)(&thread9_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[9]); }); + + // Make sure we don't timeout + res = svcWaitSynchronizationN(&output, events, 2, 1, ONE_SECOND/2); + SoftAssert(output == -1); + SoftAssert(res == 0); + + res = svcWaitSynchronizationN(&output, &threads[9], 1, 1, U64_MAX); // Wait for all threads to complete + + // NOTE: We won't pass this test correctly until we improve our scheduling. On real HW, the + // thread will have already finished, and this will be 0. On Citra, the thread finishes on this + // Wait, and the result is -1 + //SoftAssert(output == 0); + + SoftAssert(res == 0); + + return true; +} + +// Call WaitSynchN with a service handle + +bool Test_WaitSynchN_23() { + s32 output = 0; + s32 res = 0; + + Handle service = 0; + SoftAssert(srvGetServiceHandle(&service, "APT:U") == 0); + SoftAssert(service != 0); + SCOPE_EXIT({ svcCloseHandle(service); }); + + // Make sure we don't timeout + res = svcWaitSynchronizationN(&output, &service, 1, 0, ONE_SECOND/2); + SoftAssert(output == -1); + SoftAssert(res == ERR_TIMEOUT); + + return true; +} + +// WaitSynchN with an invalid handle passed in, nothing should wait + +bool Test_WaitSynchN_24() { + s32 output = 0; + s32 res = 0; + Handle handles[] = { events[0], 0xDEADBEEF }; + + svcSignalEvent(events[0]); + + res = svcWaitSynchronizationN(&output, handles, 2, 0, U64_MAX); + SoftAssert(res == ERR_INVALID_HANDLE); + + res = svcWaitSynchronizationN(&output, handles, 2, 1, ONE_SECOND / 2); + SoftAssert(res == ERR_INVALID_HANDLE); + + return true; +} + +// WaitSynchN with an invalid handle passed in, nothing should lock + +void thread10_handler(void*) { + s32 output = 0; + + Handle handles[] = { mutexes[0], 0xDEADBEEF }; + + svcWaitSynchronizationN(&output, handles, 2, 0, U64_MAX); + + svcSignalEvent(events[0]); + + svcWaitSynchronization(events[1], U64_MAX); + + svcExitThread(); +} + +bool Test_WaitSynchN_25() { + s32 output = 0; + s32 res = 0; + + svcReleaseMutex(mutexes[0]); + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcCreateThread(&threads[10], thread10_handler, 0x0, (u32*)(&thread10_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[10]); }); + + svcWaitSynchronization(events[0], U64_MAX); + + // Make sure we can still get mutex 0 due to the invalid handle + res = svcWaitSynchronizationN(&output, mutexes, 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, &threads[10], 1, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynch1 with a timeout and with wait=0 + +bool Test_WaitSynch1_00() { + s32 res = 0; + + svcClearEvent(events[0]); + + res = svcWaitSynchronization(events[0], ONE_SECOND / 2); + SoftAssert(res == ERR_TIMEOUT); + + svcSignalEvent(events[0]); + res = svcWaitSynchronization(events[0], U64_MAX); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynch1 with wait=1 + +bool Test_WaitSynch1_01() { + s32 output = 0; + s32 res = 0; + + svcClearEvent(events[0]); + + svcCreateThread(&threads[0], thread0_handler, 0x0, (u32*)(&thread0_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[0]); }); + + res = svcWaitSynchronization(events[0], U64_MAX); + SoftAssert(output == 0); + + res = svcWaitSynchronizationN(&output, &threads[0], 1, 0, U64_MAX); + SoftAssert(output == 0); + SoftAssert(res == 0); + + return true; +} + +// Test WaitSynch1 with an invalid handle + +bool Test_WaitSynch1_02() { + s32 res = svcWaitSynchronization(0xDEADBEEF, U64_MAX); + SoftAssert(res == ERR_INVALID_HANDLE); + return true; +} + +// Test WaitSynch1 with a service handle + +bool Test_WaitSynch1_03() { + s32 res = 0; + Handle service = 0; + + srvGetServiceHandle(&service, "soc:U"); + SCOPE_EXIT({ svcCloseHandle(service); }); + + res = svcWaitSynchronization(service, ONE_SECOND/2); + SoftAssert(res == ERR_TIMEOUT); + + return true; +} + +// WaitSynch1; unlocked mutex0, Make sure mutex0 locks + +void thread11_handler(void*) +{ + svcWaitSynchronization(mutexes[0], U64_MAX); + svcSignalEvent(events[0]); + svcWaitSynchronization(events[1], U64_MAX); + svcExitThread(); +} + +bool Test_WaitSynch1_04() { + s32 output = 0; + s32 res = 0; + + svcReleaseMutex(mutexes[0]); + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + + svcCreateThread(&threads[11], thread11_handler, 0x0, (u32*)(&thread11_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[11]); }); + + svcWaitSynchronization(events[0], U64_MAX); + + // Make sure we cant get mutex 0 + res = svcWaitSynchronization(mutexes[0], ONE_SECOND / 2); + SoftAssert(res == ERR_TIMEOUT); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, &threads[11], 1, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +// WaitSynch1; unlocked mutex0, Lock mutex0 + +bool Test_WaitSynch1_05() { + s32 res = 0; + + svcReleaseMutex(mutexes[0]); + + // Make sure we can get mutex 0 + res = svcWaitSynchronization(mutexes[0], U64_MAX); + SoftAssert(res == 0); + + return true; +} + +// WaitSynch1; wait then get mutex0 + +void thread12_handler(void*) +{ + svcWaitSynchronization(mutexes[0], U64_MAX); + svcSignalEvent(events[0]); + + svcSleepThread(ONE_SECOND/2); + svcReleaseMutex(mutexes[0]); + + svcWaitSynchronization(events[1], U64_MAX); + svcExitThread(); +} + +bool Test_WaitSynch1_06() { + s32 output = 0; + s32 res = 0; + + svcReleaseMutex(mutexes[0]); + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + + svcCreateThread(&threads[12], thread12_handler, 0x0, (u32*)(&thread12_stack[0x400]), 0x31, 0xfffffffe); + SCOPE_EXIT({ svcCloseHandle(threads[12]); }); + + svcWaitSynchronization(events[0], U64_MAX); + + // Make sure we cant get mutex 0 + res = svcWaitSynchronization(mutexes[0], U64_MAX); + SoftAssert(res == 0); + + svcSignalEvent(events[1]); + + res = svcWaitSynchronizationN(&output, &threads[12], 1, 1, U64_MAX); // Wait for all threads to complete + SoftAssert(output == -1); + SoftAssert(res == 0); + + return true; +} + +// WaitSynch1; semaphore test + +bool Test_WaitSynch1_07() { + s32 res = 0; + + svcClearEvent(events[0]); + svcClearEvent(events[1]); + svcClearEvent(events[2]); + + res = svcWaitSynchronization(semaphore, U64_MAX); + SoftAssert(res == 0); + + res = svcWaitSynchronization(semaphore, U64_MAX); + SoftAssert(res == 0); + + res = svcWaitSynchronization(semaphore, U64_MAX); + SoftAssert(res == 0); + + res = svcWaitSynchronization(semaphore, ONE_SECOND/2); + SoftAssert(res == ERR_TIMEOUT); + + return true; +} + +// Test WaitSynch with all Kernel Object types + +bool Test_WaitSynch_KernelType_SharedMem() { + s32 output; + s32 res; + Handle service = 0; + + // Get an example of a session handle + SoftAssert(srvGetServiceHandle(&service, "hid:USER") == 0); + SoftAssert(service != 0); + SCOPE_EXIT({ svcCloseHandle(service); }); + + // Get an example of a shared_mem handle + u32* cmdbuf=getThreadCommandBuffer(); + cmdbuf[0] = 0xa0000; + svcSendSyncRequest(service); + Handle shared_mem = cmdbuf[3]; + SoftAssert(shared_mem != 0); + + // Test WaitSynch with a shared mem handle + res = svcWaitSynchronization(shared_mem, ONE_SECOND/2); + SoftAssert(res == ERR_INVALID_HANDLE); + + res = svcWaitSynchronizationN(&output, &shared_mem, 1, 0, ONE_SECOND/2); + SoftAssert(res == ERR_INVALID_HANDLE); + + res = svcWaitSynchronizationN(&output, &shared_mem, 1, 1, ONE_SECOND/2); + SoftAssert(res == ERR_INVALID_HANDLE); + + return true; +} + +// Test WaitSynch with all Kernel Object types + +bool Test_WaitSynch_KernelType_Session() { + Handle service = 0; + + // Get an example of a session handle + SoftAssert(srvGetServiceHandle(&service, "hid:USER") == 0); + SoftAssert(service != 0); + SCOPE_EXIT({ svcCloseHandle(service); }); + + // TODO: These asserts aren't correct on a 3DS, figure out how WaitSynch works with a Session + // kernel object + + // s32 output[3]; + // s32 res[3]; + + // Test WaitSynch with a session handle + // output[0] = 0; + // res[0] = svcWaitSynchronization(service, ONE_SECOND/2); + // SoftAssert(res[0] == ERR_TIMEOUT); + + // res[1] = svcWaitSynchronizationN(&output[1], &service, 1, 0, ONE_SECOND/2); + // SoftAssert(output[1] == -1); + // SoftAssert(res[1] == ERR_TIMEOUT); + + // res[2] = svcWaitSynchronizationN(&output[2], &service, 1, 1, ONE_SECOND/2); + // SoftAssert(output[2] == -1); + // SoftAssert(res[2] == 0); // Wrong in citra + + s32 output = 0; + svcWaitSynchronizationN(&output, &service, 1, 1, ONE_SECOND / 2); + + return true; +} + +// Test WaitSynch with an AddressArbiter + +bool Test_WaitSynch_KernelType_AddressArbiter() { + s32 output; + s32 res; + Handle arb = 0; + + // TODO(bunnei): Replace with svcCreateAddressArbiter once libctru implements it + asm volatile ("svc 0x21\n" + "MOV %[out], r1" : [out] "=r"(arb)); + + SoftAssert(arb != 0); + SCOPE_EXIT({ svcCloseHandle(arb); }); + + res = svcWaitSynchronization(arb, ONE_SECOND/2); + SoftAssert(res == ERR_INVALID_HANDLE); + + // TODO(bunnei): Verify output + res = svcWaitSynchronizationN(&output, &arb, 1, 0, ONE_SECOND/2); + SoftAssert(res == ERR_INVALID_HANDLE); + res = svcWaitSynchronizationN(&output, &arb, 1, 1, ONE_SECOND/2); + SoftAssert(res == ERR_INVALID_HANDLE); + + return true; +} + +void TestAll() { + svcCreateEvent(&events[0], 1); + svcCreateEvent(&events[1], 1); + svcCreateEvent(&events[2], 1); + svcCreateEvent(&events[3], 1); + + svcCreateMutex(&mutexes[0], false); + svcCreateMutex(&mutexes[1], false); + + svcCreateSemaphore(&semaphore, 3, 10); + + Test("Test_WaitSynchN_00", "Test_WaitSynchN_00", Test_WaitSynchN_00(), true); + Test("Test_WaitSynchN_01", "Test_WaitSynchN_01", Test_WaitSynchN_01(), true); + Test("Test_WaitSynchN_02", "Test_WaitSynchN_02", Test_WaitSynchN_02(), true); + Test("Test_WaitSynchN_03", "Test_WaitSynchN_03", Test_WaitSynchN_03(), true); + Test("Test_WaitSynchN_04", "Test_WaitSynchN_04", Test_WaitSynchN_04(), true); + Test("Test_WaitSynchN_05", "Test_WaitSynchN_05", Test_WaitSynchN_05(), true); + Test("Test_WaitSynchN_06", "Test_WaitSynchN_06", Test_WaitSynchN_06(), true); + Test("Test_WaitSynchN_07", "Test_WaitSynchN_07", Test_WaitSynchN_07(), true); + Test("Test_WaitSynchN_08", "Test_WaitSynchN_08", Test_WaitSynchN_08(), true); + Test("Test_WaitSynchN_09", "Test_WaitSynchN_09", Test_WaitSynchN_09(), true); + Test("Test_WaitSynchN_10", "Test_WaitSynchN_10", Test_WaitSynchN_10(), true); + Test("Test_WaitSynchN_11", "Test_WaitSynchN_11", Test_WaitSynchN_11(), true); + Test("Test_WaitSynchN_12", "Test_WaitSynchN_12", Test_WaitSynchN_12(), true); + Test("Test_WaitSynchN_13", "Test_WaitSynchN_13", Test_WaitSynchN_13(), true); + Test("Test_WaitSynchN_14", "Test_WaitSynchN_14", Test_WaitSynchN_14(), true); + Test("Test_WaitSynchN_15", "Test_WaitSynchN_15", Test_WaitSynchN_15(), true); + Test("Test_WaitSynchN_16", "Test_WaitSynchN_16", Test_WaitSynchN_16(), true); + Test("Test_WaitSynchN_17", "Test_WaitSynchN_17", Test_WaitSynchN_17(), true); + Test("Test_WaitSynchN_18", "Test_WaitSynchN_18", Test_WaitSynchN_18(), true); + Test("Test_WaitSynchN_19", "Test_WaitSynchN_19", Test_WaitSynchN_19(), true); + Test("Test_WaitSynchN_20", "Test_WaitSynchN_20", Test_WaitSynchN_20(), true); + Test("Test_WaitSynchN_21", "Test_WaitSynchN_21", Test_WaitSynchN_21(), true); + Test("Test_WaitSynchN_22", "Test_WaitSynchN_22", Test_WaitSynchN_22(), true); + Test("Test_WaitSynchN_23", "Test_WaitSynchN_23", Test_WaitSynchN_23(), true); + Test("Test_WaitSynchN_24", "Test_WaitSynchN_24", Test_WaitSynchN_24(), true); + Test("Test_WaitSynchN_25", "Test_WaitSynchN_25", Test_WaitSynchN_25(), true); + Test("Test_WaitSynch1_00", "Test_WaitSynch1_00", Test_WaitSynch1_00(), true); + Test("Test_WaitSynch1_01", "Test_WaitSynch1_01", Test_WaitSynch1_01(), true); + Test("Test_WaitSynch1_02", "Test_WaitSynch1_02", Test_WaitSynch1_02(), true); + Test("Test_WaitSynch1_03", "Test_WaitSynch1_03", Test_WaitSynch1_03(), true); + Test("Test_WaitSynch1_04", "Test_WaitSynch1_04", Test_WaitSynch1_04(), true); + Test("Test_WaitSynch1_05", "Test_WaitSynch1_05", Test_WaitSynch1_05(), true); + Test("Test_WaitSynch1_06", "Test_WaitSynch1_06", Test_WaitSynch1_06(), true); + Test("Test_WaitSynch1_07", "Test_WaitSynch1_07", Test_WaitSynch1_07(), true); + Test("Test_WaitSynch_KernelType_Session", "Test_WaitSynch_KernelType_Session", Test_WaitSynch_KernelType_Session(), true); + Test("Test_WaitSynch_KernelType_SharedMem", "Test_WaitSynch_KernelType_SharedMem", Test_WaitSynch_KernelType_SharedMem(), true); + Test("Test_WaitSynch_KernelType_AddressArbiter", "Test_WaitSynch_KernelType_AddressArbiter", Test_WaitSynch_KernelType_AddressArbiter(), true); + + svcCloseHandle(events[0]); + svcCloseHandle(events[1]); + svcCloseHandle(events[2]); + svcCloseHandle(events[3]); + + svcCloseHandle(mutexes[0]); + svcCloseHandle(mutexes[1]); + + svcCloseHandle(semaphore); +} + +} // namespace +} // namespace