summaryrefslogtreecommitdiff
path: root/libco/sjlj.c
diff options
context:
space:
mode:
authoraliaspider2014-12-08 22:56:33 +0100
committeraliaspider2014-12-08 22:56:33 +0100
commit365077772d3d8d5d479febd0388d7e74ef08d508 (patch)
tree4021590cd2176f8e9d469ab3d620338e9ed85fda /libco/sjlj.c
parent35e1e44a1107c73a3aaccb0eff44757c602afdac (diff)
downloadpicogpsp-365077772d3d8d5d479febd0388d7e74ef08d508.tar.gz
picogpsp-365077772d3d8d5d479febd0388d7e74ef08d508.tar.bz2
picogpsp-365077772d3d8d5d479febd0388d7e74ef08d508.zip
start implementing the libretro interface. (not working yet)
Diffstat (limited to 'libco/sjlj.c')
-rw-r--r--libco/sjlj.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/libco/sjlj.c b/libco/sjlj.c
new file mode 100644
index 0000000..f074714
--- /dev/null
+++ b/libco/sjlj.c
@@ -0,0 +1,115 @@
+/*
+ libco.sjlj (2008-01-28)
+ author: Nach
+ license: public domain
+*/
+
+/*
+ * Note this was designed for UNIX systems. Based on ideas expressed in a paper
+ * by Ralf Engelschall.
+ * For SJLJ on other systems, one would want to rewrite springboard() and
+ * co_create() and hack the jmb_buf stack pointer.
+ */
+
+#define LIBCO_C
+#include <libco.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ sigjmp_buf context;
+ void (*coentry)(void);
+ void *stack;
+} cothread_struct;
+
+static thread_local cothread_struct co_primary;
+static thread_local cothread_struct *creating, *co_running = 0;
+
+static void springboard(int ignored)
+{
+ if(sigsetjmp(creating->context, 0))
+ co_running->coentry();
+}
+
+cothread_t co_active(void)
+{
+ if (!co_running)
+ co_running = &co_primary;
+ return (cothread_t)co_running;
+}
+
+cothread_t co_create(unsigned int size, void (*coentry)(void))
+{
+ if(!co_running)
+ co_running = &co_primary;
+
+ cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct));
+
+ if(thread)
+ {
+ struct sigaction handler;
+ struct sigaction old_handler;
+
+ stack_t stack;
+ stack_t old_stack;
+
+ thread->coentry = thread->stack = 0;
+
+ stack.ss_flags = 0;
+ stack.ss_size = size;
+ thread->stack = stack.ss_sp = malloc(size);
+
+ if(stack.ss_sp && !sigaltstack(&stack, &old_stack))
+ {
+ handler.sa_handler = springboard;
+ handler.sa_flags = SA_ONSTACK;
+ sigemptyset(&handler.sa_mask);
+ creating = thread;
+
+ if(!sigaction(SIGUSR1, &handler, &old_handler))
+ {
+ if(!raise(SIGUSR1))
+ thread->coentry = coentry;
+ sigaltstack(&old_stack, 0);
+ sigaction(SIGUSR1, &old_handler, 0);
+ }
+ }
+
+ if(thread->coentry != coentry)
+ {
+ co_delete(thread);
+ thread = 0;
+ }
+ }
+
+ return (cothread_t)thread;
+}
+
+void co_delete(cothread_t cothread)
+{
+ if(cothread)
+ {
+ if(((cothread_struct*)cothread)->stack)
+ free(((cothread_struct*)cothread)->stack);
+ free(cothread);
+ }
+}
+
+void co_switch(cothread_t cothread)
+{
+ if(!sigsetjmp(co_running->context, 0))
+ {
+ co_running = (cothread_struct*)cothread;
+ siglongjmp(co_running->context, 1);
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif