From beeeb057d2f0ecd6e79a39dd4a00fdbd2aeca3d5 Mon Sep 17 00:00:00 2001 From: fincs Date: Sat, 15 Dec 2018 21:18:40 +0100 Subject: [PATCH] Finishing touches to user-mode synchronization primitives --- nx/include/switch.h | 6 ++--- nx/include/switch/kernel/svc.h | 4 +-- nx/include/switch/kernel/uevent.h | 1 + nx/include/switch/kernel/utimer.h | 9 ++++--- nx/include/switch/kernel/wait.h | 45 ++++++++++++++++++++++--------- nx/source/kernel/utimer.c | 2 +- nx/source/kernel/wait.c | 14 +++++----- 7 files changed, 52 insertions(+), 29 deletions(-) diff --git a/nx/include/switch.h b/nx/include/switch.h index 56cad927..eed6c5c5 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -21,10 +21,13 @@ extern "C" { #include "switch/arm/counter.h" #include "switch/kernel/svc.h" +#include "switch/kernel/wait.h" #include "switch/kernel/tmem.h" #include "switch/kernel/shmem.h" #include "switch/kernel/mutex.h" #include "switch/kernel/event.h" +#include "switch/kernel/uevent.h" +#include "switch/kernel/utimer.h" #include "switch/kernel/rwlock.h" #include "switch/kernel/condvar.h" #include "switch/kernel/thread.h" @@ -35,9 +38,6 @@ extern "C" { #include "switch/kernel/jit.h" #include "switch/kernel/ipc.h" #include "switch/kernel/barrier.h" -#include "switch/kernel/uevent.h" -#include "switch/kernel/utimer.h" -#include "switch/kernel/wait.h" #include "switch/services/sm.h" #include "switch/services/smm.h" diff --git a/nx/include/switch/kernel/svc.h b/nx/include/switch/kernel/svc.h index f3b0823f..f38d277c 100644 --- a/nx/include/switch/kernel/svc.h +++ b/nx/include/switch/kernel/svc.h @@ -366,7 +366,7 @@ Result svcResetSignal(Handle handle); * @return Result code. * @note Syscall number 0x18. * @note \p handleCount must not be greater than \ref MAX_WAIT_OBJECTS. This is a Horizon kernel limitation. - * @note Please use \ref waitMultiHandle instead. That function handles sporadical interrupts caused by usermode synchronization primitives. + * @note This is the raw syscall, which can be cancelled by \ref svcCancelSynchronization or other means. \ref waitHandles or \ref waitMultiHandle should normally be used instead. */ Result svcWaitSynchronization(s32* index, const Handle* handles, s32 handleCount, u64 timeout); @@ -374,7 +374,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. + * @note This is the raw syscall, which can be cancelled by \ref svcCancelSynchronization or other means. \ref waitSingleHandle should normally be used instead. */ static inline Result svcWaitSynchronizationSingle(Handle handle, u64 timeout) { s32 tmp; diff --git a/nx/include/switch/kernel/uevent.h b/nx/include/switch/kernel/uevent.h index cd2a9d66..3ef76fc6 100644 --- a/nx/include/switch/kernel/uevent.h +++ b/nx/include/switch/kernel/uevent.h @@ -9,6 +9,7 @@ typedef struct UEvent UEvent; +/// User-mode event object. struct UEvent { Waitable waitable; bool signal; diff --git a/nx/include/switch/kernel/utimer.h b/nx/include/switch/kernel/utimer.h index f3cbefc1..f9d3e77a 100644 --- a/nx/include/switch/kernel/utimer.h +++ b/nx/include/switch/kernel/utimer.h @@ -9,11 +9,13 @@ typedef struct UTimer UTimer; +/// Valid types for a user-mode timer. typedef enum { - TimerType_OneShot, - TimerType_Repeating, + TimerType_OneShot, ///< Timers of this kind fire once and then stop automatically. + TimerType_Repeating, ///< Timers of this kind fire periodically. } TimerType; +/// User-mode timer object. struct UTimer { Waitable waitable; TimerType type : 8; @@ -35,7 +37,8 @@ static inline Waiter waiterForUTimer(UTimer* t) * @brief Creates a user-mode timer. * @param[out] t UTimer object. * @param[in] interval Interval (in nanoseconds). - * @param[in] type Timer type (repeating or one-shot) + * @param[in] type Type of timer to create (see \ref TimerType). + * @note The timer is stopped when it is created. Use \ref utimerStart to start it. * @note It is safe to wait on this timer with several threads simultaneously. * @note If more than one thread is listening on it, at least one thread will get the signal. No other guarantees. * @note For a repeating timer: If the timer triggers twice before you wait on it, you will only get one signal. diff --git a/nx/include/switch/kernel/wait.h b/nx/include/switch/kernel/wait.h index 01c75dd2..a864f5a4 100644 --- a/nx/include/switch/kernel/wait.h +++ b/nx/include/switch/kernel/wait.h @@ -32,7 +32,7 @@ typedef enum { // User-facing API starts here. -/// Waiter structure. +/// Waiter structure, representing any generic waitable synchronization object; both kernel-mode and user-mode. typedef struct { WaiterType type; @@ -51,49 +51,68 @@ static inline Waiter waiterForHandle(Handle h) return wait_obj; } -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 generic waitable synchronization objects, optionally with a timeout. + * @param[out] idx_out Variable that will received the index of the signalled object. + * @param[in] objects Array containing \ref Waiter structures. + * @param[in] num_objects Number of objects in the array. + * @param[in] timeout Timeout (in nanoseconds). + * @return Result code. + * @note The number of objects must not be greater than \ref MAX_WAIT_OBJECTS. This is a Horizon kernel limitation. + */ +Result waitObjects(s32* idx_out, const Waiter* objects, s32 num_objects, u64 timeout); /** - * @brief Waits for an arbitrary number of waiters. This is a macro that uses var-args. + * @brief Waits for an arbitrary number of kernel synchronization objects, optionally with a timeout. This function replaces \ref svcWaitSynchronization. + * @param[out] idx_out Variable that will received the index of the signalled object. + * @param[in] handles Array containing handles. + * @param[in] num_handles Number of handles in the array. + * @param[in] timeout Timeout (in nanoseconds). + * @return Result code. + * @note The number of objects must not be greater than \ref MAX_WAIT_OBJECTS. This is a Horizon kernel limitation. + */ +Result waitHandles(s32* idx_out, const Handle* handles, s32 num_handles, u64 timeout); + +/** + * @brief Helper macro for \ref waitObjects that accepts \ref Waiter structures as variadic arguments instead of as an array. * @param[out] idx_out The index of the signalled waiter. * @param[in] timeout Timeout (in nanoseconds). * @note The number of objects must not be greater than \ref MAX_WAIT_OBJECTS. This is a Horizon kernel limitation. */ #define waitMulti(idx_out, timeout, ...) ({ \ Waiter __objects[] = { __VA_ARGS__ }; \ - waitN((idx_out), __objects, sizeof(__objects) / sizeof(Waiter), (timeout)); \ + waitObjects((idx_out), __objects, sizeof(__objects) / sizeof(Waiter), (timeout)); \ }) /** - * @brief Waits for an arbitrary number of handles. This is a macro that uses var-args. + * @brief Helper macro for \ref waitHandles that accepts handles as variadic arguments instead of as an array. * @param[out] idx_out The index of the signalled handle. * @param[in] timeout Timeout (in nanoseconds). * @note The number of objects must not be greater than \ref MAX_WAIT_OBJECTS. This is a Horizon kernel limitation. */ #define waitMultiHandle(idx_out, timeout, ...) ({ \ Handle __handles[] = { __VA_ARGS__ }; \ - waitNHandle((idx_out), __handles, sizeof(__handles) / sizeof(Handle), (timeout)); \ + waitHandles((idx_out), __handles, sizeof(__handles) / sizeof(Handle), (timeout)); \ }) /** - * @brief Waits for a single waiter. - * @param[in] w The waiter to wait for. + * @brief Waits on a single generic waitable synchronization object, optionally with a timeout. + * @param[in] w \ref Waiter structure. * @param[in] timeout Timeout (in nanoseconds). */ static inline Result waitSingle(Waiter w, u64 timeout) { s32 idx; - return waitMulti(&idx, timeout, w); + return waitObjects(&idx, &w, 1, timeout); } /** - * @brief Waits for a single handle. - * @param[in] h The handle to wait for. + * @brief Waits for a single kernel synchronization object, optionally with a timeout. + * @param[in] h \ref Handle of the object. * @param[in] timeout Timeout (in nanoseconds). */ static inline Result waitSingleHandle(Handle h, u64 timeout) { s32 idx; - return waitMultiHandle(&idx, timeout, h); + return waitHandles(&idx, &h, 1, timeout); } diff --git a/nx/source/kernel/utimer.c b/nx/source/kernel/utimer.c index a57f884d..2a1f1f6e 100644 --- a/nx/source/kernel/utimer.c +++ b/nx/source/kernel/utimer.c @@ -42,7 +42,7 @@ void utimerStop(UTimer* t) { mutexLock(&t->waitable.mutex); - if (!t->started) { + if (t->started) { t->started = false; t->next_tick = 0; _waitableSignalAllListeners(&t->waitable); diff --git a/nx/source/kernel/wait.c b/nx/source/kernel/wait.c index 46210d40..aade1f2c 100644 --- a/nx/source/kernel/wait.c +++ b/nx/source/kernel/wait.c @@ -8,9 +8,9 @@ #include "wait.h" #include "../internal.h" -typedef Result (*WaitImplFunc)(s32* idx_out, void* objects, size_t num_objects, u64 timeout); +typedef Result (*WaitImplFunc)(s32* idx_out, const void* objects, s32 num_objects, u64 timeout); -static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 timeout) +static Result _waitObjectsImpl(s32* idx_out, const Waiter* objects, u32 num_objects, u64 timeout) { if (num_objects > MAX_WAIT_OBJECTS) return KERNELRESULT(OutOfRange); // same error returned by kernel @@ -34,7 +34,7 @@ static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 ti end_tick = armNsToTicks(timeout); for (i = 0; i < num_objects; i ++) { - Waiter* obj = &objects[i]; + const Waiter* obj = &objects[i]; u64 next_tick; bool added; @@ -116,7 +116,7 @@ clean_up: return rc; } -static Result _waitLoop(s32* idx_out, void* objects, size_t num_objects, u64 timeout, WaitImplFunc waitfunc) +static Result _waitLoop(s32* idx_out, const void* objects, s32 num_objects, u64 timeout, WaitImplFunc waitfunc) { Result rc; bool has_timeout = timeout != UINT64_MAX; @@ -140,12 +140,12 @@ static Result _waitLoop(s32* idx_out, void* objects, size_t num_objects, u64 tim return rc; } -Result waitN(s32* idx_out, Waiter* objects, size_t num_objects, u64 timeout) +Result waitObjects(s32* idx_out, const Waiter* objects, s32 num_objects, u64 timeout) { - return _waitLoop(idx_out, objects, num_objects, timeout, (WaitImplFunc)waitImpl); + return _waitLoop(idx_out, objects, num_objects, timeout, (WaitImplFunc)_waitObjectsImpl); } -Result waitNHandle(s32* idx_out, Handle* handles, size_t num_handles, u64 timeout) +Result waitHandles(s32* idx_out, const Handle* handles, s32 num_handles, u64 timeout) { return _waitLoop(idx_out, handles, num_handles, timeout, (WaitImplFunc)svcWaitSynchronization); }