diff --git a/nx/include/switch/kernel/wait.h b/nx/include/switch/kernel/wait.h index f1ef16a2..73cef748 100644 --- a/nx/include/switch/kernel/wait.h +++ b/nx/include/switch/kernel/wait.h @@ -13,11 +13,6 @@ typedef struct UEvent UEvent; typedef struct UTimer UTimer; -typedef enum { - WaiterNodeType_Event, - WaiterNodeType_Timer, -} WaiterNodeType; - typedef struct Waitable Waitable; typedef struct WaitableNode WaitableNode; @@ -26,19 +21,6 @@ struct WaitableNode { WaitableNode* next; }; -typedef struct { - WaitableNode node; - WaiterNodeType type; - Handle thread; - union { - Waitable* parent; - UEvent* parent_event; - UTimer* parent_timer; - }; - s32 idx; - s32* idx_out; -} WaiterNode; - struct Waitable { WaitableNode list; Mutex mutex; diff --git a/nx/source/kernel/uevent.c b/nx/source/kernel/uevent.c index 8f124b99..6bfcbca9 100644 --- a/nx/source/kernel/uevent.c +++ b/nx/source/kernel/uevent.c @@ -2,7 +2,6 @@ #include "kernel/svc.h" #include "kernel/mutex.h" #include "kernel/uevent.h" -#include "wait.h" #include "uevent.h" void ueventCreate(UEvent* e, bool auto_clear) @@ -28,28 +27,31 @@ void ueventSignal(UEvent* e) mutexUnlock(&e->waitable.mutex); } -void _ueventTryAutoClear(UEvent* e) +Result _ueventTryAutoClear(UEvent* e) { + Result rc = 0; mutexLock(&e->waitable.mutex); - if (e->auto_clear) - e->signal = false; + if (e->auto_clear) { + if (e->signal) + e->signal = 0; + else + rc = KERNELRESULT(Cancelled); + } mutexUnlock(&e->waitable.mutex); + return rc; } bool _ueventAddListener(UEvent* e, WaiterNode* w, s32 idx, s32* idx_out, Handle thread) { mutexLock(&e->waitable.mutex); - bool signalled = e->signal; + bool can_add = !e->signal; - if (signalled) { - if (e->auto_clear) - e->signal = false; - } else { - _waiterNodeCreate(w, WaiterNodeType_Event, &e->waitable, thread, idx, idx_out); - _waiterNodeAddToWaitable(w, &e->waitable); - } + if (can_add) + _waiterNodeAdd(w, &e->waitable, thread, idx, idx_out); + else if (e->auto_clear) + e->signal = false; mutexUnlock(&e->waitable.mutex); - return !signalled; + return can_add; } diff --git a/nx/source/kernel/uevent.h b/nx/source/kernel/uevent.h index b98e094d..e9c79b5b 100644 --- a/nx/source/kernel/uevent.h +++ b/nx/source/kernel/uevent.h @@ -1,6 +1,7 @@ // Copyright 2018 plutoo #pragma once #include "kernel/uevent.h" +#include "wait.h" -void _ueventTryAutoClear(UEvent* e); +Result _ueventTryAutoClear(UEvent* e); bool _ueventAddListener(UEvent* e, WaiterNode* w, s32 idx, s32* idx_out, Handle thread); diff --git a/nx/source/kernel/utimer.c b/nx/source/kernel/utimer.c index 6c7a158c..93b321f9 100644 --- a/nx/source/kernel/utimer.c +++ b/nx/source/kernel/utimer.c @@ -2,7 +2,6 @@ #include "kernel/svc.h" #include "kernel/utimer.h" #include "arm/counter.h" -#include "utimer.h" #include "wait.h" #define STOPPED 0 @@ -77,9 +76,7 @@ u64 _utimerGetNextTick(UTimer* t) void _utimerAddListener(UTimer* t, WaiterNode* w, s32 idx, s32* idx_out, Handle thread) { - _waiterNodeCreate(w, WaiterNodeType_Timer, &t->waitable, thread, idx, idx_out); - mutexLock(&t->waitable.mutex); - _waiterNodeAddToWaitable(w, &t->waitable); + _waiterNodeAdd(w, &t->waitable, thread, idx, idx_out); mutexUnlock(&t->waitable.mutex); } diff --git a/nx/source/kernel/utimer.h b/nx/source/kernel/utimer.h index 516c91c9..2606184b 100644 --- a/nx/source/kernel/utimer.h +++ b/nx/source/kernel/utimer.h @@ -1,6 +1,7 @@ // Copyright 2018 plutoo #pragma once #include "kernel/utimer.h" +#include "wait.h" void _utimerRecalculate(UTimer* t, u64 old_tick); u64 _utimerGetNextTick(UTimer* t); diff --git a/nx/source/kernel/wait.c b/nx/source/kernel/wait.c index bc5ac261..7944b1f9 100644 --- a/nx/source/kernel/wait.c +++ b/nx/source/kernel/wait.c @@ -114,7 +114,7 @@ static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 ti *idx_out = end_tick_idx; rc = 0; } else if (rc == KERNELRESULT(Cancelled)) { - // If no listener filled in its own index, we return the interrupt error back to caller. + // If no listener filled in its own index, we return the cancelled error back to caller. // This only happens if user for some reason manually does a svcCancelSynchronization. // Check just in case. if (triggered_idx == -1) @@ -122,15 +122,21 @@ static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 ti // An event was signalled, or a timer was updated. // So.. which is it? - switch (waiters[triggered_idx].type) { - case WaiterNodeType_Event: - _ueventTryAutoClear(waiters[triggered_idx].parent_event); - - *idx_out = triggered_idx; - rc = 0; + switch (objects[triggered_idx].type) { + default: break; - case WaiterNodeType_Timer: + case WaiterType_UEvent: + // Try to auto-clear the event. If auto-clear is enabled but + // the event is not signalled, that means the state of the + // event has changed and thus we need to retry the wait. + rc = _ueventTryAutoClear(objects[triggered_idx].event); + if (R_SUCCEEDED(rc)) + *idx_out = triggered_idx; + break; + + case WaiterType_UTimer: + // Timer state changed, so we need to retry the wait. rc = KERNELRESULT(Cancelled); break; } @@ -140,7 +146,7 @@ clean_up: // Remove listeners. for (i = 0; i < num_objects; i ++) if (waiters_added & (1ULL << i)) - _waiterNodeFree(&waiters[i]); + _waiterNodeRemove(&waiters[i]); return rc; } diff --git a/nx/source/kernel/wait.h b/nx/source/kernel/wait.h index 6422f042..6cfc5176 100644 --- a/nx/source/kernel/wait.h +++ b/nx/source/kernel/wait.h @@ -3,6 +3,16 @@ #include "kernel/mutex.h" #include "kernel/wait.h" +typedef struct WaiterNode WaiterNode; + +struct WaiterNode { + WaitableNode node; + Waitable* parent; + Handle thread; + s32* idx_out; + s32 idx; +}; + static inline void _waitableInitialize(Waitable* ww) { mutexInit(&ww->mutex); @@ -30,25 +40,23 @@ static inline void _waitableSignalAllListeners(Waitable* ww) } } -static inline void _waiterNodeCreate( - WaiterNode* w, WaiterNodeType type, Waitable* parent, Handle thread, +static inline void _waiterNodeAdd( + WaiterNode* w, Waitable* parent, Handle thread, s32 idx, s32* idx_out) { - w->type = type; + // Initialize WaiterNode fields w->parent = parent; w->thread = thread; w->idx = idx; w->idx_out = idx_out; + + // Add WaiterNode to the parent's linked list + w->node.next = parent->list.next; + parent->list.next = &w->node; + w->node.prev = &parent->list; } -static inline void _waiterNodeAddToWaitable(WaiterNode* w, Waitable* ww) -{ - w->node.next = ww->list.next; - ww->list.next = &w->node; - w->node.prev = &ww->list; -} - -static inline void _waiterNodeFree(WaiterNode* w) +static inline void _waiterNodeRemove(WaiterNode* w) { mutexLock(&w->parent->mutex); w->node.prev->next = w->node.next;