Following 163 lines are reduced from a "very portable" way to implement
threads in usermode,
without hacking jmpbuf, using sigaltstack to set the stack pointer.


On OpenBSD 4.6 x86 and powerpc it hangs if I use -pthread, but otherwise does
not.
 The hang is in the while(..) sigsuspend(), not too surprising.
  Using pthread_sigmask and pthread_kill(pthread_self()) doesn't fix it.


Any ideas?
Maybe it is "fixed" in 4.7? I'll try that soon.
The approach is described here:
http://www.usenix.org/event/usenix2000/general/full_papers/engelschall/engels
chall_html/index.html
and this is faithful rendition of it, except for reducing it a little here


I use -pthread to get at pthread_fork, and would rather not be "fragile that
way" -- works without -pthread, doesn't with.
  If I have no other choice, I'll probably do without pthread_fork and
therefore without -pthread and with the fragility.

If I can get this to work I can stop hacking jmpbuf.


The code seems ok on MacOSX and Linux.


Thanks,
 - Jay


#include <assert.h>
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/signal.h>

#define ZERO_MEMORY(a) (memset(&(a), 0, sizeof(a)))

typedef struct {
  sigjmp_buf jb;
} Context;

#define SWAP_CONTEXT(oldc, newc)       \
do {                                   \
    if (sigsetjmp((oldc)->jb, 1) == 0) \
        siglongjmp((newc)->jb, 1);     \
} while (0)

Context mctx_caller;
sig_atomic_t volatile mctx_called;
Context * volatile mctx_create;
void (* volatile mctx_create_func)(void);
sigset_t mctx_create_sigs;

void mctx_create_boot(void)
{
    void (*volatile mctx_start_func)(void);

    fprintf(stderr, "mctx_create_boot\n");
   
    sigprocmask(SIG_SETMASK, &mctx_create_sigs, NULL);
    mctx_start_func = mctx_create_func;
    SWAP_CONTEXT(mctx_create, &mctx_caller);
    mctx_start_func();
    abort(); /* not reached */
}

void mctx_create_trampoline(int sig)
{
    fprintf(stderr, "mctx_create_trampoline\n");
    if (sigsetjmp(mctx_create->jb, 0) == 0)
    {
        mctx_called = 1;
        return;
    }
   
    mctx_create_boot();
}

void
xMakeContext(
    Context *context,
    void (*function)(void),
    void *stack,
    size_t stack_size)
{
    struct sigaction sa;
    struct sigaction osa;
    stack_t ss;
    stack_t oss;
    sigset_t osigs;
    sigset_t sigs;

    fprintf(stderr, "xMakeContext\n");

    ZERO_MEMORY(sa);
    ZERO_MEMORY(osa);
    ZERO_MEMORY(ss);
    ZERO_MEMORY(oss);
    ZERO_MEMORY(osigs);
    ZERO_MEMORY(sigs);

    sigemptyset(&sigs);
    sigaddset(&sigs, SIGUSR1);
    sigprocmask(SIG_BLOCK, &sigs, &osigs);
   
    sa.sa_handler = mctx_create_trampoline;
    sa.sa_flags = SA_ONSTACK;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGUSR1, &sa, &osa);
   
    ss.ss_sp = stack;
    ss.ss_size = stack_size;
    ss.ss_flags = 0;
    sigaltstack(&ss, &oss);
   
    mctx_create = context;
    mctx_create_func = function;
    mctx_create_sigs = osigs;
    mctx_called = 0;
    kill(getpid(), SIGUSR1);
    sigfillset(&sigs);
    sigdelset(&sigs, SIGUSR1);
    while (!mctx_called)
        sigsuspend(&sigs);
       
    sigaltstack(NULL, &ss);
    ss.ss_flags = SS_DISABLE;
    sigaltstack(&ss, NULL);
    if (!(oss.ss_flags & SS_DISABLE))
        sigaltstack(&oss, NULL);
    sigaction(SIGUSR1, &osa, NULL);
    sigprocmask(SIG_SETMASK, &osigs, NULL);
   
    SWAP_CONTEXT(&mctx_caller, context);
}

void *
MakeContext (void (*p)(void), size_t  words)
{
  Context *c = (Context *)calloc (1, sizeof(*c));
  size_t size = sizeof(void *) * words;
  size_t pagesize = getpagesize();
  char *sp = { 0 };
  size_t pages = { 0 };
  int er = { 0 };

  if (c == NULL)
    goto Error;
  if (size <= 0) return c;
  if (size < MINSIGSTKSZ) size = MINSIGSTKSZ;

  /* Round up to a whole number of pages, and
   * allocate two extra pages, one at the start
   * and one at the end, and don't allow accessing
   * either one (catch stack overflow and underflow).
   */
  pages = (size + pagesize - 1) / pagesize + 2;
  size = pages * pagesize;
  sp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1,
0);
  if (sp == NULL)
    goto Error;
  if (mprotect(sp, pagesize, PROT_NONE)) abort();
  if (mprotect(sp + size - pagesize, pagesize, PROT_NONE)) abort();

  xMakeContext(c, p, sp + pagesize, size - 2 * pagesize);

  return c;
Error:
  er = errno;
  if (c) free(c);
  if (sp) munmap(sp, size);
  errno = er;
  return NULL;
}

void F1(void)
{
  fprintf(stderr, "F1\n");
}

int main()
{
  MakeContext(F1, 1000);
  return 0;
}

Reply via email to