mirror of
https://github.com/libretro/RetroArch.git
synced 2024-12-13 20:33:22 +00:00
116 lines
2.3 KiB
C
116 lines
2.3 KiB
C
/*
|
|
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)
|
|
{
|
|
stack_t stack;
|
|
stack_t old_stack;
|
|
|
|
struct sigaction handler = {{0}};
|
|
struct sigaction old_handler = {{0}};
|
|
|
|
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
|