diff options
-rw-r--r-- | DESIGN | 19 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | TFbuild | 7 | ||||
-rw-r--r-- | build/TFbuild.epilogue | 20 | ||||
-rw-r--r-- | build/TFbuild.main | 233 | ||||
-rw-r--r-- | build/TFbuild.prologue | 18 | ||||
-rw-r--r-- | include/libtf/atomic.h | 19 | ||||
-rw-r--r-- | include/libtf/defines.h | 67 | ||||
-rw-r--r-- | include/libtf/fiber.h | 52 | ||||
-rw-r--r-- | include/libtf/list.h | 194 | ||||
-rw-r--r-- | include/libtf/tf.h | 20 | ||||
-rw-r--r-- | src/TFbuild | 5 | ||||
-rw-r--r-- | src/fiber.c | 131 | ||||
-rw-r--r-- | src/uctx-setjmp.h | 109 | ||||
-rw-r--r-- | test/TFbuild | 3 | ||||
-rw-r--r-- | test/simple1.c | 32 |
16 files changed, 937 insertions, 0 deletions
@@ -0,0 +1,19 @@ +SCHEDULER +- 4-heap (or 2-heap) with linked nodes +- epoll +- edge-triggered monitoring for file i/o (no syscalls to modify fdset) +- signalfd for signal handling +- eventfd for thread pool wakeup + +FIBERS +- timer node on fiber control data (for delayed heapify; and reuse on timeouts) +- fd, signal, pid wait struct on stack of wait function +- fiber_kill can interrupt wait + +SCHEDULER <-> THREAD POOL +- scheduler queues to thread pool array fifo queue, + - semaphore protects threads so no underqueueing happens + - eventfd notifies scheduler when queue has free space again + - head/tail updated atomically and item position recovered +- thread pool atomically pushes to resume atomic stack and sets eventfd + - scheduler thread atomically pops all and updates fiber states diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..33859f9 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +## +# Package definitions + +PACKAGE := libtf +VERSION := 0.1 +TFBUILD := build/ + +include $(TFBUILD)TFbuild.main @@ -0,0 +1,7 @@ +subdirs-y += src +subdirs-$(TEST) += test +CFLAGS += -I$(srctree)include + +install: + $(INSTALLDIR) $(DESTDIR)$(DOCDIR) + $(INSTALL) README $(DESTDIR)$(DOCDIR) diff --git a/build/TFbuild.epilogue b/build/TFbuild.epilogue new file mode 100644 index 0000000..af0baad --- /dev/null +++ b/build/TFbuild.epilogue @@ -0,0 +1,20 @@ +# Store per-directory variables +#$(call MemoizeVariables, CFLAGS LDFLAGS LIBS) + +file-vars := $(filter $(foreach m,$(local-vars),$(m)_%),$(.VARIABLES)) +file-vars := $(filter-out $(foreach m,$(local-vars),$(m)_ALL),$(file-vars)) +$(foreach m,$(file-vars),$(eval $(call MemoizeVariable,$(m)))) + +# Handle this directory's rules +$(foreach m,$(libs-y),$(eval $(call CreateLibrary,$(m)))) +$(foreach m,$(progs-y),$(eval $(call CreateProgram,$(m)))) + +# Handle subdir of this subdir +subdirs--$(recursion-level) := $(addsuffix /,$(subdirs-y)) +include $(subdirs-y:%=$(build-prologue) $(current-dir)%/TFbuild $(build-epilogue)) + +# And restore parent context +$(foreach m,$(local-vars),$(eval $(call MemoizeAndPopVariable,$(m)))) + +current-dir := $(parent-dir--$(recursion-level)) +current-dirc := $(parent-dirc--$(recursion-level)) diff --git a/build/TFbuild.main b/build/TFbuild.main new file mode 100644 index 0000000..23ca461 --- /dev/null +++ b/build/TFbuild.main @@ -0,0 +1,233 @@ +# call once recursively so we get rid of default rules and variables +ifndef RECURSIVE + +all: + +Makefile: ; + +%: + @$(MAKE) -rR --no-print-directory RECURSIVE=1 $(MAKECMDGOALS) + +else + +# default target +all: libs progs + @: + +.SECONDEXPANSION: + +# default directories +DESTDIR ?= +SBINDIR ?= /usr/sbin +CONFDIR ?= /etc/$(PACKAGE) +MANDIR ?= /usr/share/man +DOCDIR ?= /usr/share/doc/$(PACKAGE) +STATEDIR ?= /var/run + +# utilities and default flags for them. +CROSS_COMPILE ?= +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +LD := $(CROSS_COMPILE)ld +INSTALL := install +INSTALLDIR := $(INSTALL) -d + +CFLAGS := -Wall -Wstrict-prototypes -D_GNU_SOURCE -std=gnu99 +CFLAGS_ALL ?= -g -O2 +LDFLAGS := $(LDFLAGS) +LDFLAGS_ALL ?= -g + +# some helpers +PHONY:=all libs progs allobjdirs arguments-changed +build-prologue:=$(TFBUILD)TFbuild.prologue +build-epilogue:=$(TFBUILD)TFbuild.epilogue +comma:=, +squote:=' +empty:= +space:=$(empty) $(empty) +separator:=-- + +ifdef V + ifeq ("$(origin V)", "command line") + VERBOSE = $(V) + endif +endif +ifndef VERBOSE + VERBOSE = 0 +endif +ifeq ($(VERBOSE),1) + quiet = + Q = +else + quiet=quiet_ + Q = @ +endif +ifneq ($(findstring s,$(MAKEFLAGS)),) + quiet=silent_ +endif + +# recursion related stuff +srctree := +objtree := obj/ +current-dir := +current-dirc := +recursion-level = $(words $(current-dirc)) +next-recursion-level = $(words $(current-dirc) dummy) +objdir = $(objtree)$(current-dir) + +# globals +all-progs := +all-libs := +local-vars := CFLAGS LDFLAGS LIBS + +# helper macros for targets +escsq = $(subst $(squote),'\$(squote)',$1) +printable-target = $(patsubst $(objtree)%,%,$@) +depfile = $(subst $(comma),_,$(@).d) +#arg-check-prereq = $(if $(arg-check),arguments-changed \ + #$(warning arg-check $(arg-check))\ + #$(warning old $@ - $(cmd_$(@)))\ + #$(warning new $1 - $(cmd_$(1)))\ + #) +arg-check-prereq = $(if $(arg-check),arguments-changed) +arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$(@))) \ + $(filter-out $(cmd_$(@)), $(cmd_$(1))) ) +echo-cmd = $(if $($(quiet)cmd_$(1)),\ + echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';) +make-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1))))) +cmd = @$(echo-cmd) $(cmd_$(1)) +primary_source = $(firstword $^) + +#target-objects2=$(addprefix $(objdir),$(if $(filter-out $(origin $(1)-objs-y),undefined),$($(1)-objs-y),$(1).o)) +#target-objects=$(warning target-objects $(1)=$(target-objects2))$(target-objects2) +target-objects=$(addprefix $(objdir),$(if $(filter-out $(origin $(1)-objs-y),undefined),$($(1)-objs-y),$(1).o)) + +# why - tell all prerequisites that have changed +ifeq ($(VERBOSE),2) +why = - due to $(filter-out $(PHONY),$?) $(filter $(PHONY),$^) +echo-why = $(call escsq, $(strip $(why))) +endif + +# rules for targets +define CreateDirectory +ifeq ($(filter createdir-$(1),$(PHONY)),) +PHONY+=createdir-$(1) +ifneq ($(shell [ -d $(1) ] && echo yes), yes) +createdir-$(1): + $(Q)mkdir -p $(1) +allobjdirs: createdir-$(1) +endif +endif +endef + +define CallRule + @set -e; \ + $(rule_$(1)) +endef + +define ExecuteCommand + @set -e; \ + $(echo-cmd) $(cmd_$(1)); \ + echo 'cmd_$@ := $(make-cmd)' > $(@).cmd +endef + +define MemoizeVariable +ifneq ($($(1)),) +$(current-dir)--$(1):=$($(1)) +$(1):= +endif +endef + +define PushVariable +inherit-$(1)-$(recursion-level):=$($(1)) +endef + +define PopVariable +$(current-dir)--$(1):=$($(1)) +$(1):=$(inherit-$(1)-$(recursion-level)) +endef + +define MemoizeAndPopVariable +$(current-dir)--$(1):=$($(1)) +$(1):=$(inherit-$(1)-$(recursion-level)) +endef + +# GCC C-compiler +c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS_ALL) \ + $($(dir $(printable-target))--CFLAGS) \ + $($(dir $(printable-target))--CFLAGS_$(notdir $(primary_source))) + +quiet_cmd_cc_o_c = CC $(printable-target) + cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $(primary_source) + +define rule_cc_o_c + $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \ + (echo 'cmd_$@ := $(call make-cmd,cc_o_c)'; echo; cat $(depfile)) \ + > $@.cmd ; \ + rm $(depfile) +endef + +$(objtree)%.o: $(srctree)%.c $$(call arg-check-prereq,cc_o_c) |allobjdirs + $(call CallRule,cc_o_c) + +# AR static library archiver +quiet_cmd_ar = AR $(printable-target) + cmd_ar = $(AR) -r $@ $? 2> /dev/null && \ + $(RANLIB) $@ + +define CreateLibrary +all-libs += $(objdir)$(1).a + +$(objdir)$(1).a: $(target-objects) $(LIBS) | createdir-$(objdir) + +-include $(addsuffix .cmd,$(target-objects)) + +$(call CreateDirectory,$(objdir)) +endef + +# Linker +ld_flags = $(LDFLAGS_ALL) \ + $($(dir $(printable-target))--LDFLAGS) \ + $($(dir $(printable-target))--LDFLAGS_$(notdir $(primary_source))) + +quiet_cmd_ld = LD $(printable-target) + cmd_ld = $(CC) $(ld_flags) -o $@ $(filter-out $(PHONY),$^) $($(@D)--LIBS) $($@--LIBS) + +define CreateProgram +all-progs += $(objdir)$(1) + +$(objdir)$(1): $(target-objects) $(LIBS) | createdir-$(objdir) + +-include $(addsuffix .cmd,$(objdir)$(1) $(target-objects)) + +$(call CreateDirectory,$(objdir)) +endef + +# version string +GIT_REV := $(shell test -d .git && git describe 2> /dev/null || echo exported) +ifneq ($(GIT_REV), exported) +FULL_VERSION := $(patsubst v%,%,$(GIT_REV)) +else +FULL_VERSION := $(VERSION) +endif + +# include the main directory's TFbuild +include $(build-prologue) TFbuild $(build-epilogue) + +# debug dump all variables +#$(foreach VAR,$(sort $(.VARIABLES)),$(warning $(VAR)=$($(VAR)))) + +progs: $(all-progs) +libs: $(all-libs) + +$(all-libs): %: + $(call ExecuteCommand,ar) + +$(all-progs): %: $$(call arg-check-prereq,ld) + $(call ExecuteCommand,ld) + +.PHONY: $(PHONY) +$(PHONY): + +endif diff --git a/build/TFbuild.prologue b/build/TFbuild.prologue new file mode 100644 index 0000000..2878ca0 --- /dev/null +++ b/build/TFbuild.prologue @@ -0,0 +1,18 @@ +# Reset all per-directory variables +subdirs-y := +libs-y := +progs-y := + +# Remove this directory as being handled +current-subdir := $(firstword $(subdirs--$(recursion-level))) +subdirs--$(recursion-level) := $(filter-out $(current-subdir),$(subdirs--$(recursion-level))) + +# Traverse down to selected subdir +parent-dir--$(next-recursion-level) := $(current-dir) +parent-dirc--$(next-recursion-level) := $(current-dirc) +current-dir := $(current-dir)$(current-subdir) +current-dirc += $(current-subdir) + +$(foreach m,$(local-vars),$(eval $(call PushVariable,$(m)))) + +#$(warning Enter $(current-dir)) diff --git a/include/libtf/atomic.h b/include/libtf/atomic.h new file mode 100644 index 0000000..ec9f1e0 --- /dev/null +++ b/include/libtf/atomic.h @@ -0,0 +1,19 @@ +/* atomic.h - wrapper for atomic gcc built-ins + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#ifndef TF_ATOMIC_H +#define TF_ATOMIC_H + +#define tf_atomic_inc(var) __sync_add_and_fetch(&(var), 1) +#define tf_atomic_dec(var) __sync_add_and_fetch(&(var), -1) + +#endif diff --git a/include/libtf/defines.h b/include/libtf/defines.h new file mode 100644 index 0000000..7e35ff7 --- /dev/null +++ b/include/libtf/defines.h @@ -0,0 +1,67 @@ +/* tf.h - libtf helper defines + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#ifndef TF_DEFINES_H +#define TF_DEFINES_H + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +#ifndef NULL +#define NULL 0L +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef array_size +#define array_size(array) (sizeof(array) / sizeof((array)[0])) +#endif + +#ifndef offsetof +#define offsetof(type, member) __builtin_offsetof (type, member) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif + +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#define attribute_never_inline __attribute__((noinline)) +#define attribute_weak_function __attribute__((weak)) + +#define TF_BUG_ON(cond) if (cond) { \ + fprintf(stderr, "BUG: failure at %s:%d/%s(): %s!\n", \ + __FILE__, __LINE__, __func__, #cond); \ + abort(); \ +} + +#define TF_ALIGN(size,align) ((((size_t)(size)) + (align)-1) & ~((align)-1)) + +#define TF_EMPTY_ARRAY 0 + +#endif diff --git a/include/libtf/fiber.h b/include/libtf/fiber.h new file mode 100644 index 0000000..48d4924 --- /dev/null +++ b/include/libtf/fiber.h @@ -0,0 +1,52 @@ +/* tf.h - libtf main include + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#ifndef TF_FIBER_H +#define TF_FIBER_H + +#include <errno.h> +#include <libtf/defines.h> +#include <libtf/atomic.h> +#include <libtf/list.h> + +#ifndef TF_STACK_SIZE +#define TF_STACK_SIZE 4096 +#endif + +struct tf_fiber { + unsigned int ref_count; + struct tf_list_node queue_node; + char data[TF_EMPTY_ARRAY]; +}; + +typedef void (*tf_fiber_proc)(void *fiber); + +int tf_main(tf_fiber_proc fiber_main); + +void *tf_fiber_create(tf_fiber_proc fiber_main, int private_size); +void *tf_fiber_get(void *data); +void tf_fiber_put(void *data); + +int tf_schedule(int err); +void tf_kill(void *fiber); + +static inline int tf_yield(void) +{ + return tf_schedule(EAGAIN); +} + +static inline int tf_exit(void) +{ + return tf_schedule(EFAULT); +} + +#endif diff --git a/include/libtf/list.h b/include/libtf/list.h new file mode 100644 index 0000000..22b76a8 --- /dev/null +++ b/include/libtf/list.h @@ -0,0 +1,194 @@ +/* list.h - libtf single and double linked list macros + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + * + * This based to some degree to the code in the linux kernel. This is subset + * of the functionality, and tf_list has separate head and node types. + */ + +#ifndef TF_LIST_H +#define TF_LIST_H + +#include <libtf/defines.h> + +struct tf_hlist_head { + struct tf_hlist_node *first; +}; + +struct tf_hlist_node { + struct tf_hlist_node *next; + struct tf_hlist_node **pprev; +}; + +static inline int tf_hlist_empty(const struct tf_hlist_head *h) +{ + return !h->first; +} + +static inline int tf_hlist_hashed(const struct tf_hlist_node *n) +{ + return n->pprev != NULL; +} + +static inline void tf_hlist_del(struct tf_hlist_node *n) +{ + *n->pprev = n->next; + n->next = NULL; + n->pprev = NULL; +} + +static inline void tf_hlist_add_head(struct tf_hlist_node *n, struct tf_hlist_head *h) +{ + struct tf_hlist_node *first = h->first; + + n->next = first; + n->pprev = &h->first; + h->first = n; +} + +static inline void tf_hlist_add_after(struct tf_hlist_node *n, struct tf_hlist_node *prev) +{ + n->next = prev->next; + n->pprev = &prev->next; + prev->next = n; +} + +static inline struct tf_hlist_node **tf_hlist_tail_ptr(struct tf_hlist_head *h) +{ + struct tf_hlist_node *n = h->first; + if (n == NULL) + return &h->first; + while (n->next != NULL) + n = n->next; + return &n->next; +} + +#define tf_hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define tf_hlist_for_each(pos, head) \ + for (pos = (head)->first; pos; pos = pos->next) + +#define tf_hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); pos = n) + +#define tf_hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; pos && \ + ({ tpos = tf_hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +#define tf_hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = tf_hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + + +struct tf_list_node { + struct tf_list_node *next, *prev; +}; + +struct tf_list_head { + struct tf_list_node node; +}; + +#define TF_LIST_INITIALIZER(l) { .next = &l, .prev = &l } +#define TF_LIST_HEAD_INITIALIZER(l) { .node = TF_LIST_INITIALIZER((l).node) } + +static inline void tf_list_init(struct tf_list_node *list) +{ + list->next = list; + list->prev = list; +} + +static inline void tf_list_init_head(struct tf_list_head *head) +{ + tf_list_init(&head->node); +} + +static inline void __tf_list_add(struct tf_list_node *new, + struct tf_list_node *prev, + struct tf_list_node *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void tf_list_add_after(struct tf_list_node *new, struct tf_list_node *head) +{ + __tf_list_add(new, head, head->next); +} + +static inline void tf_list_add_before(struct tf_list_node *new, struct tf_list_node *head) +{ + __tf_list_add(new, head->prev, head); +} + +static inline void tf_list_add_head(struct tf_list_node *new, struct tf_list_head *head) +{ + tf_list_add_after(new, &head->node); +} + +static inline void tf_list_add_tail(struct tf_list_node *new, struct tf_list_head *head) +{ + tf_list_add_before(new, &head->node); +} + +static inline void __tf_list_del(struct tf_list_node * prev, struct tf_list_node *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void tf_list_del(struct tf_list_node *entry) +{ + __tf_list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +static inline int tf_list_hashed(const struct tf_list_node *n) +{ + return n->next != n && n->next != NULL; +} + +static inline int tf_list_empty(const struct tf_list_head *h) +{ + return !tf_list_hashed(&h->node); +} + +#define tf_list_next(ptr, type, member) \ + (tf_list_hashed(ptr) ? container_of((ptr)->next,type,member) : NULL) + +#define tf_list_first(head, type, member) \ + tf_list_next(&((head)->node), type, member) + +#define tf_list_entry(ptr, type, member) container_of(ptr,type,member) + +#define tf_list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define tf_list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#define tf_list_for_each_entry(pos, head, member) \ + for (pos = tf_list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = tf_list_entry(pos->member.next, typeof(*pos), member)) + +#define tf_list_for_each_entry_safe(pos, n, head, member) \ + for (pos = tf_list_entry((head)->next, typeof(*pos), member), \ + n = tf_list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = tf_list_entry(n->member.next, typeof(*n), member)) + +#endif diff --git a/include/libtf/tf.h b/include/libtf/tf.h new file mode 100644 index 0000000..7ff7b25 --- /dev/null +++ b/include/libtf/tf.h @@ -0,0 +1,20 @@ +/* tf.h - libtf umbrella include + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#ifndef TF_H +#define TF_H + +#define TF_UCTX_H "uctx-setjmp.h" + +#include <libtf/fiber.h> + +#endif diff --git a/src/TFbuild b/src/TFbuild new file mode 100644 index 0000000..211b734 --- /dev/null +++ b/src/TFbuild @@ -0,0 +1,5 @@ +libs-y += libtf + +libtf-objs-y += fiber.o dheap.o + +CFLAGS_dheap.c += -funroll-all-loops diff --git a/src/fiber.c b/src/fiber.c new file mode 100644 index 0000000..0db2984 --- /dev/null +++ b/src/fiber.c @@ -0,0 +1,131 @@ +/* fiber.c - fiber management and scheduling + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ +#include <errno.h> +#include <libtf/tf.h> +#include TF_UCTX_H + +struct tf_scheduler { + struct tf_list_head run_q; + struct tf_list_head sleep_q; + + struct tf_fiber * active_fiber; + int num_fibers; +}; + +/* FIXME: should be in thread local storage */ +static struct tf_scheduler *__scheduler; + +void *tf_fiber_create(tf_fiber_proc fiber_main, int private_size) +{ + struct tf_scheduler *sched = __scheduler; + struct tf_fiber *fiber; + + fiber = tf_uctx_create(fiber_main, private_size); + + /* The initial references for caller and scheduler */ + *fiber = (struct tf_fiber) { + .ref_count = 2, + .queue_node = TF_LIST_INITIALIZER(fiber->queue_node), + }; + + tf_list_add_tail(&fiber->queue_node, &sched->run_q); + sched->num_fibers++; + + return fiber->data; +} + +void __tf_fiber_destroy(struct tf_fiber *fiber) +{ + tf_uctx_destroy(fiber); +} + +void *tf_fiber_get(void *data) +{ + struct tf_fiber *fiber = container_of(data, struct tf_fiber, data); + tf_atomic_inc(fiber->ref_count); + return data; +} + +void tf_fiber_put(void *data) +{ + struct tf_fiber *fiber = container_of(data, struct tf_fiber, data); + if (tf_atomic_dec(fiber->ref_count) == 0) + __tf_fiber_destroy(fiber); +} + +static void run_fiber(void) +{ + struct tf_scheduler *sched = __scheduler; + struct tf_fiber *schedf = container_of((void*) __scheduler, struct tf_fiber, data); + struct tf_fiber *f; + + if (tf_list_empty(&sched->run_q)) + return; + + f = tf_list_first(&sched->run_q, struct tf_fiber, queue_node); + tf_list_del(&f->queue_node); + + sched->active_fiber = f; + switch (tf_uctx_transfer(schedf, f, 1)) { + case EFAULT: /* Fiber is dead */ + tf_fiber_put(f->data); + sched->num_fibers--; + break; + case EAGAIN: /* Yielded, reshedule */ + tf_list_add_tail(&f->queue_node, &sched->run_q); + break; + case EIO: /* Blocked, in sleep */ + tf_list_add_tail(&f->queue_node, &sched->sleep_q); + break; + default: + TF_BUG_ON("bad scheduler call from fiber"); + } +} + +int tf_main(tf_fiber_proc main_fiber) +{ + struct tf_uctx *ctx = alloca(sizeof(struct tf_uctx) + sizeof(struct tf_scheduler)); + struct tf_scheduler *sched = (struct tf_scheduler*) ctx->fiber.data; + int stack_guard = STACK_GUARD; + + ctx->stack_guard = &stack_guard; + *sched = (struct tf_scheduler){ + .run_q = TF_LIST_HEAD_INITIALIZER(sched->run_q), + .sleep_q = TF_LIST_HEAD_INITIALIZER(sched->sleep_q), + }; + __scheduler = sched; + tf_fiber_put(tf_fiber_create(main_fiber, 0)); + do { + run_fiber(); + } while (likely(sched->num_fibers)); + __scheduler = NULL; + + return 0; +} + +int tf_schedule(int err) +{ + struct tf_scheduler *sched = __scheduler; + struct tf_fiber *schedf = container_of((void*) __scheduler, struct tf_fiber, data); + struct tf_fiber *f = sched->active_fiber; + int r; + + r = tf_uctx_transfer(f, schedf, err); + if (r == 1) + return 0; + + return r; +} + +void tf_kill(void *fiber) +{ +} diff --git a/src/uctx-setjmp.h b/src/uctx-setjmp.h new file mode 100644 index 0000000..33e187e --- /dev/null +++ b/src/uctx-setjmp.h @@ -0,0 +1,109 @@ +/* uctx-setjmp.h - setjmp based user-space context switching + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#include <libtf/fiber.h> + +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#ifdef VALGRIND +#include <valgrind/valgrind.h> +#endif + +#define STACK_GUARD 0xbad57ac4 + +struct tf_uctx { + int *stack_guard; + void *alloc; + jmp_buf jmpbuf; +#ifdef VALGRIND + unsigned int stack_id; +#endif + struct tf_fiber fiber; +}; + +#define set_stack(ptr) ({ __asm__("movl %0, %%esp" : : "r"(ptr)); }) +#define get_stack() ({ void *ptr; __asm__("movl %%esp, %0" : "=r"(ptr)); ptr; }) + +attribute_never_inline +static void *tf_uctx_start(tf_fiber_proc fiber_main, size_t private_size) +{ + struct tf_uctx *uctx; + + uctx = alloca(private_size); + if (setjmp(uctx->jmpbuf) != 0) { + fiber_main(uctx->fiber.data); + tf_exit(); + } + return uctx; +} + +static inline +struct tf_fiber *tf_uctx_create(tf_fiber_proc fiber_main, int private_size) +{ + size_t size = TF_STACK_SIZE; + struct tf_uctx *uctx; + void *stack, *old_stack, *stack_head, *stack_tail; +#ifdef VALGRIND + int stack_id; +#endif + + stack = malloc(size); + if (stack == NULL) + return NULL; +#ifdef VALGRIND + stack_id = VALGRIND_STACK_REGISTER(stack, size); +#endif + + /* Assumes stack grows up */ + stack_head = stack + size - 8; + stack_tail = stack; + + old_stack = get_stack(); + set_stack(stack_head); + uctx = tf_uctx_start(fiber_main, private_size + sizeof(struct tf_uctx)); + set_stack(old_stack); + +#ifdef VALGRIND + uctx->stack_id = stack_id; +#endif + uctx->alloc = stack; + uctx->stack_guard = stack_tail; + *uctx->stack_guard = STACK_GUARD; + + return &uctx->fiber; +} + +static inline +void tf_uctx_destroy(struct tf_fiber *fiber) +{ + struct tf_uctx *uctx = container_of(fiber, struct tf_uctx, fiber); +#ifdef VALGRIND + VALGRIND_STACK_DEREGISTER(uctx->stack_id); +#endif + free(uctx->alloc); +} + +static inline +int tf_uctx_transfer(struct tf_fiber *from, struct tf_fiber *to, int value) +{ + struct tf_uctx *ufrom = container_of(from, struct tf_uctx, fiber); + struct tf_uctx *uto = container_of(to, struct tf_uctx, fiber); + int r; + + TF_BUG_ON(unlikely(*ufrom->stack_guard != STACK_GUARD)); + + r = setjmp(ufrom->jmpbuf); + if (r == 0) + longjmp(uto->jmpbuf, value); + return r; +} diff --git a/test/TFbuild b/test/TFbuild new file mode 100644 index 0000000..d2648ed --- /dev/null +++ b/test/TFbuild @@ -0,0 +1,3 @@ +progs-$(TEST) += simple1 sleep + +LIBS += $(objtree)src/libtf.a diff --git a/test/simple1.c b/test/simple1.c new file mode 100644 index 0000000..65a787c --- /dev/null +++ b/test/simple1.c @@ -0,0 +1,32 @@ +#include <libtf/tf.h> +#include <stdio.h> + +struct ctx { + int id; +}; + +static void work_fiber(void *ptr) +{ + struct ctx *c = (struct ctx*) ptr; + + printf("Hello%d.1\n", c->id); + tf_yield(); + printf("Hello%d.2\n", c->id); +} + +static void init_fiber(void *ptr) +{ + struct ctx *c; + int i; + + for (i = 0; i < 6; i++) { + c = tf_fiber_create(work_fiber, sizeof(struct ctx)); + c->id = i; + tf_fiber_put(c); + } +} + +int main(int argc, char **argv) +{ + return tf_main(init_fiber); +} |