summaryrefslogtreecommitdiffstats
path: root/src/uctx-setjmp.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/uctx-setjmp.h')
-rw-r--r--src/uctx-setjmp.h109
1 files changed, 109 insertions, 0 deletions
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;
+}