Finishing touches to user-mode synchronization primitives

This commit is contained in:
fincs 2018-12-15 21:18:40 +01:00 committed by fincs
parent 8c786c610e
commit beeeb057d2
7 changed files with 52 additions and 29 deletions

View File

@ -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"

View File

@ -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;

View File

@ -9,6 +9,7 @@
typedef struct UEvent UEvent;
/// User-mode event object.
struct UEvent {
Waitable waitable;
bool signal;

View File

@ -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.

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}