/* * Copyright (c) 2001 Alfred Perlstein * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "zbthreads.h" struct zbthread { sigjmp_buf zth_outbuf; /* returned address (when thread exits) */ sigjmp_buf zth_resbuf; /* resumed address (where thread left off) */ void *zth_stk; /* stack */ }; static void zbthread_sigbounce(int); static void zbthread_free(struct zbthread *); /*volatile*/ static struct zbthread *zbthread_cur; static zbthread_startroutine zbthread_start; static void *zbthread_threadarg; static struct sigaltstack oldstk; static struct sigaction osa; static sigset_t oset; #define ZBTHREAD_SIG SIGUSR1 #define ZBTHREAD_YIELD 1 #define ZBTHREAD_EXIT 2 #ifdef ZBTHREAD_DEBUG #define bug(args...) \ do { \ fprintf(stderr, "|%s(%d)\n`-> ", \ __func__, __LINE__); \ fprintf(stderr, args); \ fprintf(stderr, "\n"); \ } while (0) #else #define bug(args...) #endif int zbthread_create(zbthreadp, startit, threadarg) struct zbthread **zbthreadp; zbthread_startroutine startit; void *threadarg; { struct zbthread *zth, *oldzth; struct sigaltstack sigstk; struct sigaction sa; sigset_t set; int error, x; *zbthreadp = NULL; zth = malloc(sizeof(*zth)); if (zth == NULL) return (errno); sigstk.ss_size = SIGSTKSZ + 256 * 1024; sigstk.ss_sp = malloc(sigstk.ss_size); if (sigstk.ss_sp == NULL) { free(zth); return (errno); } oldzth = zbthread_cur; x = sigsetjmp(zth->zth_outbuf, 1); error = 0; if (x == ZBTHREAD_EXIT) { /* * we're here because the thread exited */ bug("ZBTHREAD_EXIT"); *zbthreadp = NULL; zbthread_cur = oldzth; goto err; } else if (x == ZBTHREAD_YIELD) { /* * we're here because the thread yeilded */ bug("ZBTHREAD_YIELD"); *zbthreadp = zbthread_cur; zbthread_cur = oldzth; return (0); } /* * defer signals */ sigemptyset(&set); sigaddset(&set, ZBTHREAD_SIG); x = sigprocmask(SIG_BLOCK, &set, &oset); if (x != 0) { error = errno; goto err; } sa.sa_flags = SA_ONSTACK; sa.sa_handler = &zbthread_sigbounce; sigemptyset(&sa.sa_mask); x = sigaction(ZBTHREAD_SIG, &sa, &osa); if (x != 0) { error = errno; goto err_procmask; } sigstk.ss_flags = 0; x = sigaltstack(&sigstk, &oldstk); if (x != 0) { error = errno; goto err_sigaction; } sigprocmask(SIG_SETMASK, &oset, NULL); zbthread_threadarg = threadarg; zbthread_start = startit; zbthread_cur = zth; bug("about to raise signal"); raise(ZBTHREAD_SIG); /* we don't get here */ abort(); err_sigaction: sigaltstack(&oldstk, 0); err_procmask: sigprocmask(SIG_SETMASK, &oset, NULL); err: zbthread_free(zth); bug("returning error"); return (error); } int zbthread_run(zbthreadp) struct zbthread **zbthreadp; { struct zbthread *oldzth; int x; bug("called"); if (*zbthreadp == NULL || *zbthreadp == zbthread_cur) return (EINVAL); oldzth = zbthread_cur; x = sigsetjmp((*zbthreadp)->zth_outbuf, 1); if (x == ZBTHREAD_EXIT) { bug("ZBTHREAD_EXIT"); /* * we're here because the thread exited */ zbthread_free(*zbthreadp); *zbthreadp = NULL; zbthread_cur = oldzth; return (0); } else if (x == ZBTHREAD_YIELD) { bug("ZBTHREAD_YIELD"); /* * we're here because the thread yeilded */ zbthread_cur = oldzth; return (0); } zbthread_cur = *zbthreadp; bug("about to run thread"); siglongjmp(zbthread_cur->zth_resbuf, 1); } int zbthread_yeild(void) { struct zbthread *zth; bug("called"); zth = zbthread_cur; if (zth == NULL) return (EINVAL); if (sigsetjmp(zth->zth_resbuf, 1) != 0) { bug("being run"); zbthread_cur = zth; return (0); } bug("yeilding to %p", zth); siglongjmp(zth->zth_outbuf, ZBTHREAD_YIELD); } int zbthread_exit(void) { struct zbthread *zth; zth = zbthread_cur; bug("called thread == %p", zth); if (zth == NULL) return (EINVAL); siglongjmp(zth->zth_outbuf, ZBTHREAD_EXIT); } static void zbthread_sigbounce(sig) int sig; { sigaltstack(&oldstk, 0); sigaction(SIGUSR1, &osa, NULL); zbthread_start(zbthread_threadarg); bug("about to bounce!"); siglongjmp(zbthread_cur->zth_outbuf, ZBTHREAD_EXIT); } static void zbthread_free(zth) struct zbthread *zth; { free(zth->zth_stk); free(zth); }