API Design

/* A cooperative task is a function that yields control back to the game loop
   at defined points. Tasks resume where they left off next frame. */

/* Task states */
typedef enum {
    SH_TASK_READY,      /* Waiting to run */
    SH_TASK_RUNNING,    /* Currently executing */
    SH_TASK_YIELDED,    /* Yielded, will resume next frame */
    SH_TASK_SLEEPING,   /* Yielded with timer, resumes after N ms */
    SH_TASK_DONE,       /* Completed */
} sh_task_state_t;

/* Task handle (opaque) */
typedef struct sh_task sh_task_t;

/* Task function signature. Return 0 = done, 1 = yield (resume next frame). */
typedef int (*sh_task_fn)(sh_task_t *self, void *userdata);

/* Task scheduler (lives in game loop) */
typedef struct {
    sh_task_t  *tasks[16];   /* Max 16 concurrent tasks */
    int         count;
    uint64_t    frame_budget_ns;  /* Max ns to spend on tasks per frame */
} sh_scheduler_t;

/* Initialize scheduler. budget_ns = 0 means no limit (run all ready tasks). */
void sh_scheduler_init(sh_scheduler_t *sched, uint64_t frame_budget_ns);

/* Create and enqueue a cooperative task. */
sh_task_t *sh_task_create(sh_scheduler_t *sched, sh_task_fn fn, void *userdata);

/* Yield current task (resume next frame). Call from within task function. */
void sh_task_yield(sh_task_t *self);

/* Yield current task for N milliseconds. */
void sh_task_sleep(sh_task_t *self, uint32_t ms);

/* Check if task is done. */
int sh_task_is_done(const sh_task_t *self);

/* Destroy a completed task. */
void sh_task_destroy(sh_task_t *task);

/* Run ready tasks. Call once per frame, typically after update() but before render().
   Respects frame_budget_ns — stops running tasks if budget exceeded. */
void sh_scheduler_tick(sh_scheduler_t *sched);

/* Destroy scheduler and all tasks. */
void sh_scheduler_shutdown(sh_scheduler_t *sched);


/* --- Async I/O (uses KOS worker thread underneath) --- */

/* Request async file read. Returns immediately. Check sh_task_is_done(). */
sh_task_t *sh_async_read(sh_scheduler_t *sched,
                          const char *path,
                          void **out_data,
                          int *out_size);

/* Request async texture load (file read + VRAM upload). */
sh_task_t *sh_async_tex_load(sh_scheduler_t *sched,
                              const char *path,
                              sh_texture_t *out);