typedef void CoroutineFunction(mco_coro *co); CCtx *_CoroutineContext = NULL; CCtx *GetCoroutineContext() { Assert(_CoroutineContext); return _CoroutineContext; } void DestroyCoroutine(CCtx *n) { mco_destroy(n->co); Release(&n->arena); } void RemoveCoroutine(String name) { IterRemove(ActiveCoroutines) { IterRemovePrepare(ActiveCoroutines); if (it.name == name) { DestroyCoroutine(&it); remove_item = true; } } } CCtx CreateCoroutine(CoroutineFunction *func, String name) { mco_desc desc = mco_desc_init(func, 0); mco_coro *coro = NULL; mco_result ok = mco_create(&coro, &desc); assert(ok == MCO_SUCCESS); return {coro, name}; } #define AddCoroutine(x) AddCoroutineEx(x, #x) CCtx *AddCoroutineEx(CoroutineFunction *func, String name) { CCtx coroutine = CreateCoroutine(func, name); Add(&ActiveCoroutines, coroutine); return GetLast(ActiveCoroutines); } void ResumeCoroutine(CCtx *dat) { CCtx *prev_curr = _CoroutineContext; _CoroutineContext = dat; mco_result ok = mco_resume(dat->co); Assert(ok == MCO_SUCCESS); _CoroutineContext = prev_curr; } void UpdateCoroutines(Event *event) { ProfileFunction(); double start = GetTimeSeconds(); for (;ActiveCoroutines.len;) { IterRemove(ActiveCoroutines) { IterRemovePrepare(ActiveCoroutines); ProfileScopeEx(it.name); mco_state status = mco_status(it.co); if (status == MCO_DEAD) { DestroyCoroutine(&it); remove_item = true; } else { mco_push(it.co, &event, sizeof(Event *)); _CoroutineContext = ⁢ mco_result ok = mco_resume(it.co); if (ok != MCO_SUCCESS) { ReportErrorf("failed to resume coroutine %d", ok); DestroyCoroutine(&it); remove_item = true; } _CoroutineContext = NULL; } } #if SLOW_BUILD //:CoroutineLeakCheck // Make sure we don't leak scratch arenas between coroutine boundaries. // it leads to memory corruption! If you hit Assert here make sure you // don't leak the scratch arena. for (int i = 0; i < Lengthof(ScratchArenas); i += 1) { Assert(ScratchArenas[i].refs == 0); } #endif double took = GetTimeSeconds() - start; bool dont_loop_on_coroutines_when_budget_is_ok_exit_immediately = Testing; if (dont_loop_on_coroutines_when_budget_is_ok_exit_immediately || (took > (0.016666 / 3.0))) { break; } } } Event *Yield(mco_coro *co) { mco_result ok = mco_yield(co); Assert(ok == MCO_SUCCESS); Event *event = NULL; ok = mco_pop(co, &event, sizeof(Event *)); Assert(ok == MCO_SUCCESS); return event; }