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:
Andrei Alexeyev 2020-03-08 02:35:53 +02:00
parent f2c8fae0cc
commit d54c58a7bc
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
2 changed files with 61 additions and 19 deletions

View file

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

View file

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