diff --git a/nx/include/switch/kernel/uevent.h b/nx/include/switch/kernel/uevent.h index 7509db36..623a1b11 100644 --- a/nx/include/switch/kernel/uevent.h +++ b/nx/include/switch/kernel/uevent.h @@ -1,6 +1,5 @@ // Copyright 2018 plutoo #pragma once -#include "../kernel/mutex.h" #include "../kernel/wait.h" typedef struct UsermodeEvent UsermodeEvent; @@ -12,6 +11,21 @@ struct UsermodeEvent bool auto_clear; }; +/** + * @brief Creates a usermode event. + * @param[out] e UsermodeEvent object. + * @param[in] bool auto_clear Whether to automatically clear the event. + * @note It is safe to wait on this event 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. + */ void ueventCreate(UsermodeEvent* e, bool auto_clear); +/** + * @brief Clears the event signal. + * @param[in] e UsermodeEvent object. + */ void ueventClear(UsermodeEvent* e); +/** + * @brief Signals the event. + * @param[in] e UsermodeEvent object. + */ void ueventSignal(UsermodeEvent* e); diff --git a/nx/include/switch/kernel/utimer.h b/nx/include/switch/kernel/utimer.h index 0a320188..b8d69283 100644 --- a/nx/include/switch/kernel/utimer.h +++ b/nx/include/switch/kernel/utimer.h @@ -1,6 +1,5 @@ // Copyright 2018 plutoo #pragma once -#include "../kernel/mutex.h" #include "../kernel/wait.h" typedef struct UsermodeTimer UsermodeTimer; @@ -12,6 +11,23 @@ struct UsermodeTimer u64 interval; }; +/** + * @brief Creates a usermode timer. + * @param[out] t UsermodeTimer object. + * @param[in] interval Interval (in nanoseconds). + * @param[in] start Whether to start the timer right away. + * @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 If the timer triggers twice before you wait on it, you will only get one signal. + */ void utimerCreate(UsermodeTimer* t, u64 interval, bool start); +/** + * @brief Starts the timer. + * @param[in] t UsermodeTimer object. + */ void utimerStart(UsermodeTimer* t); +/** + * @brief Stops the timer. + * @param[in] t UsermodeTimer object. + */ void utimerStop(UsermodeTimer* t); diff --git a/nx/include/switch/kernel/wait.h b/nx/include/switch/kernel/wait.h index d98017f8..ba3685a8 100644 --- a/nx/include/switch/kernel/wait.h +++ b/nx/include/switch/kernel/wait.h @@ -58,6 +58,7 @@ typedef struct { }; } Waiter; +/// Creates a waiter for a kernelmode handle. static inline Waiter waiterForHandle(Handle h) { Waiter wait_obj; @@ -66,6 +67,7 @@ static inline Waiter waiterForHandle(Handle h) return wait_obj; } +/// Creates a waiter for a usermode timer. static inline Waiter waiterForUtimer(UsermodeTimer* t) { Waiter wait_obj; @@ -74,6 +76,7 @@ static inline Waiter waiterForUtimer(UsermodeTimer* t) return wait_obj; } +/// Creates a waiter for a usermode event. static inline Waiter waiterForUevent(UsermodeEvent* e) { Waiter wait_obj; @@ -82,16 +85,23 @@ static inline Waiter waiterForUevent(UsermodeEvent* e) return wait_obj; } +/// Creates a waiter for a kernelmode event. static inline Waiter waiterForEvent(Event* e) { return waiterForHandle(e->revent); } +/// Creates a waiter for a thread exit. static inline Waiter waiterForThreadExit(Thread* t) { return waiterForHandle(t->handle); } +/** + * @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. + * @param[in] timeout Timeout (in nanoseconds). + * @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)) - Result waitN(s32* idx_out, u64 timeout, Waiter* objects, size_t num_objects); diff --git a/nx/source/kernel/wait.c b/nx/source/kernel/wait.c index d3bfe400..78298bc4 100644 --- a/nx/source/kernel/wait.c +++ b/nx/source/kernel/wait.c @@ -69,7 +69,7 @@ static Result waitImpl(s32* idx_out, u64 timeout, Waiter* objects, size_t num_ob } // Always add a listener on the timer, - // So that we can detect another thread were to stopping/starting it during our waiting. + // If the timer is started/stopped we want to detect that. _utimerAddListener( obj->timer, &waiters[num_waiters], num_waiters, &triggered_idx, own_thread_handle); @@ -111,8 +111,9 @@ static Result waitImpl(s32* idx_out, u64 timeout, Waiter* objects, size_t num_ob if (rc == KernelError_Timeout) { // If we hit the user-supplied timeout, we return the timeout error back to caller. - if (end_tick_idx == -1) + if (end_tick_idx == -1) { goto clean_up; + } // If not, it means a timer triggered the timeout. _utimerRecalculate(objects[end_tick_idx].timer, end_tick + cur_tick); @@ -134,6 +135,7 @@ static Result waitImpl(s32* idx_out, u64 timeout, Waiter* objects, size_t num_ob { case WaiterNodeType_Event: _ueventTryAutoClear(waiters[triggered_idx].parent_event); + *idx_out = triggered_idx; rc = 0; break; @@ -171,8 +173,9 @@ Result waitN(s32* idx_out, u64 timeout, Waiter* objects, size_t num_objects) { u64 time_spent = armTicksToNs(armGetSystemTick() - cur_tick); - if (time_spent >= timeout) + if (time_spent >= timeout) { return KernelError_Timeout; + } timeout -= time_spent; }