/* * machinarium. * * cooperative multitasking engine. */ #include #include static void mm_scheduler_main(void *arg) { mm_coroutine_t *coroutine = arg; mm_scheduler_t *scheduler = &mm_self->scheduler; mm_scheduler_set(scheduler, coroutine, MM_CACTIVE); coroutine->function(coroutine->function_arg); /* wakeup joiners */ mm_list_t *i; mm_list_foreach(&coroutine->joiners, i) { mm_coroutine_t *joiner; joiner = mm_container_of(i, mm_coroutine_t, link_join); mm_scheduler_set(scheduler, joiner, MM_CREADY); } /* Mark coroutine free and ready to be put back in * cache for reuse. * * Yet this can not be done here, because yield expects previous * coroutine stack to be available. */ mm_scheduler_set(scheduler, coroutine, MM_CFREE); mm_scheduler_yield(scheduler); } int mm_scheduler_init(mm_scheduler_t *scheduler) { mm_list_init(&scheduler->list_ready); mm_list_init(&scheduler->list_active); scheduler->id_seq = 0; scheduler->count_ready = 0; scheduler->count_active = 0; mm_coroutine_init(&scheduler->main); scheduler->current = &scheduler->main; return 0; } void mm_scheduler_free(mm_scheduler_t *scheduler) { mm_coroutine_t *coroutine; mm_list_t *i, *p; mm_list_foreach_safe(&scheduler->list_ready, i, p) { coroutine = mm_container_of(i, mm_coroutine_t, link); mm_coroutine_free(coroutine); } mm_list_foreach_safe(&scheduler->list_active, i, p) { coroutine = mm_container_of(i, mm_coroutine_t, link); mm_coroutine_free(coroutine); } } void mm_scheduler_run(mm_scheduler_t *scheduler, mm_coroutine_cache_t *cache) { while (scheduler->count_ready > 0) { mm_coroutine_t *coroutine; coroutine = mm_container_of(scheduler->list_ready.next, mm_coroutine_t, link); mm_scheduler_set(&mm_self->scheduler, coroutine, MM_CACTIVE); mm_scheduler_call(&mm_self->scheduler, coroutine); if (coroutine->state == MM_CFREE) mm_coroutine_cache_push(cache, coroutine); } } void mm_scheduler_new(mm_scheduler_t *scheduler, mm_coroutine_t *coroutine, mm_function_t function, void *arg) { mm_list_init(&coroutine->link); mm_list_init(&coroutine->link_join); mm_list_init(&coroutine->joiners); coroutine->cancel = 0; coroutine->id = scheduler->id_seq++; coroutine->function = function; coroutine->function_arg = arg; mm_context_create(&coroutine->context, &coroutine->stack, mm_scheduler_main, coroutine); mm_scheduler_set(scheduler, coroutine, MM_CREADY); } mm_coroutine_t* mm_scheduler_find(mm_scheduler_t *scheduler, uint64_t id) { mm_coroutine_t *coroutine; mm_list_t *i; mm_list_foreach(&scheduler->list_ready, i) { coroutine = mm_container_of(i, mm_coroutine_t, link); if (coroutine->id == id) return coroutine; } mm_list_foreach(&scheduler->list_active, i) { coroutine = mm_container_of(i, mm_coroutine_t, link); if (coroutine->id == id) return coroutine; } return NULL; } void mm_scheduler_set(mm_scheduler_t *scheduler, mm_coroutine_t *coroutine, mm_coroutinestate_t state) { if (coroutine->state == state) return; switch (coroutine->state) { case MM_CNEW: case MM_CFREE: break; case MM_CREADY: scheduler->count_ready--; break; case MM_CACTIVE: scheduler->count_active--; break; } mm_list_t *target = NULL; switch (state) { case MM_CNEW: case MM_CFREE: break; case MM_CREADY: target = &scheduler->list_ready; scheduler->count_ready++; break; case MM_CACTIVE: target = &scheduler->list_active; scheduler->count_active++; break; } mm_list_unlink(&coroutine->link); mm_list_init(&coroutine->link); if (state != MM_CFREE) mm_list_append(target, &coroutine->link); coroutine->state = state; } void mm_scheduler_call(mm_scheduler_t *scheduler, mm_coroutine_t *coroutine) { mm_coroutine_t *resume = scheduler->current; assert(resume != NULL); coroutine->resume = resume; scheduler->current = coroutine; mm_context_swap(&resume->context, &coroutine->context); } void mm_scheduler_yield(mm_scheduler_t *scheduler) { mm_coroutine_t *current = scheduler->current; mm_coroutine_t *resume = current->resume; assert(resume != NULL); scheduler->current = resume; mm_context_swap(¤t->context, &resume->context); } void mm_scheduler_join(mm_coroutine_t *coroutine, mm_coroutine_t *joiner) { mm_list_append(&coroutine->joiners, &joiner->link_join); }