diff options
Diffstat (limited to 'src/common/rswitch.c')
-rw-r--r-- | src/common/rswitch.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/src/common/rswitch.c b/src/common/rswitch.c new file mode 100644 index 0000000..9373b4e --- /dev/null +++ b/src/common/rswitch.c @@ -0,0 +1,173 @@ +/* + * rswitch.c -- context switch code using POSIX threads and semaphores + * + * This code implements co-expression context switching on any system that + * provides POSIX threads and semaphores. + * + * Anonymous semaphores are used unless NamedSemaphores is defined. + * (This is for MacOS which does not have anonymous semaphores.) + */ + +#include <fcntl.h> +#include <limits.h> +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "../h/define.h" + +extern void new_context(int, void *); +extern void syserr(char *msg); +extern void *alloc(unsigned int n); + +extern long stksize; /* value of COEXPSIZE */ + +static int inited = 0; /* has first-time initialization been done? */ +static pthread_attr_t attribs; /* thread creation attributes */ + +/* + * Define a "context" struct to hold the thread information we need. + */ +typedef struct { + pthread_t thread; /* thread ID (thread handle) */ + sem_t sema; /* synchronization semaphore (if unnamed) */ + sem_t *semp; /* pointer to semaphore */ + int alive; /* set zero when thread is to die */ + } context; + +static void makesem(context *ctx); +static void *nctramp(void *arg); +static void uerror(char *msg); + +/* + * Treat an Icon "cstate" array as an array of context pointers. + * cstate[0] is used by Icon code that thinks it's setting a stack pointer. + * We use cstate[1] to point to the actual context struct. + * (Both of these are initialized to NULL by Icon 9.4.1 or later.) + */ +typedef context **cstate; + +/* + * coswitch(old, new, first) -- switch contexts. + */ +int coswitch(void *o, void *n, int first) { + + cstate ocs = o; /* old cstate pointer */ + cstate ncs = n; /* new cstate pointer */ + context *old, *new; /* old and new context pointers */ + size_t newsize; /* stack size for new thread */ + size_t pagesize; /* system page size */ + + if (inited) /* if not first call */ + old = ocs[1]; /* load current context pointer */ + else { + /* + * This is the first coswitch() call. + * Allocate and initialize the context struct for &main. + */ + old = ocs[1] = alloc(sizeof(context)); + makesem(old); + old->thread = pthread_self(); + old->alive = 1; + + /* + * Set up thread attributes to honor COEXPSIZE for setting stack size. + */ + pagesize = sysconf(_SC_PAGESIZE); + newsize = stksize; + #ifdef PTHREAD_STACK_MIN + if (newsize < PTHREAD_STACK_MIN) /* ensure system minimum is met */ + newsize = PTHREAD_STACK_MIN; + #endif + if (pagesize > 0 && (newsize % pagesize) != 0) { + /* some systems require an exact multiple of the system page size */ + newsize = newsize + pagesize - (newsize % pagesize); + } + pthread_attr_init(&attribs); + if (pthread_attr_setstacksize(&attribs, newsize) != 0) { + uerror("cannot set stacksize for thread"); + } + + inited = 1; + } + + if (first != 0) /* if not first call for this cstate */ + new = ncs[1]; /* load new context pointer */ + else { + /* + * This is a newly allocated cstate array. + * Allocate and initialize a context struct. + */ + new = ncs[1] = alloc(sizeof(context)); + makesem(new); + if (pthread_create(&new->thread, &attribs, nctramp, new) != 0) + uerror("cannot create thread"); + new->alive = 1; + } + + sem_post(new->semp); /* unblock the new thread */ + sem_wait(old->semp); /* block this thread */ + + if (!old->alive) + pthread_exit(NULL); /* if unblocked because unwanted */ + return 0; /* else return to continue running */ + } + +/* + * coclean(old) -- clean up co-expression state before freeing. + */ +void coclean(void *o) { + cstate ocs = o; /* old cstate pointer */ + context *old = ocs[1]; /* old context pointer */ + if (old == NULL) /* if never initialized, do nothing */ + return; + old->alive = 0; /* signal thread to exit */ + sem_post(old->semp); /* unblock it */ + pthread_join(old->thread, NULL); /* wait for thread to exit */ + #ifdef NamedSemaphores + sem_close(old->semp); /* close associated semaphore */ + #else + sem_destroy(old->semp); /* destroy associated semaphore */ + #endif + free(old); /* free context block */ + } + +/* + * makesem(ctx) -- initialize semaphore in context struct. + */ +static void makesem(context *ctx) { + #ifdef NamedSemaphores /* if cannot use unnamed semaphores */ + char name[50]; + sprintf(name, "i%ld.sem", (long)getpid()); + ctx->semp = sem_open(name, O_CREAT, S_IRUSR | S_IWUSR, 0); + if (ctx->semp == (sem_t *)SEM_FAILED) + uerror("cannot create semaphore"); + sem_unlink(name); + #else /* NamedSemaphores */ + if (sem_init(&ctx->sema, 0, 0) == -1) + uerror("cannot init semaphore"); + ctx->semp = &ctx->sema; + #endif /* NamedSemaphores */ + } + +/* + * nctramp() -- trampoline for calling new_context(0,0). + */ +static void *nctramp(void *arg) { + context *new = arg; /* new context pointer */ + sem_wait(new->semp); /* wait for signal */ + new_context(0, 0); /* call new_context; will not return */ + syserr("new_context returned to nctramp"); + return NULL; + } + +/* + * uerror(s) -- abort due to Unix error. + */ +static void uerror(char *msg) { + perror(msg); + syserr(NULL); + } |