Simplify WaiterNode, make it private. Improve UEvent autoclear logic.

This commit is contained in:
fincs 2018-12-14 21:27:15 +01:00 committed by fincs
parent 47b786bcea
commit c9ee12ce20
7 changed files with 53 additions and 56 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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