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));
|
||||
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_NvinfoFailedToInitialize,
|
||||
LibnxError_NvbufFailedToInitialize,
|
||||
LibnxError_TooManyWaitables,
|
||||
};
|
||||
|
||||
/// libnx binder error codes
|
||||
|
@ -36,14 +36,14 @@ Result eventWait(Event* t, u64 timeout)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
||||
|
||||
if (timeout != U64_MAX)
|
||||
deadline = armGetSystemTick() + timeout * 12 / 625; // timeout: ns->ticks
|
||||
deadline = armGetSystemTick() + armNsToTick(timeout); // timeout: ns->ticks
|
||||
|
||||
do {
|
||||
do {
|
||||
s64 this_timeout = -1;
|
||||
if (deadline) {
|
||||
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);
|
||||
|
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