2016-11-08 12:07:09 +00:00
|
|
|
|
|
|
|
/*
|
2016-11-08 14:53:52 +00:00
|
|
|
* flint.
|
2016-11-08 12:07:09 +00:00
|
|
|
*
|
|
|
|
* Cooperative multitasking engine.
|
|
|
|
*/
|
|
|
|
|
2016-11-08 14:53:52 +00:00
|
|
|
#include <flint_private.h>
|
2016-11-08 12:07:09 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
ft_scheduler_main(void *arg)
|
|
|
|
{
|
|
|
|
ftfiber *fiber = arg;
|
|
|
|
ftscheduler *s = fiber->scheduler;
|
2016-11-17 10:03:42 +00:00
|
|
|
ft *f = fiber->data;
|
2016-11-08 12:07:09 +00:00
|
|
|
ft_scheduler_set(fiber, FT_FACTIVE);
|
2016-11-17 12:35:56 +00:00
|
|
|
|
2016-11-08 12:07:09 +00:00
|
|
|
fiber->function(fiber->arg);
|
2016-11-17 10:03:42 +00:00
|
|
|
ft_wakeup_waiters(f, fiber);
|
2016-11-17 12:35:56 +00:00
|
|
|
|
|
|
|
ft_fiber_timer_stop(fiber);
|
2016-11-08 12:07:09 +00:00
|
|
|
ft_scheduler_set(fiber, FT_FFREE);
|
|
|
|
ft_scheduler_yield(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ft_scheduler_init(ftscheduler *s, int size_stack, void *data)
|
|
|
|
{
|
|
|
|
ft_listinit(&s->list_ready);
|
|
|
|
ft_listinit(&s->list_active);
|
|
|
|
ft_listinit(&s->list_free);
|
|
|
|
s->count_ready = 0;
|
|
|
|
s->count_active = 0;
|
|
|
|
s->count_free = 0;
|
|
|
|
s->size_stack = size_stack;
|
|
|
|
s->data = data;
|
|
|
|
s->seq = 1;
|
|
|
|
ft_fiber_init(&s->main);
|
|
|
|
ft_context_init_main(&s->main.context);
|
|
|
|
s->current = &s->main;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ft_scheduler_free(ftscheduler *s)
|
|
|
|
{
|
|
|
|
ftfiber *fiber;
|
|
|
|
ftlist *i, *p;
|
|
|
|
ft_listforeach_safe(&s->list_ready, i, p) {
|
|
|
|
fiber = ft_container_of(i, ftfiber, link);
|
|
|
|
ft_fiber_free(fiber);
|
|
|
|
}
|
|
|
|
ft_listforeach_safe(&s->list_active, i, p) {
|
|
|
|
fiber = ft_container_of(i, ftfiber, link);
|
|
|
|
ft_fiber_free(fiber);
|
|
|
|
}
|
|
|
|
ft_listforeach_safe(&s->list_free, i, p) {
|
|
|
|
fiber = ft_container_of(i, ftfiber, link);
|
|
|
|
ft_fiber_free(fiber);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ftfiber*
|
|
|
|
ft_scheduler_new(ftscheduler *s, ftfiberf function, void *arg)
|
|
|
|
{
|
|
|
|
ftfiber *fiber;
|
|
|
|
if (s->count_free) {
|
|
|
|
fiber = ft_container_of(s->list_free.next, ftfiber, link);
|
|
|
|
assert(fiber->state == FT_FFREE);
|
2016-11-17 10:03:42 +00:00
|
|
|
ft_listinit(&fiber->link_wait);
|
|
|
|
ft_listinit(&fiber->waiters);
|
2016-11-17 12:35:56 +00:00
|
|
|
ft_fiber_opend(fiber);
|
2016-11-08 12:07:09 +00:00
|
|
|
} else {
|
|
|
|
fiber = ft_fiber_alloc(s->size_stack);
|
|
|
|
if (fiber == NULL)
|
|
|
|
return NULL;
|
|
|
|
fiber->scheduler = s;
|
|
|
|
}
|
2016-11-14 15:32:13 +00:00
|
|
|
ft_context_init(&fiber->context,
|
|
|
|
ft_fiber_stackof(fiber),
|
|
|
|
s->size_stack,
|
|
|
|
&s->main.context,
|
|
|
|
ft_scheduler_main,
|
|
|
|
fiber);
|
2016-11-08 12:07:09 +00:00
|
|
|
fiber->id = s->seq++;
|
|
|
|
fiber->function = function;
|
|
|
|
fiber->arg = arg;
|
|
|
|
fiber->data = NULL;
|
|
|
|
ft_scheduler_set(fiber, FT_FREADY);
|
|
|
|
return fiber;
|
|
|
|
}
|
|
|
|
|
2016-11-17 10:03:42 +00:00
|
|
|
ftfiber*
|
|
|
|
ft_scheduler_match(ftscheduler *s, uint64_t id)
|
|
|
|
{
|
|
|
|
ftfiber *fiber;
|
|
|
|
ftlist *i;
|
|
|
|
ft_listforeach(&s->list_active, i) {
|
|
|
|
fiber = ft_container_of(i, ftfiber, link);
|
|
|
|
if (fiber->id == id)
|
|
|
|
return fiber;
|
|
|
|
}
|
|
|
|
ft_listforeach(&s->list_ready, i) {
|
|
|
|
fiber = ft_container_of(i, ftfiber, link);
|
|
|
|
if (fiber->id == id)
|
|
|
|
return fiber;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-11-08 12:07:09 +00:00
|
|
|
void
|
|
|
|
ft_scheduler_set(ftfiber *fiber, ftfiberstate state)
|
|
|
|
{
|
|
|
|
ftscheduler *s = fiber->scheduler;
|
|
|
|
if (fiber->state == state)
|
|
|
|
return;
|
|
|
|
switch (fiber->state) {
|
|
|
|
case FT_FNEW: break;
|
|
|
|
case FT_FREADY:
|
|
|
|
s->count_ready--;
|
|
|
|
break;
|
|
|
|
case FT_FACTIVE:
|
|
|
|
s->count_active--;
|
|
|
|
break;
|
|
|
|
case FT_FFREE:
|
|
|
|
s->count_free--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ftlist *target = NULL;
|
|
|
|
switch (state) {
|
|
|
|
case FT_FNEW: break;
|
|
|
|
case FT_FREADY:
|
|
|
|
target = &s->list_ready;
|
|
|
|
s->count_ready++;
|
|
|
|
break;
|
|
|
|
case FT_FACTIVE:
|
|
|
|
target = &s->list_active;
|
|
|
|
s->count_active++;
|
|
|
|
break;
|
|
|
|
case FT_FFREE:
|
|
|
|
target = &s->list_free;
|
|
|
|
s->count_free++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ft_listunlink(&fiber->link);
|
|
|
|
ft_listinit(&fiber->link);
|
|
|
|
ft_listappend(target, &fiber->link);
|
|
|
|
fiber->state = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ft_scheduler_call(ftfiber *fiber)
|
|
|
|
{
|
|
|
|
ftscheduler *s = fiber->scheduler;
|
|
|
|
ftfiber *resume = s->current;
|
|
|
|
assert(resume != NULL);
|
|
|
|
fiber->resume = resume;
|
|
|
|
s->current = fiber;
|
|
|
|
ft_context_swap(&resume->context, &fiber->context);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ft_scheduler_yield(ftscheduler *s)
|
|
|
|
{
|
|
|
|
ftfiber *current = s->current;
|
|
|
|
ftfiber *resume = current->resume;
|
|
|
|
assert(resume != NULL);
|
|
|
|
s->current = resume;
|
|
|
|
ft_context_swap(¤t->context, &resume->context);
|
|
|
|
}
|
2016-11-17 10:03:42 +00:00
|
|
|
|
|
|
|
void ft_scheduler_wait(ftfiber *fiber, ftfiber *waiter)
|
|
|
|
{
|
|
|
|
ft_listappend(&fiber->waiters, &waiter->link_wait);
|
|
|
|
}
|