Created
August 3, 2009 20:26
-
-
Save Arachnid/160808 to your computer and use it in GitHub Desktop.
Simple fiber library. Compile with: gcc -o eepy.o -g -c eepy.c; gcc -o test -lpthread -g test.c eepy.o
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <pthread.h> | |
#include <setjmp.h> | |
#include <signal.h> | |
#include <stdlib.h> | |
#include "eepy.h" | |
// Information about an active eepy process | |
typedef struct eepy_process { | |
jmp_buf context; | |
stack_t stack; | |
volatile int state; | |
int free_stack; | |
void (*start)(void); | |
struct eepy_process *next; | |
} eepy_process; | |
typedef struct { | |
eepy_process *current; | |
eepy_process *prev; | |
} eepy_thread_state; | |
struct { | |
volatile int initialized; | |
malloc_func *alloc; | |
free_func *free; | |
eepy_process *runnable; | |
eepy_process *last_runnable; | |
// Key for per-thread current process info | |
pthread_key_t threadkey; | |
// Used to lock and signal the runnable list | |
pthread_cond_t runnable_cond; | |
pthread_mutex_t runnable_lock; | |
// Prevents multiple concurrent process creations | |
pthread_mutex_t spawn_lock; | |
eepy_process *spawn_process; | |
} state; | |
void _eepy_restore(eepy_thread_state *threadstate, int nowait) { | |
pthread_mutex_lock(&state.runnable_lock); | |
if(nowait && state.runnable == NULL) { | |
pthread_mutex_unlock(&state.runnable_lock); | |
threadstate->current = threadstate->prev; | |
threadstate->prev = NULL; | |
threadstate->current->state = EEPY_RUNNING; | |
return; | |
} | |
while(state.runnable == NULL) | |
// Nothing to run, wait until there is | |
pthread_cond_wait(&state.runnable_cond, &state.runnable_lock); | |
// Pop the oldest runnable off the stack | |
threadstate->current = state.runnable; | |
state.runnable = state.runnable->next; | |
pthread_mutex_unlock(&state.runnable_lock); | |
threadstate->current->state = EEPY_RUNNING; | |
longjmp(threadstate->current->context, 1); | |
} | |
void _eepy_runqueue_add(eepy_process *process) { | |
pthread_mutex_lock(&state.runnable_lock); | |
process->next = NULL; | |
if(state.runnable != NULL) { | |
state.last_runnable->next = process; | |
state.last_runnable = process; | |
} else { | |
state.runnable = process; | |
state.last_runnable = process; | |
// Unblock someone waiting for something to run | |
pthread_cond_signal(&state.runnable_cond); | |
} | |
pthread_mutex_unlock(&state.runnable_lock); | |
} | |
void _eepy_onresume(eepy_thread_state *threadstate) { | |
if(threadstate->prev != NULL) { | |
// Clean up after the previous work unit | |
switch(threadstate->prev->state) { | |
case EEPY_STOPPED: | |
// Store the previous process to the end of the runqueue | |
_eepy_runqueue_add(threadstate->prev); | |
break; | |
case EEPY_TERMINATING: | |
// Clean up after the previous (terminated) process | |
if(threadstate->prev->free_stack) | |
state.free(threadstate->prev->stack.ss_sp); | |
state.free(threadstate->prev); | |
break; | |
} | |
threadstate->prev = NULL; | |
} | |
} | |
// Initializes the eepy library | |
void eepy_init() { | |
eepy_process *main_process; | |
eepy_thread_state *main_thread; | |
if(!state.initialized) { | |
pthread_key_create(&state.threadkey, NULL); | |
pthread_cond_init(&state.runnable_cond, NULL); | |
pthread_mutex_init(&state.runnable_lock, NULL); | |
pthread_mutex_init(&state.spawn_lock, NULL); | |
state.alloc = malloc; | |
state.free = free; | |
state.initialized = TRUE; | |
// Turn the current execution unit into a process | |
main_process = state.alloc(sizeof(eepy_process)); | |
main_process->free_stack = FALSE; | |
main_process->state = EEPY_RUNNING; | |
main_thread = state.alloc(sizeof(eepy_thread_state)); | |
main_thread->current = main_process; | |
main_thread->prev = NULL; | |
pthread_setspecific(state.threadkey, main_thread); | |
} | |
} | |
void eepy_yield() { | |
eepy_thread_state *threadstate = pthread_getspecific(state.threadkey); | |
eepy_process *current = threadstate->current; | |
current->state = EEPY_STOPPED; | |
threadstate->prev = current; | |
setjmp(current->context); | |
if(current->state != EEPY_STOPPED) { | |
// Subsequent invocation - clean up previous process and continue | |
_eepy_onresume(pthread_getspecific(state.threadkey)); | |
} else { | |
// Initial invocation - go find a process to resume | |
_eepy_restore(threadstate, TRUE); | |
} | |
} | |
void eepy_main() { | |
eepy_thread_state *thread_state = state.alloc(sizeof(eepy_thread_state)); | |
thread_state->current = NULL; | |
thread_state->prev = NULL; | |
pthread_setspecific(state.threadkey, thread_state); | |
_eepy_restore(NULL, FALSE); | |
} | |
void _eepy_process_main(int argument) { | |
// Save the current context, and return to terminate the signal handler scope | |
if(!setjmp(state.spawn_process->context)) | |
return; | |
// We are being called again from the main context - call the function. | |
eepy_thread_state *threadstate = pthread_getspecific(state.threadkey); | |
_eepy_onresume(threadstate); | |
threadstate->current->start(); | |
// Process terminating | |
threadstate->current->state = EEPY_TERMINATING; | |
_eepy_restore(threadstate, FALSE); | |
} | |
void eepy_spawn(void (*start)(), int stack_size) { | |
eepy_process *newprocess = state.alloc(sizeof(eepy_process)); | |
stack_t oldStack; | |
struct sigaction handler, oldHandler; | |
if(stack_size <= 0) | |
stack_size = SIGSTKSZ; | |
newprocess->state = EEPY_STOPPED; | |
newprocess->start = start; | |
newprocess->free_stack = TRUE; | |
newprocess->stack.ss_flags = 0; | |
newprocess->stack.ss_size = stack_size; | |
newprocess->stack.ss_sp = state.alloc(stack_size); | |
// Grab the spawn lock | |
pthread_mutex_lock(&state.spawn_lock); | |
state.spawn_process = newprocess; | |
// Install the new stack for the signal handler | |
assert(sigaltstack(&newprocess->stack, &oldStack) == 0); | |
// Set and call the signal handler to create the stack | |
handler.sa_handler = &_eepy_process_main; | |
handler.sa_flags = SA_ONSTACK; | |
sigemptyset(&handler.sa_mask); | |
assert(sigaction(SIGUSR1, &handler, &oldHandler) == 0); | |
assert(raise(SIGUSR1) == 0); | |
// Restore the original stack and handler | |
sigaltstack(&oldStack, 0); | |
sigaction(SIGUSR1, &oldHandler, 0); | |
pthread_mutex_unlock(&state.spawn_lock); | |
_eepy_runqueue_add(newprocess); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#ifndef TRUE | |
#define TRUE 1 | |
#define FALSE 0 | |
#endif | |
#define EEPY_STOPPED 0 | |
#define EEPY_RUNNING 1 | |
#define EEPY_TERMINATING 2 | |
typedef void* (malloc_func)(size_t); | |
typedef void (free_func)(void*); | |
void eepy_init(); | |
void eepy_yield(); | |
void eepy_main(); | |
void eepy_spawn(void (*start)(), int stack_size); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "eepy.h" | |
#include <stdio.h> | |
void other() { | |
printf("In other()\n"); | |
eepy_yield(); | |
printf("Back in other()\n"); | |
} | |
int main(int argc, char *argv[]) { | |
eepy_init(NULL, NULL, 0); | |
eepy_spawn(other, 0); | |
printf("In main()\n"); | |
eepy_yield(); | |
printf("Back in main()\n"); | |
eepy_yield(); | |
exit(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment