summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DESIGN19
-rw-r--r--Makefile8
-rw-r--r--TFbuild7
-rw-r--r--build/TFbuild.epilogue20
-rw-r--r--build/TFbuild.main233
-rw-r--r--build/TFbuild.prologue18
-rw-r--r--include/libtf/atomic.h19
-rw-r--r--include/libtf/defines.h67
-rw-r--r--include/libtf/fiber.h52
-rw-r--r--include/libtf/list.h194
-rw-r--r--include/libtf/tf.h20
-rw-r--r--src/TFbuild5
-rw-r--r--src/fiber.c131
-rw-r--r--src/uctx-setjmp.h109
-rw-r--r--test/TFbuild3
-rw-r--r--test/simple1.c32
16 files changed, 937 insertions, 0 deletions
diff --git a/DESIGN b/DESIGN
new file mode 100644
index 0000000..2ffc535
--- /dev/null
+++ b/DESIGN
@@ -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
diff --git a/TFbuild b/TFbuild
new file mode 100644
index 0000000..7bfde59
--- /dev/null
+++ b/TFbuild
@@ -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);
+}