1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
/* BLURB gpl
Coda File System
Release 5
Copyright (c) 2005 Carnegie Mellon University
Additional copyrights listed below
This code is distributed "AS IS" without warranty of any kind under
the terms of the GNU General Public Licence Version 2, as shown in the
file LICENSE. The technical and financial contributors to Coda are
listed in the file CREDITS.
Additional copyrights
#*/
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include "lwp_ucontext.h"
/* if we already have ucontext.h we don't need any of this */
#ifndef CODA_USE_UCONTEXT
/* Some architectures have a stack that grows up instead of down */
//#define STACK_GROWS_UP 1
/* next most portable alternative is to use sigaltstack */
#ifdef HAVE_SIGALTSTACK
jmp_buf parent;
#define returnto(ctx) longjmp(*ctx, 1)
#else
/* otherwise, use the old LWP savecontext/returnto assembly routines */
struct lwp_context { /* saved context for dispatcher */
char *topstack; /* ptr to top of process stack */
char *returnadd; /* return address ? */
char *ccr; /* Condition code register */
};
int savecontext (void (*f)(void), struct lwp_context *ctx, char *stack);
int returnto (struct lwp_context *ctx);
static struct lwp_context parent;
#endif
/* make stacks 16-byte aligned and leave space for silly LinuxPPC lunkage, or
* we segfault entering functions --troy */
#define STACK_PAD 64
/* global variables used when initializing new threads */
static ucontext_t *child;
static void (*child_func)();
static int child_argc;
static void *child_arg;
/* helper function to start a new thread */
static void _thread(void)
{
ucontext_t *this = child;
void (*func)() = child_func;
int argc = child_argc;
void *arg = child_arg;
child = NULL;
if (sigsetjmp(this->uc_mcontext, 0) == 0)
returnto(&parent);
/* restore signal mask */
sigprocmask(SIG_SETMASK, &this->uc_sigmask, NULL);
if (argc)
func(arg);
else
func();
if (this->uc_link)
setcontext(this->uc_link);
/* according to the makecontext documentation, this 'thread' now exits.
* But we don't know which other 'context' we should jump to... */
exit(0);
}
/* Implementations for getcontext, setcontext, makecontext, and swapcontext */
void _lwp_initctx(ucontext_t *ucp)
{
sigset_t sigempty;
sigemptyset(&sigempty);
memset(ucp, 0, sizeof(ucontext_t));
/* save the signal mask of the current thread */
sigprocmask(SIG_BLOCK, &sigempty, &ucp->uc_sigmask);
}
int setcontext(const ucontext_t *ucp)
{
siglongjmp(*(sigjmp_buf *)&ucp->uc_mcontext, 1);
assert(0); /* we should never get here */
}
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
{
if (sigsetjmp(oucp->uc_mcontext, 1) == 0)
siglongjmp(ucp->uc_mcontext, 1);
return 0;
}
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
{
va_list ap;
char *stack = ucp->uc_stack.ss_sp;
#ifdef HAVE_SIGALTSTACK
struct sigaction action, oldaction;
sigset_t sigs, oldsigs;
stack_t oldstack;
#endif
assert(stack != NULL);
child = ucp;
child_func = func;
child_argc = argc;
if (argc) {
assert(argc <= 1);
va_start(ap, argc);
child_arg = va_arg(ap, void *);
va_end(ap);
}
#ifndef HAVE_SIGALTSTACK
#ifndef STACK_GROWS_UP
stack += (ucp->uc_stack.ss_size-1) & ~(STACK_PAD-1);
#endif
/* The old lwp technique looks a lot simpler now doesn't it. However
* it requires processor specific assembly which is a PITA */
savecontext(_thread, &parent, stack);
#else /* HAVE_SIGALTSTACK */
action.sa_handler = (void(*)(int))_thread;
action.sa_flags = SA_ONSTACK;
sigemptyset(&action.sa_mask);
sigemptyset(&sigs);
sigaddset(&sigs, SIGUSR1);
/* we use a signal to jump into the new stack */
sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
sigaltstack(&ucp->uc_stack, &oldstack);
sigaction(SIGUSR1, &action, &oldaction);
kill(getpid(), SIGUSR1);
/* handle the SIGUSR1 signal */
sigfillset(&sigs);
sigdelset(&sigs, SIGUSR1);
if (!setjmp(parent))
while (child)
sigsuspend(&sigs);
/* The new context should be set up, now revert to the old state... */
sigaltstack(&oldstack, NULL);
sigaction(SIGUSR1, &oldaction, NULL);
sigprocmask(SIG_SETMASK, &oldsigs, NULL);
#endif /* HAVE_SIGALTSTACK */
}
#endif
|