hwtests/source/tests/kernel/waitsynch.cpp

1141 lines
32 KiB
C++

// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <limits>
#include <string>
#include <cstring>
#include <stdio.h>
#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