mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 12:32:40 +02:00
Generalize Waitable, moving UEvent/UTimer specific code into a vtable.
This commit is contained in:
parent
1d14cad1cf
commit
8d813ee666
@ -19,8 +19,8 @@ struct UEvent {
|
|||||||
static inline Waiter waiterForUEvent(UEvent* e)
|
static inline Waiter waiterForUEvent(UEvent* e)
|
||||||
{
|
{
|
||||||
Waiter wait_obj;
|
Waiter wait_obj;
|
||||||
wait_obj.type = WaiterType_UEvent;
|
wait_obj.type = WaiterType_Waitable;
|
||||||
wait_obj.event = e;
|
wait_obj.waitable = &e->waitable;
|
||||||
return wait_obj;
|
return wait_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ struct UTimer {
|
|||||||
static inline Waiter waiterForUTimer(UTimer* t)
|
static inline Waiter waiterForUTimer(UTimer* t)
|
||||||
{
|
{
|
||||||
Waiter wait_obj;
|
Waiter wait_obj;
|
||||||
wait_obj.type = WaiterType_UTimer;
|
wait_obj.type = WaiterType_Waitable;
|
||||||
wait_obj.timer = t;
|
wait_obj.waitable = &t->waitable;
|
||||||
return wait_obj;
|
return wait_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
// Implementation details.
|
// Implementation details.
|
||||||
|
|
||||||
typedef struct Waitable Waitable;
|
typedef struct Waitable Waitable;
|
||||||
|
typedef struct WaitableMethods WaitableMethods;
|
||||||
typedef struct WaitableNode WaitableNode;
|
typedef struct WaitableNode WaitableNode;
|
||||||
|
|
||||||
struct WaitableNode {
|
struct WaitableNode {
|
||||||
@ -18,6 +19,7 @@ struct WaitableNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Waitable {
|
struct Waitable {
|
||||||
|
const WaitableMethods* vt;
|
||||||
WaitableNode list;
|
WaitableNode list;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
};
|
};
|
||||||
@ -25,8 +27,7 @@ struct Waitable {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
WaiterType_Handle,
|
WaiterType_Handle,
|
||||||
WaiterType_HandleWithClear,
|
WaiterType_HandleWithClear,
|
||||||
WaiterType_UTimer,
|
WaiterType_Waitable,
|
||||||
WaiterType_UEvent,
|
|
||||||
} WaiterType;
|
} WaiterType;
|
||||||
|
|
||||||
// User-facing API starts here.
|
// User-facing API starts here.
|
||||||
@ -37,8 +38,7 @@ typedef struct {
|
|||||||
|
|
||||||
union {
|
union {
|
||||||
Handle handle;
|
Handle handle;
|
||||||
struct UTimer* timer;
|
Waitable* waitable;
|
||||||
struct UEvent* event;
|
|
||||||
};
|
};
|
||||||
} Waiter;
|
} Waiter;
|
||||||
|
|
||||||
|
@ -3,11 +3,21 @@
|
|||||||
#include "kernel/svc.h"
|
#include "kernel/svc.h"
|
||||||
#include "kernel/mutex.h"
|
#include "kernel/mutex.h"
|
||||||
#include "kernel/uevent.h"
|
#include "kernel/uevent.h"
|
||||||
#include "uevent.h"
|
#include "wait.h"
|
||||||
|
|
||||||
|
static bool _ueventBeginWait(Waitable* ww, WaiterNode* w, u64 cur_tick, u64* next_tick);
|
||||||
|
static Result _ueventOnTimeout(Waitable* ww, u64 old_tick);
|
||||||
|
static Result _ueventOnSignal(Waitable* ww);
|
||||||
|
|
||||||
|
static const WaitableMethods g_ueventVt = {
|
||||||
|
.beginWait = _ueventBeginWait,
|
||||||
|
.onTimeout = _ueventOnTimeout,
|
||||||
|
.onSignal = _ueventOnSignal,
|
||||||
|
};
|
||||||
|
|
||||||
void ueventCreate(UEvent* e, bool auto_clear)
|
void ueventCreate(UEvent* e, bool auto_clear)
|
||||||
{
|
{
|
||||||
_waitableInitialize(&e->waitable);
|
_waitableInitialize(&e->waitable, &g_ueventVt);
|
||||||
|
|
||||||
e->signal = false;
|
e->signal = false;
|
||||||
e->auto_clear = auto_clear;
|
e->auto_clear = auto_clear;
|
||||||
@ -28,22 +38,29 @@ void ueventSignal(UEvent* e)
|
|||||||
mutexUnlock(&e->waitable.mutex);
|
mutexUnlock(&e->waitable.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result _ueventTryAutoClear(UEvent* e)
|
Result _ueventOnSignal(Waitable* ww)
|
||||||
{
|
{
|
||||||
|
UEvent* e = (UEvent*)ww;
|
||||||
Result rc = 0;
|
Result rc = 0;
|
||||||
mutexLock(&e->waitable.mutex);
|
mutexLock(&e->waitable.mutex);
|
||||||
|
|
||||||
|
// 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.
|
||||||
if (e->auto_clear) {
|
if (e->auto_clear) {
|
||||||
if (e->signal)
|
if (e->signal)
|
||||||
e->signal = false;
|
e->signal = false;
|
||||||
else
|
else
|
||||||
rc = KERNELRESULT(Cancelled);
|
rc = KERNELRESULT(Cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutexUnlock(&e->waitable.mutex);
|
mutexUnlock(&e->waitable.mutex);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _ueventAddListener(UEvent* e, WaiterNode* w)
|
bool _ueventBeginWait(Waitable* ww, WaiterNode* w, u64 cur_tick, u64* next_tick)
|
||||||
{
|
{
|
||||||
|
UEvent* e = (UEvent*)ww;
|
||||||
mutexLock(&e->waitable.mutex);
|
mutexLock(&e->waitable.mutex);
|
||||||
|
|
||||||
bool can_add = !e->signal;
|
bool can_add = !e->signal;
|
||||||
@ -56,3 +73,9 @@ bool _ueventAddListener(UEvent* e, WaiterNode* w)
|
|||||||
mutexUnlock(&e->waitable.mutex);
|
mutexUnlock(&e->waitable.mutex);
|
||||||
return can_add;
|
return can_add;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result _ueventOnTimeout(Waitable* ww, u64 old_tick)
|
||||||
|
{
|
||||||
|
// This is not supposed to happen.
|
||||||
|
return KERNELRESULT(Cancelled);
|
||||||
|
}
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
// Copyright 2018 plutoo
|
|
||||||
#pragma once
|
|
||||||
#include "kernel/uevent.h"
|
|
||||||
#include "wait.h"
|
|
||||||
|
|
||||||
Result _ueventTryAutoClear(UEvent* e);
|
|
||||||
bool _ueventAddListener(UEvent* e, WaiterNode* w);
|
|
@ -1,14 +1,25 @@
|
|||||||
// Copyright 2018 plutoo
|
// Copyright 2018 plutoo
|
||||||
|
#include "result.h"
|
||||||
|
#include "arm/counter.h"
|
||||||
#include "kernel/svc.h"
|
#include "kernel/svc.h"
|
||||||
#include "kernel/utimer.h"
|
#include "kernel/utimer.h"
|
||||||
#include "arm/counter.h"
|
|
||||||
#include "wait.h"
|
#include "wait.h"
|
||||||
|
|
||||||
#define STOPPED 0
|
#define STOPPED 0
|
||||||
|
|
||||||
|
static bool _utimerBeginWait(Waitable* ww, WaiterNode* w, u64 cur_tick, u64* next_tick);
|
||||||
|
static Result _utimerOnTimeout(Waitable* ww, u64 old_tick);
|
||||||
|
static Result _utimerOnSignal(Waitable* ww);
|
||||||
|
|
||||||
|
static const WaitableMethods g_utimerVt = {
|
||||||
|
.beginWait = _utimerBeginWait,
|
||||||
|
.onTimeout = _utimerOnTimeout,
|
||||||
|
.onSignal = _utimerOnSignal,
|
||||||
|
};
|
||||||
|
|
||||||
void utimerCreate(UTimer* t, u64 interval, TimerType type)
|
void utimerCreate(UTimer* t, u64 interval, TimerType type)
|
||||||
{
|
{
|
||||||
_waitableInitialize(&t->waitable);
|
_waitableInitialize(&t->waitable, &g_utimerVt);
|
||||||
|
|
||||||
t->next_tick = STOPPED;
|
t->next_tick = STOPPED;
|
||||||
t->interval = armNsToTicks(interval);
|
t->interval = armNsToTicks(interval);
|
||||||
@ -40,10 +51,8 @@ void utimerStop(UTimer* t)
|
|||||||
mutexUnlock(&t->waitable.mutex);
|
mutexUnlock(&t->waitable.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _utimerRecalculate(UTimer* t, u64 old_tick)
|
static void _utimerRecalculate(UTimer* t, u64 old_tick)
|
||||||
{
|
{
|
||||||
mutexLock(&t->waitable.mutex);
|
|
||||||
|
|
||||||
if (t->next_tick == old_tick) {
|
if (t->next_tick == old_tick) {
|
||||||
u64 interval = t->interval;
|
u64 interval = t->interval;
|
||||||
u64 new_tick = 0;
|
u64 new_tick = 0;
|
||||||
@ -53,30 +62,54 @@ void _utimerRecalculate(UTimer* t, u64 old_tick)
|
|||||||
new_tick = STOPPED;
|
new_tick = STOPPED;
|
||||||
break;
|
break;
|
||||||
case TimerType_Repeating:
|
case TimerType_Repeating:
|
||||||
new_tick = old_tick + ((svcGetSystemTick() - old_tick + interval - 1)/interval)*interval;
|
new_tick = old_tick + ((armGetSystemTick() - old_tick + interval - 1)/interval)*interval;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
t->next_tick = new_tick;
|
t->next_tick = new_tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutexUnlock(&t->waitable.mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 _utimerGetNextTick(UTimer* t)
|
static bool _utimerBeginWait(Waitable* ww, WaiterNode* w, u64 cur_tick, u64* next_tick)
|
||||||
{
|
{
|
||||||
u64 ret;
|
UTimer* t = (UTimer*)ww;
|
||||||
|
|
||||||
mutexLock(&t->waitable.mutex);
|
mutexLock(&t->waitable.mutex);
|
||||||
ret = t->next_tick;
|
|
||||||
mutexUnlock(&t->waitable.mutex);
|
|
||||||
|
|
||||||
return ret;
|
// By default we do want to perform a wait, because if the
|
||||||
|
// timer's start/stop state changes we want to detect that.
|
||||||
|
u64 timer_tick = t->next_tick;
|
||||||
|
bool do_wait = true;
|
||||||
|
|
||||||
|
// Skip timer if stopped.
|
||||||
|
if (timer_tick != STOPPED) {
|
||||||
|
s64 diff = timer_tick - cur_tick;
|
||||||
|
|
||||||
|
// If the timer is already signalled, we're done.
|
||||||
|
if (diff < 0) {
|
||||||
|
_utimerRecalculate(t, timer_tick);
|
||||||
|
do_wait = false;
|
||||||
|
} else
|
||||||
|
*next_tick = diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_wait)
|
||||||
|
_waiterNodeAdd(w);
|
||||||
|
|
||||||
|
mutexUnlock(&t->waitable.mutex);
|
||||||
|
return do_wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _utimerAddListener(UTimer* t, WaiterNode* w)
|
static Result _utimerOnTimeout(Waitable* ww, u64 old_tick)
|
||||||
{
|
{
|
||||||
|
UTimer* t = (UTimer*)ww;
|
||||||
mutexLock(&t->waitable.mutex);
|
mutexLock(&t->waitable.mutex);
|
||||||
_waiterNodeAdd(w);
|
_utimerRecalculate(t, old_tick);
|
||||||
mutexUnlock(&t->waitable.mutex);
|
mutexUnlock(&t->waitable.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result _utimerOnSignal(Waitable* ww)
|
||||||
|
{
|
||||||
|
// Timer state changed, so we need to retry the wait.
|
||||||
|
return KERNELRESULT(Cancelled);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
// Copyright 2018 plutoo
|
|
||||||
#pragma once
|
|
||||||
#include "kernel/utimer.h"
|
|
||||||
#include "wait.h"
|
|
||||||
|
|
||||||
void _utimerRecalculate(UTimer* t, u64 old_tick);
|
|
||||||
u64 _utimerGetNextTick(UTimer* t);
|
|
||||||
void _utimerAddListener(UTimer* t, WaiterNode* w);
|
|
@ -6,8 +6,6 @@
|
|||||||
#include "kernel/uevent.h"
|
#include "kernel/uevent.h"
|
||||||
#include "arm/counter.h"
|
#include "arm/counter.h"
|
||||||
#include "wait.h"
|
#include "wait.h"
|
||||||
#include "utimer.h"
|
|
||||||
#include "uevent.h"
|
|
||||||
#include "../internal.h"
|
#include "../internal.h"
|
||||||
|
|
||||||
#define MAX_WAIT 0x40
|
#define MAX_WAIT 0x40
|
||||||
@ -39,7 +37,7 @@ static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 ti
|
|||||||
|
|
||||||
for (i = 0; i < num_objects; i ++) {
|
for (i = 0; i < num_objects; i ++) {
|
||||||
Waiter* obj = &objects[i];
|
Waiter* obj = &objects[i];
|
||||||
u64 timer_tick;
|
u64 next_tick;
|
||||||
bool added;
|
bool added;
|
||||||
|
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
@ -49,49 +47,25 @@ static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 ti
|
|||||||
handles[i] = obj->handle;
|
handles[i] = obj->handle;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WaiterType_UTimer:
|
case WaiterType_Waitable:
|
||||||
timer_tick = _utimerGetNextTick(obj->timer);
|
// Try to wait on the object. If it doesn't add a listener for this thread then
|
||||||
|
// it means the object is signalled and we're already done.
|
||||||
// Skip timer if stopped.
|
next_tick = UINT64_MAX;
|
||||||
if (timer_tick != 0) {
|
_waiterNodeInitialize(&waiters[i], obj->waitable, own_thread_handle, i, &triggered_idx);
|
||||||
// If the timer already signalled, we're done.
|
added = obj->waitable->vt->beginWait(obj->waitable, &waiters[i], cur_tick, &next_tick);
|
||||||
if (timer_tick < cur_tick) {
|
|
||||||
_utimerRecalculate(obj->timer, timer_tick);
|
|
||||||
|
|
||||||
*idx_out = i;
|
|
||||||
rc = 0;
|
|
||||||
goto clean_up;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override the user-supplied timeout if timer would fire before that.
|
|
||||||
if ((timer_tick - cur_tick) < end_tick) {
|
|
||||||
end_tick = timer_tick - cur_tick;
|
|
||||||
end_tick_idx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always add a listener on the timer,
|
|
||||||
// If the timer is started/stopped we want to detect that.
|
|
||||||
_waiterNodeInitialize(&waiters[i], &obj->timer->waitable, own_thread_handle, i, &triggered_idx);
|
|
||||||
_utimerAddListener(obj->timer, &waiters[i]);
|
|
||||||
|
|
||||||
waiters_added |= 1UL << i;
|
|
||||||
handles[i] = dummy_handle;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WaiterType_UEvent:
|
|
||||||
// Try to add a listener to the event, if it hasn't already signalled.
|
|
||||||
_waiterNodeInitialize(&waiters[i], &obj->event->waitable, own_thread_handle, i, &triggered_idx);
|
|
||||||
added = _ueventAddListener(obj->event, &waiters[i]);
|
|
||||||
|
|
||||||
// If the event already happened, we're done.
|
|
||||||
if (!added) {
|
if (!added) {
|
||||||
*idx_out = i;
|
*idx_out = i;
|
||||||
rc = 0;
|
rc = 0;
|
||||||
goto clean_up;
|
goto clean_up;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the event hasn't signalled, we added a listener.
|
// Otherwise, override the user-supplied timeout if the object specified an earlier timeout.
|
||||||
|
if (next_tick < end_tick) {
|
||||||
|
end_tick = next_tick;
|
||||||
|
end_tick_idx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add (fake) handle to the array.
|
||||||
waiters_added |= 1UL << i;
|
waiters_added |= 1UL << i;
|
||||||
handles[i] = dummy_handle;
|
handles[i] = dummy_handle;
|
||||||
break;
|
break;
|
||||||
@ -116,11 +90,11 @@ static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 ti
|
|||||||
if (end_tick_idx == -1)
|
if (end_tick_idx == -1)
|
||||||
goto clean_up;
|
goto clean_up;
|
||||||
|
|
||||||
// If not, it means a timer triggered the timeout.
|
// If not, it means an object triggered the timeout; handle it.
|
||||||
_utimerRecalculate(objects[end_tick_idx].timer, end_tick + cur_tick);
|
Waitable* w = objects[end_tick_idx].waitable;
|
||||||
|
rc = w->vt->onTimeout(w, end_tick + cur_tick);
|
||||||
*idx_out = end_tick_idx;
|
if (R_SUCCEEDED(rc))
|
||||||
rc = 0;
|
*idx_out = end_tick_idx;
|
||||||
} else if (R_VALUE(rc) == KERNELRESULT(Cancelled)) {
|
} else if (R_VALUE(rc) == KERNELRESULT(Cancelled)) {
|
||||||
// If no listener filled in its own index, we return the cancelled 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.
|
// This only happens if user for some reason manually does a svcCancelSynchronization.
|
||||||
@ -128,26 +102,11 @@ static Result waitImpl(s32* idx_out, Waiter* objects, size_t num_objects, u64 ti
|
|||||||
if (triggered_idx == -1)
|
if (triggered_idx == -1)
|
||||||
goto clean_up;
|
goto clean_up;
|
||||||
|
|
||||||
// An event was signalled, or a timer was updated.
|
// An object was signalled, handle it.
|
||||||
// So.. which is it?
|
Waitable* w = objects[triggered_idx].waitable;
|
||||||
switch (objects[triggered_idx].type) {
|
rc = w->vt->onSignal(w);
|
||||||
default:
|
if (R_SUCCEEDED(rc))
|
||||||
break;
|
*idx_out = triggered_idx;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clean_up:
|
clean_up:
|
||||||
|
@ -14,9 +14,16 @@ struct WaiterNode {
|
|||||||
s32 idx;
|
s32 idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void _waitableInitialize(Waitable* ww)
|
struct WaitableMethods {
|
||||||
|
bool (* beginWait)(Waitable* ww, WaiterNode* w, u64 cur_tick, u64* next_tick);
|
||||||
|
Result (* onTimeout)(Waitable* ww, u64 old_tick);
|
||||||
|
Result (* onSignal)(Waitable* ww);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void _waitableInitialize(Waitable* ww, const WaitableMethods* vt)
|
||||||
{
|
{
|
||||||
mutexInit(&ww->mutex);
|
mutexInit(&ww->mutex);
|
||||||
|
ww->vt = vt;
|
||||||
ww->list.next = &ww->list;
|
ww->list.next = &ww->list;
|
||||||
ww->list.prev = &ww->list;
|
ww->list.prev = &ww->list;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user