diff --git a/Makefile b/Makefile index e22d435..f4fb6a2 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,8 @@ LIBUCONTEXT_STATIC_PATH = ${LIBDIR}/${LIBUCONTEXT_STATIC_NAME} LIBUCONTEXT_HEADERS = \ include/libucontext/libucontext.h \ include/libucontext/bits.h +LIBUCONTEXT_EXAMPLES = \ + examples/cooperative_threading all: ${LIBUCONTEXT_SONAME} ${LIBUCONTEXT_STATIC_NAME} ${LIBUCONTEXT_PC} @@ -98,6 +100,10 @@ check: test_libucontext ${LIBUCONTEXT_SONAME} test_libucontext: test_libucontext.c ${LIBUCONTEXT_NAME} $(CC) -std=c99 -D_BSD_SOURCE ${CFLAGS} ${CPPFLAGS} $@.c -o $@ -L. -lucontext +examples: ${LIBUCONTEXT_EXAMPLES} +examples/cooperative_threading: examples/cooperative_threading.c ${LIBUCONTEXT_NAME} + $(CC) -std=c99 -D_BSD_SOURCE ${CFLAGS} ${CPPFLAGS} $@.c -o $@ -L. -lucontext + ifeq ($(FREESTANDING),no) include/libucontext/bits.h: arch/common/bits.h diff --git a/examples/cooperative_threading.c b/examples/cooperative_threading.c new file mode 100644 index 0000000..123e9ea --- /dev/null +++ b/examples/cooperative_threading.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include + +libucontext_ucontext_t mainctx = {}; +libucontext_ucontext_t *curthr = &mainctx; +libucontext_ucontext_t *threads = NULL; +size_t thrcount = 0; + +void +yieldto(libucontext_ucontext_t *target) +{ + libucontext_ucontext_t *oldthr = curthr; + curthr = target; + + libucontext_swapcontext(oldthr, curthr); +} + +void +yield(void) +{ + libucontext_ucontext_t *newthr; + + /* we set uc_flags to non-zero to signal thread completion. */ + do + newthr = &threads[random() % thrcount]; + while (newthr == curthr || newthr->uc_flags); + + srandom(time(NULL)); + + yieldto(newthr); +} + +void +worker(size_t multiple) +{ + size_t accum = 1; + + for (size_t i = 0; i < 10; i++) + { + accum += (multiple * i); + + printf("[%p] accumulated %zu\n", curthr, accum); + yield(); + } + + /* mark thread as completed, so we don't return here */ + curthr->uc_flags = 1; +} + +void +create(size_t multiple) +{ + libucontext_ucontext_t *cursor; + + thrcount += 1; + threads = realloc(threads, sizeof(*threads) * thrcount); + + cursor = &threads[thrcount - 1]; + memset(cursor, '\0', sizeof *cursor); + + /* initialize the new thread's values to our current context */ + libucontext_getcontext(cursor); + + /* set up uc_link */ + cursor->uc_link = thrcount > 1 ? &threads[thrcount - 2] : &mainctx; + + /* set up a stack */ + cursor->uc_stack.ss_size = 8192; + cursor->uc_stack.ss_sp = calloc(1, cursor->uc_stack.ss_size); + + /* set up the function call */ + libucontext_makecontext(cursor, worker, 1, multiple); +} + +int +main(int argc, const char *argv[]) +{ + srandom(time(NULL)); + + libucontext_getcontext(&mainctx); + + for (size_t i = 1; i < 4; i++) + create(i); + + /* start the threads off by yielding to the last one */ + yieldto(&threads[thrcount - 1]); + + return EXIT_SUCCESS; +}