kernel: Introduce utimer, uevent, and waitN

This commit is contained in:
plutooo 2018-10-27 04:29:51 +02:00 committed by fincs
parent 42e7cf44cb
commit 370d78453e
11 changed files with 357 additions and 2 deletions

View File

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

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

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

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

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

View File

@ -122,6 +122,7 @@ enum {
LibnxError_ApmFailedToInitialize,
LibnxError_NvinfoFailedToInitialize,
LibnxError_NvbufFailedToInitialize,
LibnxError_TooManyWaitables,
};
/// libnx binder error codes

View File

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