diff --git a/nx/include/switch/kernel/svc.h b/nx/include/switch/kernel/svc.h index 6e6d89fe..932d474b 100644 --- a/nx/include/switch/kernel/svc.h +++ b/nx/include/switch/kernel/svc.h @@ -362,6 +362,7 @@ Result svcResetSignal(Handle handle); * @brief Waits on one or more synchronization objects, optionally with a timeout. * @return Result code. * @note Syscall number 0x18. + * @note Please use \ref waitMultiHandle instead. That function handles sporadical interrupts caused by usermode synchronization primitives. */ Result svcWaitSynchronization(s32* index, const Handle* handles, s32 handleCount, u64 timeout); @@ -369,6 +370,7 @@ Result svcWaitSynchronization(s32* index, const Handle* handles, s32 handleCount * @brief Waits on a single synchronization object, optionally with a timeout. * @return Result code. * @note Wrapper for \ref svcWaitSynchronization. + * @note Please use \ref waitSingleHandle instead. That function handles sporadical interrupts caused by usermode synchronization primitives. */ static inline Result svcWaitSynchronizationSingle(Handle handle, u64 timeout) { s32 tmp; diff --git a/nx/include/switch/kernel/wait.h b/nx/include/switch/kernel/wait.h index ba3685a8..575f7eee 100644 --- a/nx/include/switch/kernel/wait.h +++ b/nx/include/switch/kernel/wait.h @@ -95,6 +95,9 @@ static inline Waiter waiterForThreadExit(Thread* t) { return waiterForHandle(t->handle); } +Result waitN(s32* idx_out, Waiter* objects, size_t num_objects, u64 timeout); +Result waitNHandle(s32* idx_out, Handle* handles, size_t num_handles, u64 timeout); + /** * @brief Waits for an arbitrary number of waiters. This is a macro that uses var-args. * @param[out] idx_out The index of the signalled waiter. @@ -102,6 +105,33 @@ static inline Waiter waiterForThreadExit(Thread* t) { * @note The number of waiters must be less than 64. This is a Horizon kernel limitation. */ #define waitMulti(idx_out, timeout, ...) \ - waitN((idx_out), (timeout), (Waiter[]) { __VA_ARGS__ }, sizeof((Waiter[]) { __VA_ARGS__ }) / sizeof(Waiter)) + waitN((idx_out), (Waiter[]) { __VA_ARGS__ }, sizeof((Waiter[]) { __VA_ARGS__ }) / sizeof(Waiter), (timeout)) -Result waitN(s32* idx_out, u64 timeout, Waiter* objects, size_t num_objects); +/** + * @brief Waits for an arbitrary number of handles. This is a macro that uses var-args. + * @param[out] idx_out The index of the signalled handle. + * @param[in] timeout Timeout (in nanoseconds). + * @note The number of handles must be less than 64. This is a Horizon kernel limitation. + */ +#define waitMultiHandle(idx_out, timeout, ...) \ + waitNHandle((idx_out), (Handle[]) { __VA_ARGS__ }, sizeof((Handle[]) { __VA_ARGS__ }) / sizeof(Handle), (timeout)) + +/** + * @brief Waits for a single waiter. + * @param[in] w The waiter to wait for. + * @param[in] timeout Timeout (in nanoseconds). + */ +static inline Result waitSingle(Waiter w, u64 timeout) { + s32 idx; + return waitMulti(&idx, timeout, w); +} + +/** + * @brief Waits for a single handle. + * @param[in] h The handle to wait for. + * @param[in] timeout Timeout (in nanoseconds). + */ +static inline Result waitSingleHandle(Handle h, u64 timeout) { + s32 idx; + return waitMultiHandle(&idx, timeout, h); +} diff --git a/nx/source/kernel/thread.c b/nx/source/kernel/thread.c index 74c1380a..f12eda8a 100644 --- a/nx/source/kernel/thread.c +++ b/nx/source/kernel/thread.c @@ -6,6 +6,7 @@ #include "kernel/svc.h" #include "kernel/virtmem.h" #include "kernel/thread.h" +#include "kernel/wait.h" #include "../internal.h" extern const u8 __tdata_lma[]; @@ -113,7 +114,7 @@ Result threadStart(Thread* t) { } Result threadWaitForExit(Thread* t) { - return svcWaitSynchronizationSingle(t->handle, -1); + return waitSingleHandle(t->handle, -1); } Result threadClose(Thread* t) { diff --git a/nx/source/kernel/wait.c b/nx/source/kernel/wait.c index 78298bc4..b3dc81c7 100644 --- a/nx/source/kernel/wait.c +++ b/nx/source/kernel/wait.c @@ -13,9 +13,11 @@ #define MAX_WAIT 0x40 #define KernelError_Timeout 0xEA01 -#define KernelError_Interrupt 0xEC01 +#define KernelError_Canceled 0xEC01 -static Result waitImpl(s32* idx_out, u64 timeout, Waiter* objects, size_t num_objects) +typedef Result (*WaitImplFunc)(s32* idx_out, void* objects, size_t num_objects, u64 timeout); + +static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 timeout) { if (num_objects > MAX_WAIT) return MAKERESULT(Module_Libnx, LibnxError_TooManyWaitables); @@ -121,7 +123,7 @@ static Result waitImpl(s32* idx_out, u64 timeout, Waiter* objects, size_t num_ob *idx_out = end_tick_idx; rc = 0; } - else if (rc == KernelError_Interrupt) + else if (rc == KernelError_Canceled) { // If no listener filled in its own index, we return the interrupt error back to caller. // This only happens if user for some reason manually does a svcCancelSynchronization. @@ -141,7 +143,7 @@ static Result waitImpl(s32* idx_out, u64 timeout, Waiter* objects, size_t num_ob break; case WaiterNodeType_Timer: - rc = KernelError_Interrupt; + rc = KernelError_Canceled; break; } } @@ -156,14 +158,14 @@ clean_up: return rc; } -Result waitN(s32* idx_out, u64 timeout, Waiter* objects, size_t num_objects) +static Result _waitLoop(WaitImplFunc wait, s32* idx_out, void* objects, size_t num_objects, u64 timeout) { while (1) { u64 cur_tick = armGetSystemTick(); - Result rc = waitImpl(idx_out, timeout, objects, num_objects); + Result rc = wait(idx_out, objects, num_objects, timeout); - if (rc == KernelError_Interrupt) + if (rc == KernelError_Canceled) { // On timer stop/start an interrupt is sent to listeners. // It means the timer state has changed, and we should restart the wait. @@ -185,3 +187,11 @@ Result waitN(s32* idx_out, u64 timeout, Waiter* objects, size_t num_objects) } } } + +Result waitN(s32* idx_out, Waiter* objects, size_t num_objects, u64 timeout) { + return _waitLoop((WaitImplFunc) &waitImpl, idx_out, (void*) objects, num_objects, timeout); +} + +Result waitNHandle(s32* idx_out, Handle* handles, size_t num_handles, u64 timeout) { + return _waitLoop((WaitImplFunc) &svcWaitSynchronization, idx_out, (void*) handles, num_handles, timeout); +}