mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 20:42:44 +02:00
kernel: Introduce utimer, uevent, and waitN
This commit is contained in:
parent
42e7cf44cb
commit
370d78453e
@ -26,3 +26,11 @@ static inline u64 armGetSystemTickFreq(void) {
|
|||||||
__asm__ ("mrs %x[data], cntfrq_el0" : [data] "=r" (ret));
|
__asm__ ("mrs %x[data], cntfrq_el0" : [data] "=r" (ret));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u64 armNsToTick(u64 ns) {
|
||||||
|
return (ns * 12) / 625;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 armTickToNs(u64 tick) {
|
||||||
|
return (tick * 625) / 12;
|
||||||
|
}
|
||||||
|
23
nx/include/switch/kernel/uevent.h
Normal file
23
nx/include/switch/kernel/uevent.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#pragma once
|
||||||
|
#include "kernel/mutex.h"
|
||||||
|
#include "kernel/waiter.h"
|
||||||
|
|
||||||
|
typedef struct UsermodeEvent UsermodeEvent;
|
||||||
|
|
||||||
|
struct UsermodeEvent
|
||||||
|
{
|
||||||
|
Mutex mutex;
|
||||||
|
WaiterNode waiter_list;
|
||||||
|
bool signal;
|
||||||
|
bool auto_clear;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ueventCreate(UsermodeEvent* e, bool auto_clear);
|
||||||
|
void ueventClear(UsermodeEvent* e);
|
||||||
|
void ueventSignal(UsermodeEvent* e);
|
||||||
|
|
||||||
|
// Internal methods (do not use!):
|
||||||
|
bool _ueventConsumeIfSignalled(UsermodeEvent* e);
|
||||||
|
void _ueventAddListener(UsermodeEvent* e, WaiterNode* w);
|
||||||
|
void _ueventRemoveListener(UsermodeEvent* e, WaiterNode* w);
|
17
nx/include/switch/kernel/utimer.h
Normal file
17
nx/include/switch/kernel/utimer.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#pragma once
|
||||||
|
#include "kernel/svc.h"
|
||||||
|
|
||||||
|
typedef struct UsermodeTimer UsermodeTimer;
|
||||||
|
|
||||||
|
struct UsermodeTimer
|
||||||
|
{
|
||||||
|
u64 next_time;
|
||||||
|
u64 interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
void utimerCreate(UsermodeTimer* t, u64 interval);
|
||||||
|
|
||||||
|
// Internal methods (do not use!):
|
||||||
|
void _utimerRecalculate(UsermodeTimer* t, u64 old_time);
|
||||||
|
u64 _utimerGetNextTime(UsermodeTimer* t);
|
24
nx/include/switch/kernel/wait.h
Normal file
24
nx/include/switch/kernel/wait.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#pragma once
|
||||||
|
#include "kernel/mutex.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WaitObjectType_Handle,
|
||||||
|
WaitObjectType_UsermodeTimer,
|
||||||
|
WaitObjectType_UsermodeEvent,
|
||||||
|
} WaitObjectType;
|
||||||
|
|
||||||
|
typedef struct UsermodeTimer UsermodeTimer;
|
||||||
|
typedef struct UsermodeEvent UsermodeEvent;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
WaitObjectType type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
Handle handle;
|
||||||
|
UsermodeTimer* timer;
|
||||||
|
UsermodeEvent* event;
|
||||||
|
};
|
||||||
|
} WaitObject;
|
||||||
|
|
||||||
|
Result waitN(s32* idx_out, WaitObject* objects, size_t num_objects, u64 timeout);
|
35
nx/include/switch/kernel/waiter.h
Normal file
35
nx/include/switch/kernel/waiter.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#pragma once
|
||||||
|
#include "kernel/svc.h"
|
||||||
|
#include "kernel/mutex.h"
|
||||||
|
#include "kernel/wait.h"
|
||||||
|
|
||||||
|
#define MAX_WAIT 0x40
|
||||||
|
|
||||||
|
typedef struct WaiterNode WaiterNode;
|
||||||
|
typedef struct Waiter Waiter;
|
||||||
|
|
||||||
|
struct WaiterNode
|
||||||
|
{
|
||||||
|
Waiter* owner;
|
||||||
|
s32 idx;
|
||||||
|
WaiterNode* prev;
|
||||||
|
WaiterNode* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Waiter
|
||||||
|
{
|
||||||
|
Mutex mutex;
|
||||||
|
Handle thread;
|
||||||
|
s32 signalled_idx;
|
||||||
|
|
||||||
|
WaiterNode nodes[MAX_WAIT];
|
||||||
|
size_t num_nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal methods (do not use!):
|
||||||
|
void _waiterCreate(Waiter* w);
|
||||||
|
void _waiterFree(Waiter* w, WaitObject* objects);
|
||||||
|
void _waiterSubscribe(Waiter* w, UsermodeEvent* e);
|
||||||
|
bool _waiterSignal(Waiter* w, s32 idx);
|
||||||
|
s32 _waiterGetSignalledIndex(Waiter* w);
|
@ -122,6 +122,7 @@ enum {
|
|||||||
LibnxError_ApmFailedToInitialize,
|
LibnxError_ApmFailedToInitialize,
|
||||||
LibnxError_NvinfoFailedToInitialize,
|
LibnxError_NvinfoFailedToInitialize,
|
||||||
LibnxError_NvbufFailedToInitialize,
|
LibnxError_NvbufFailedToInitialize,
|
||||||
|
LibnxError_TooManyWaitables,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// libnx binder error codes
|
/// libnx binder error codes
|
||||||
|
@ -36,14 +36,14 @@ Result eventWait(Event* t, u64 timeout)
|
|||||||
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
||||||
|
|
||||||
if (timeout != U64_MAX)
|
if (timeout != U64_MAX)
|
||||||
deadline = armGetSystemTick() + timeout * 12 / 625; // timeout: ns->ticks
|
deadline = armGetSystemTick() + armNsToTick(timeout); // timeout: ns->ticks
|
||||||
|
|
||||||
do {
|
do {
|
||||||
do {
|
do {
|
||||||
s64 this_timeout = -1;
|
s64 this_timeout = -1;
|
||||||
if (deadline) {
|
if (deadline) {
|
||||||
this_timeout = deadline - armGetSystemTick();
|
this_timeout = deadline - armGetSystemTick();
|
||||||
this_timeout = (this_timeout >= 0 ? this_timeout : 0) * 625 / 12; // ticks->ns
|
this_timeout = armTickToNs(this_timeout >= 0 ? this_timeout : 0); // ticks->ns
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = svcWaitSynchronizationSingle(t->revent, this_timeout);
|
rc = svcWaitSynchronizationSingle(t->revent, this_timeout);
|
||||||
|
74
nx/source/kernel/uevent.c
Normal file
74
nx/source/kernel/uevent.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#include "kernel/svc.h"
|
||||||
|
#include "kernel/mutex.h"
|
||||||
|
#include "kernel/waiter.h"
|
||||||
|
#include "kernel/uevent.h"
|
||||||
|
|
||||||
|
void ueventCreate(UsermodeEvent* e, bool auto_clear)
|
||||||
|
{
|
||||||
|
mutexInit(&e->mutex);
|
||||||
|
e->waiter_list.prev = &e->waiter_list;
|
||||||
|
e->waiter_list.next = &e->waiter_list;
|
||||||
|
e->signal = false;
|
||||||
|
e->auto_clear = auto_clear;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ueventClear(UsermodeEvent* e)
|
||||||
|
{
|
||||||
|
mutexLock(&e->mutex);
|
||||||
|
e->signal = false;
|
||||||
|
mutexUnlock(&e->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ueventSignal(UsermodeEvent* e)
|
||||||
|
{
|
||||||
|
mutexLock(&e->mutex);
|
||||||
|
e->signal = true;
|
||||||
|
|
||||||
|
WaiterNode* end = &e->waiter_list;
|
||||||
|
WaiterNode* w = end;
|
||||||
|
|
||||||
|
while (w->next != end)
|
||||||
|
{
|
||||||
|
w = w->next;
|
||||||
|
|
||||||
|
bool signalled = _waiterSignal(w->owner, w->idx);
|
||||||
|
|
||||||
|
if (signalled && e->auto_clear)
|
||||||
|
e->signal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutexUnlock(&e->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _ueventConsumeIfSignalled(UsermodeEvent* e)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
mutexLock(&e->mutex);
|
||||||
|
|
||||||
|
ret = e->signal;
|
||||||
|
|
||||||
|
if (ret && e->auto_clear)
|
||||||
|
e->signal = false;
|
||||||
|
|
||||||
|
mutexUnlock(&e->mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _ueventAddListener(UsermodeEvent* e, WaiterNode* w)
|
||||||
|
{
|
||||||
|
mutexLock(&e->mutex);
|
||||||
|
e->waiter_list.next->prev = w;
|
||||||
|
w->next = e->waiter_list.next;
|
||||||
|
w->prev = &e->waiter_list;
|
||||||
|
e->waiter_list.next = w;
|
||||||
|
mutexUnlock(&e->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _ueventRemoveListener(UsermodeEvent* e, WaiterNode* w)
|
||||||
|
{
|
||||||
|
mutexLock(&e->mutex);
|
||||||
|
w->prev->next = w->next;
|
||||||
|
w->next->prev = w->prev;
|
||||||
|
mutexUnlock(&e->mutex);
|
||||||
|
}
|
22
nx/source/kernel/utimer.c
Normal file
22
nx/source/kernel/utimer.c
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#include "kernel/svc.h"
|
||||||
|
#include "kernel/utimer.h"
|
||||||
|
#include "arm/counter.h"
|
||||||
|
|
||||||
|
void utimerCreate(UsermodeTimer* t, u64 interval)
|
||||||
|
{
|
||||||
|
t->next_time = armGetSystemTick() + armNsToTick(interval);
|
||||||
|
t->interval = armNsToTick(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _utimerRecalculate(UsermodeTimer* t, u64 old_time)
|
||||||
|
{
|
||||||
|
s64 interval = t->interval;
|
||||||
|
s64 new_time = ((armGetSystemTick() - old_time + interval - 1) / interval) * interval;
|
||||||
|
__sync_bool_compare_and_swap(&t->next_time, old_time, new_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 _utimerGetNextTime(UsermodeTimer* t)
|
||||||
|
{
|
||||||
|
return t->next_time;
|
||||||
|
}
|
104
nx/source/kernel/wait.c
Normal file
104
nx/source/kernel/wait.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#include "result.h"
|
||||||
|
#include "kernel/svc.h"
|
||||||
|
#include "kernel/wait.h"
|
||||||
|
#include "kernel/waiter.h"
|
||||||
|
#include "kernel/utimer.h"
|
||||||
|
#include "kernel/uevent.h"
|
||||||
|
#include "arm/counter.h"
|
||||||
|
#include "../internal.h"
|
||||||
|
|
||||||
|
Result waitN(s32* idx_out, WaitObject* objects, size_t num_objects, u64 timeout)
|
||||||
|
{
|
||||||
|
if (num_objects > MAX_WAIT)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_TooManyWaitables);
|
||||||
|
|
||||||
|
Handle dummy_handle = getThreadVars()->handle;
|
||||||
|
|
||||||
|
Waiter waiter;
|
||||||
|
_waiterCreate(&waiter);
|
||||||
|
|
||||||
|
Handle handles[MAX_WAIT];
|
||||||
|
u64 cur_tick = armGetSystemTick();
|
||||||
|
|
||||||
|
u64 end_time = (timeout == UINT64_MAX) ? UINT64_MAX : cur_tick + timeout;
|
||||||
|
s32 end_time_idx = -1;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i=0; i<num_objects; i++)
|
||||||
|
{
|
||||||
|
WaitObject* obj = &objects[i];
|
||||||
|
u64 timer_tick;
|
||||||
|
|
||||||
|
switch (obj->type)
|
||||||
|
{
|
||||||
|
case WaitObjectType_UsermodeTimer:
|
||||||
|
// Override the user-supplied timeout if timer would fire before that.
|
||||||
|
timer_tick = _utimerGetNextTime(obj->timer);
|
||||||
|
|
||||||
|
if (timer_tick < end_time)
|
||||||
|
{
|
||||||
|
end_time = timer_tick;
|
||||||
|
end_time_idx = i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WaitObjectType_UsermodeEvent:
|
||||||
|
// If the event already happened, we're done.
|
||||||
|
if (_ueventConsumeIfSignalled(obj->event))
|
||||||
|
{
|
||||||
|
*idx_out = i;
|
||||||
|
_waiterFree(&waiter, objects);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not, add a listener.
|
||||||
|
_waiterSubscribe(&waiter, obj->event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WaitObjectType_Handle:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
handles[i] = (obj->type == WaitObjectType_Handle) ? obj->handle : dummy_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the timer already signalled, we're done.
|
||||||
|
if (end_time < cur_tick)
|
||||||
|
{
|
||||||
|
*idx_out = end_time_idx;
|
||||||
|
_utimerRecalculate(objects[end_time_idx].timer, end_time);
|
||||||
|
_waiterFree(&waiter, objects);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the actual syscall.
|
||||||
|
Result rc;
|
||||||
|
rc = svcWaitSynchronization(idx_out, handles, num_objects, end_time - cur_tick);
|
||||||
|
|
||||||
|
// Timeout-error?
|
||||||
|
if (rc == 0xEA01)
|
||||||
|
{
|
||||||
|
// If the user-supplied timeout, we return the error back to them.
|
||||||
|
if (end_time_idx == -1)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// If not, it means a timer was triggered.
|
||||||
|
*idx_out = end_time_idx;
|
||||||
|
_utimerRecalculate(objects[end_time_idx].timer, end_time);
|
||||||
|
_waiterFree(&waiter, objects);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupted-error?
|
||||||
|
if (rc == 0xEC01)
|
||||||
|
{
|
||||||
|
// An event was signalled.
|
||||||
|
*idx_out = _waiterGetSignalledIndex(&waiter);
|
||||||
|
_waiterFree(&waiter, objects);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_waiterFree(&waiter, objects);
|
||||||
|
return rc;
|
||||||
|
}
|
47
nx/source/kernel/waiter.c
Normal file
47
nx/source/kernel/waiter.c
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2018 plutoo
|
||||||
|
#include "kernel/waiter.h"
|
||||||
|
#include "kernel/uevent.h"
|
||||||
|
#include "../internal.h"
|
||||||
|
|
||||||
|
#define NOT_YET_SIGNALLED (-1)
|
||||||
|
|
||||||
|
void _waiterCreate(Waiter* w)
|
||||||
|
{
|
||||||
|
mutexInit(&w->mutex);
|
||||||
|
w->thread = getThreadVars()->handle;
|
||||||
|
w->signalled_idx = NOT_YET_SIGNALLED;
|
||||||
|
w->num_nodes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _waiterFree(Waiter* w, WaitObject* objects)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i=0; i<w->num_nodes; i++)
|
||||||
|
_ueventRemoveListener(objects[w->nodes[i].idx].event, &w->nodes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _waiterSignal(Waiter* w, s32 idx)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
mutexLock(&w->mutex);
|
||||||
|
|
||||||
|
if (w->signalled_idx == NOT_YET_SIGNALLED)
|
||||||
|
{
|
||||||
|
w->signalled_idx = idx;
|
||||||
|
svcCancelSynchronization(w->thread);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutexUnlock(&w->mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _waiterSubscribe(Waiter* w, UsermodeEvent* e)
|
||||||
|
{
|
||||||
|
_ueventAddListener(e, &w->nodes[w->num_nodes++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 _waiterGetSignalledIndex(Waiter* w)
|
||||||
|
{
|
||||||
|
return w->signalled_idx;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user