Skip to content
Advertisement

Segmentation fault with ucontext makecontext on OS X 10.10

#include <stdio.h>
#include <stdlib.h>
#define _XOPEN_SOURCE 600
#include <ucontext.h>

/* Tests creation.
   Should print "Hello World!" */

typedef struct thread_t{
  ucontext_t thread_context;
}thread_t;

void *thr1(void *in) {
  printf("Hello World!n");
  fflush(stdout);
  return NULL;
}

void *thr2(void *in) {
  printf("goodbye World!n");
  fflush(stdout);
  return NULL;
}

int main() {
  thread_t t1;
  thread_t t2;

  thread_create( &t1, thr1, NULL);
  // if you comment out the following line, the program will run like a charm. 
  thread_create( &t2, thr2, NULL);
  setcontext(&t1.thread_context);

  return EXIT_SUCCESS;
}

void thread_routine(void *(*start_routine)(void *), void *arg)
{
  start_routine(arg);
  printf("gtthread routine finishedn");
}

int thread_create(thread_t *thread,
        void *(*start_routine)(void *),
        void *arg){
  if (getcontext(&(thread->thread_context)) == -1)
  {
    perror("getcontext");
  }

  thread->thread_context.uc_stack.ss_sp = (char*) malloc(SIGSTKSZ);
  thread->thread_context.uc_stack.ss_size = SIGSTKSZ;
  thread->thread_context.uc_link = NULL;

  makecontext(&(thread->thread_context), thread_routine, 2, (void *)start_routine, arg);
}

I run my code in OS X 10.10 with gcc. I am trying to implement a usercontext library.

If I comment out thread_create( &t2, thr2, NULL);, the code will produce desired effect. I have no idea why a line related to t2 will lead to segmentation fault of t1.

Author’s Notes

I happily work on implementing a usercontext library after switching to Ubuntu. Everything works fine. No segmentation fault anymore. And as expected, it crashes on OS X 10.10.

My guess would be that since makecontext(), swapcontext(), and etc. is deprecated on OS X since 10.6 as warned by compiler, I shouldn’t expect that it will work.

Advertisement

Answer

Your program has some flaws, some of them causing it to produce undefined behavior. Undefined behavior can turn out to manifest as exactly the behavior you expected, but if it happens to do so once, in some particular environment, that does not provide any reason to expect that it will do so again — not in that environment, and certainly not in a different environment.

Here are the more serious issues I noticed:

  • thread_routine() does not have the correct return type or argument types for a context’s start function. The context start function is expected to return void (i.e. nothing) not void *, which is altogether different. Also the actual arguments passed to it, if any, will all be type int. Therefore, when this function is called by your program as a result of setcontext(), undefined behavior results. On a machine where function pointers and object pointers are both the same size as int, you may get away with it, but on other machines the program is likely to crash hard. This is probably the cause of the segfault you observed.

  • In thread_create() you initialize thread->thread_context.uc_link to NULL. This is not an error in itself, but in the bigger picture it will have the effect that when the context’s start function returns, the (OS) thread in which it is running will exit. Presumably, you would rather be afforded the opportunity to swap in a different context.

  • You use setcontext() to switch to the t1 context. If successful, this call does not return neither then nor later, leaving you no way to later switch to the t2 context. In practice, though, the thread and therefore the whole program will exit anyway when t1’s start function returns (see above), so it’s moot for you. For what it’s worth, though, you should probably be using swapcontext() instead.

  • thread_create() is declared to return an int, but it has no return statement.

gcc ought to be emitting warnings about some of these issues, by the way. If not, then turn up the warning level. The -Wall level ought to be sufficient, but I often turn on -Wextra as well to look for issues. Occasionally there are warnings you can safely ignore (especially with -Wextra), but you should make that evaluation separately for every single one.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement