#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 returnvoid
(i.e. nothing) notvoid *
, which is altogether different. Also the actual arguments passed to it, if any, will all be typeint
. Therefore, when this function is called by your program as a result ofsetcontext()
, undefined behavior results. On a machine where function pointers and object pointers are both the same size asint
, 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 initializethread->thread_context.uc_link
toNULL
. 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 thet1
context. If successful, this call does not return neither then nor later, leaving you no way to later switch to thet2
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 usingswapcontext()
instead.thread_create()
is declared to return anint
, but it has noreturn
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.