diff --git a/Makefile b/Makefile index 2eb9a50..ec78759 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,10 @@ $(LIB): $(OBJS) testyield: testyield.c $(LIB) $(CC) $(CFLAGS) -o $@ testyield.c $(LIB) -test: testyield +testdelay: testdelay.c $(LIB) + $(CC) $(CFLAGS) -o $@ testdelay.c $(LIB) + +test: testyield testdelay clean: rm -f $(OBJS) diff --git a/coio.c b/coio.c index b3d3ef1..004ee58 100644 --- a/coio.c +++ b/coio.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include +#include #include "coioimpl.h" #include "coro.h" @@ -22,8 +23,37 @@ static coro_context _sched_ctx; static unsigned long _taskcount = 0; CoioTaskList coio_ready = {0, 0}; +CoioTaskList coio_sleeping = {0, 0}; CoioTask *coio_current; +static void +_process_events() +{ + uvlong now; + int ms = 5; + CoioTask *t; + + if ((t = coio_sleeping.head) != NULL && t->timeout != 0) { + now = coio_now(); + if (now >= t->timeout) { + ms = 0; + } else { + ms = (t->timeout - now) / 1000000; + } + } + /* TODO:do I/O polling instead of usleep */ + usleep(ms * 1000); + + /* handle CLOCK_MONOTONIC bugs (VirtualBox anyone?) */ + while (!coio_ready.head) { + /* wake up timed out tasks */ + now = coio_now(); + while ((t = coio_sleeping.head) && now >= t->timeout) { + coio_rdy(t); + } + } +} + int coio_main() { @@ -32,6 +62,9 @@ coio_main() /* scheduler mainloop */ for (;;) { + if (!coio_ready.head && coio_sleeping.head) + _process_events(); + if (!coio_ready.head) break; @@ -88,11 +121,55 @@ coio_create(coio_func f, void *arg, unsigned int stacksize) return 0; } +uvlong +coio_timeout(CoioTask * task, int ms) +{ + CoioTask *t; + + if (ms > 0) + task->timeout = coio_now() + (ms * 1000000); + + for (t = coio_sleeping.head; t != NULL && t->timeout && t->timeout < task->timeout; t = t->next); + + if (t) { + task->prev = t->prev; + task->next = t; + } else { + task->prev = coio_sleeping.tail; + task->next = NULL; + } + + t = coio_current; + + if (t->prev) { + t->prev->next = t; + } else { + coio_sleeping.head = t; + } + + if (t->next) { + t->next->prev = t; + } else { + coio_sleeping.tail = t; + } + + return task->timeout; +} + +int +coio_delay(int ms) +{ + uvlong when; + when = coio_timeout(coio_current, ms); + coio_transfer(); + return (coio_now() - when) / 1000000; +} + void coio_yield() { - coio_add(&coio_ready, coio_current); - coro_transfer(&coio_current->ctx, &_sched_ctx); + coio_rdy(coio_current); + coio_transfer(); } void @@ -124,3 +201,28 @@ coio_del(CoioTaskList * lst, CoioTask * task) lst->tail = task->prev; } } + +void +coio_rdy(CoioTask * task) +{ + task->timeout = 0; + coio_del(&coio_sleeping, task); + coio_add(&coio_ready, task); +} + +void +coio_transfer() +{ + coro_transfer(&coio_current->ctx, &_sched_ctx); +} + +uvlong +coio_now() +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) + return -1; + + return (uvlong) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; +} diff --git a/coio.h b/coio.h index f5d6026..20a7e6c 100644 --- a/coio.h +++ b/coio.h @@ -22,10 +22,13 @@ extern "C" { typedef struct CoioTask CoioTask; typedef void (*coio_func) (void *arg); + typedef unsigned long long uvlong; int coio_main (); - int coio_create(void (*f) (void *arg), void *arg, unsigned int stacksize); + int coio_create(coio_func f, void *arg, unsigned int stacksize); void coio_yield(); + uvlong coio_now(); + int coio_delay(int ms); #ifdef __cplusplus } diff --git a/coioimpl.h b/coioimpl.h index f3d22ec..0acd967 100644 --- a/coioimpl.h +++ b/coioimpl.h @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef COIOIMPL_H -#define COIOIMPL_ +#define COIOIMPL_H #include "coro.h" #include "coio.h" @@ -28,6 +28,7 @@ struct CoioTask { coio_func func; void *arg; + uvlong timeout; int done; /* linked list support */ @@ -40,7 +41,13 @@ struct CoioTaskList { CoioTask *tail; }; +extern CoioTaskList coio_ready; +extern CoioTaskList coio_sleeping; +extern CoioTask *coio_current; + void coio_add (CoioTaskList * lst, CoioTask * task); void coio_del (CoioTaskList * lst, CoioTask * task); +void coio_rdy (CoioTask * task); +void coio_transfer(); #endif diff --git a/testdelay.c b/testdelay.c new file mode 100644 index 0000000..eb42587 --- /dev/null +++ b/testdelay.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015 Moritz Bitsch + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include "coio.h" + +void +_t1(void *arg) +{ + printf("going to sleep 1000ms (1s)\n"); + coio_delay(1000); + printf("woken up\n"); +} + +int +main(int argc, char **argv) +{ + (void) argc; + (void) argv; + + coio_create(_t1, NULL, 0x8000); + + if (coio_main() < 0) { + printf("Deadlocked\n"); + return 1; + } + return 0; +}