Add ability to wait for task's "finished" event
This works even if the scheduler is shutting down, allowing reliable "cleanup" routines to be registered.
This commit is contained in:
parent
f2c8fae0cc
commit
d54c58a7bc
2 changed files with 61 additions and 19 deletions
|
@ -85,6 +85,8 @@ struct CoTaskData {
|
|||
} event;
|
||||
};
|
||||
} wait;
|
||||
|
||||
CoTaskEvents events;
|
||||
};
|
||||
|
||||
typedef struct CoTaskInitData {
|
||||
|
@ -329,6 +331,7 @@ INLINE CoTaskData *get_task_data(CoTask *task) {
|
|||
|
||||
static void cotask_finalize(CoTask *task) {
|
||||
CoTaskData *task_data = get_task_data(task);
|
||||
COEVENT_CANCEL_ARRAY(task_data->events);
|
||||
|
||||
TASK_DEBUG("Finalizing task %s", task->debug_label);
|
||||
|
||||
|
@ -392,6 +395,8 @@ static void cotask_entry_setup(CoTask *task, CoTaskData *data, CoTaskInitData *i
|
|||
if(master_data) {
|
||||
cotask_enslave(master_data, data);
|
||||
}
|
||||
|
||||
COEVENT_INIT_ARRAY(data->events);
|
||||
}
|
||||
|
||||
static void *cotask_entry(void *varg) {
|
||||
|
@ -407,6 +412,7 @@ static void *cotask_entry(void *varg) {
|
|||
varg = func(varg);
|
||||
|
||||
TASK_DEBUG("Task %s about to die naturally", task->debug_label);
|
||||
coevent_signal(&data.events.finished);
|
||||
cotask_finalize(task);
|
||||
|
||||
return varg;
|
||||
|
@ -422,6 +428,7 @@ static void *cotask_entry_noyield(void *varg) {
|
|||
// init_data is now invalid
|
||||
|
||||
TASK_DEBUG("Task %s about to die naturally", task->debug_label);
|
||||
coevent_signal(&data.events.finished);
|
||||
cotask_finalize(task);
|
||||
|
||||
return varg;
|
||||
|
@ -512,7 +519,8 @@ CoEventStatus coevent_poll(const CoEvent *evt, const CoEventSnapshot *snap) {
|
|||
|
||||
if(
|
||||
evt->unique_id != snap->unique_id ||
|
||||
evt->num_signaled < snap->num_signaled
|
||||
evt->num_signaled < snap->num_signaled ||
|
||||
evt->unique_id == 0
|
||||
) {
|
||||
EVT_DEBUG("Event was canceled");
|
||||
return CO_EVENT_CANCELED;
|
||||
|
@ -542,7 +550,7 @@ static bool cotask_do_wait(CoTaskData *task_data) {
|
|||
}
|
||||
|
||||
case COTASK_WAIT_EVENT: {
|
||||
TASK_DEBUG("COTASK_WAIT_EVENT in task %s", task_data->task->debug_label);
|
||||
// TASK_DEBUG("COTASK_WAIT_EVENT in task %s", task_data->task->debug_label);
|
||||
|
||||
CoEventStatus stat = coevent_poll(task_data->wait.event.pevent, &task_data->wait.event.snapshot);
|
||||
if(stat != CO_EVENT_PENDING) {
|
||||
|
@ -633,7 +641,10 @@ static void coevent_add_subscriber(CoEvent *evt, CoTask *task) {
|
|||
}
|
||||
|
||||
CoWaitResult cotask_wait_event(CoEvent *evt, void *arg) {
|
||||
assert(evt->unique_id > 0);
|
||||
// assert(evt->unique_id > 0);
|
||||
if(evt->unique_id == 0) {
|
||||
return (CoWaitResult) { .event_status = CO_EVENT_CANCELED };
|
||||
}
|
||||
|
||||
CoTask *task = cotask_active();
|
||||
CoTaskData *task_data = get_task_data(task);
|
||||
|
@ -679,6 +690,11 @@ EntityInterface *(cotask_bind_to_entity)(CoTask *task, EntityInterface *ent) {
|
|||
return ent;
|
||||
}
|
||||
|
||||
CoTaskEvents *cotask_get_events(CoTask *task) {
|
||||
CoTaskData *task_data = get_task_data(task);
|
||||
return &task_data->events;
|
||||
}
|
||||
|
||||
void coevent_init(CoEvent *evt) {
|
||||
static uint32_t g_uid;
|
||||
*evt = (CoEvent) { .unique_id = ++g_uid };
|
||||
|
@ -785,24 +801,39 @@ uint cosched_run_tasks(CoSched *sched) {
|
|||
}
|
||||
|
||||
static void force_finish_task(CoTask *task) {
|
||||
TASK_DEBUG("Finishing task %s", task->debug_label);
|
||||
|
||||
if(task->data) {
|
||||
TASK_DEBUG("Task %s has data, finalizing", task->debug_label);
|
||||
cotask_finalize(task);
|
||||
} else {
|
||||
TASK_DEBUG("Task %s had no data", task->debug_label);
|
||||
}
|
||||
|
||||
cotask_free(task);
|
||||
}
|
||||
|
||||
static void pre_finish_task_list(CoTaskList *tasks) {
|
||||
for(CoTask *t = tasks->first; t; t = t->next) {
|
||||
if(t->data) {
|
||||
COEVENT_CANCEL_ARRAY(t->data->events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void finish_task_list(CoTaskList *tasks) {
|
||||
for(CoTask *t; (t = alist_pop(tasks));) {
|
||||
force_finish_task(t);
|
||||
}
|
||||
}
|
||||
|
||||
void cosched_finish(CoSched *sched) {
|
||||
for(CoTask *t = sched->pending_tasks.first, *next; t; t = next) {
|
||||
next = t->next;
|
||||
force_finish_task(t);
|
||||
}
|
||||
|
||||
for(CoTask *t = sched->tasks.first, *next; t; t = next) {
|
||||
next = t->next;
|
||||
force_finish_task(t);
|
||||
}
|
||||
|
||||
pre_finish_task_list(&sched->tasks);
|
||||
pre_finish_task_list(&sched->pending_tasks);
|
||||
finish_task_list(&sched->tasks);
|
||||
finish_task_list(&sched->pending_tasks);
|
||||
assert(!sched->tasks.first);
|
||||
assert(!sched->pending_tasks.first);
|
||||
memset(sched, 0, sizeof(*sched));
|
||||
}
|
||||
|
||||
|
@ -817,7 +848,7 @@ void coroutines_shutdown(void) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef CO_TASK_STATS
|
||||
#include "video.h"
|
||||
#include "resource/font.h"
|
||||
#endif
|
||||
|
|
|
@ -38,7 +38,7 @@ typedef enum CoEventStatus {
|
|||
} CoEventStatus;
|
||||
|
||||
typedef struct BoxedTask {
|
||||
uintptr_t ptr;
|
||||
alignas(alignof(void*)) uintptr_t ptr;
|
||||
uint32_t unique_id;
|
||||
} BoxedTask;
|
||||
|
||||
|
@ -58,8 +58,20 @@ typedef struct CoEventSnapshot {
|
|||
uint16_t num_signaled;
|
||||
} CoEventSnapshot;
|
||||
|
||||
#define COEVENTS_ARRAY(...) \
|
||||
union { \
|
||||
CoEvent _first_event_; \
|
||||
struct { CoEvent __VA_ARGS__; }; \
|
||||
}
|
||||
|
||||
typedef COEVENTS_ARRAY(
|
||||
finished
|
||||
) CoTaskEvents;
|
||||
|
||||
typedef LIST_ANCHOR(CoTask) CoTaskList;
|
||||
|
||||
struct CoSched {
|
||||
LIST_ANCHOR(CoTask) tasks, pending_tasks;
|
||||
CoTaskList tasks, pending_tasks;
|
||||
};
|
||||
|
||||
typedef struct CoWaitResult {
|
||||
|
@ -94,7 +106,7 @@ CoWaitResult cotask_wait_event_or_die(CoEvent *evt, void *arg);
|
|||
CoStatus cotask_status(CoTask *task);
|
||||
CoTask *cotask_active(void);
|
||||
EntityInterface *cotask_bind_to_entity(CoTask *task, EntityInterface *ent) attr_returns_nonnull;
|
||||
CoEvent *cotask_get_finished_event(CoTask *task);
|
||||
CoTaskEvents *cotask_get_events(CoTask *task);
|
||||
|
||||
BoxedTask cotask_box(CoTask *task);
|
||||
CoTask *cotask_unbox(BoxedTask box);
|
||||
|
@ -108,8 +120,6 @@ CoEventStatus coevent_poll(const CoEvent *evt, const CoEventSnapshot *snap);
|
|||
|
||||
void _coevent_array_action(uint num, CoEvent *events, void (*func)(CoEvent*));
|
||||
|
||||
#define COEVENTS_ARRAY(...) union { CoEvent _first_event_; struct { CoEvent __VA_ARGS__; }; }
|
||||
|
||||
#define COEVENT_ARRAY_ACTION(func, array) (_coevent_array_action(sizeof(array)/sizeof(CoEvent), &((array)._first_event_), func))
|
||||
#define COEVENT_INIT_ARRAY(array) COEVENT_ARRAY_ACTION(coevent_init, array)
|
||||
#define COEVENT_CANCEL_ARRAY(array) COEVENT_ARRAY_ACTION(coevent_cancel, array)
|
||||
|
@ -379,6 +389,7 @@ DECLARE_EXTERN_TASK(_cancel_task_helper, { BoxedTask task; });
|
|||
#define INVOKE_SUBTASK_INDIRECT(iface, ...) INVOKE_TASK_INDIRECT_(cosched_new_subtask, iface, __VA_ARGS__, ._dummy_1 = 0)
|
||||
|
||||
#define THIS_TASK cotask_box(cotask_active())
|
||||
#define TASK_EVENTS(task) cotask_get_events(cotask_unbox(task))
|
||||
|
||||
#define YIELD cotask_yield(NULL)
|
||||
#define WAIT(delay) cotask_wait(delay)
|
||||
|
|
Loading…
Reference in a new issue