I've been trying to write a program with a logging thread that will consume messages in 'printf format' passed via a struct. It seemed that this should be possible using va_copy to copy the variadic arguments but they would always come out as garbage. This is with gcc 4.1.2 on amd64. Reading through the amd64 ABI it's now clear that the va_list is just a struct and the actual values are stored in registers. So I imagine that when it switches threads the registers are restored and the va_list isn't valid anymore. But I can't find any documentation about whether the va_* macros were ever supposed to be thread safe. It seems that they probably are everywhere except PPC and amd64.
Is there a portable way to pass a va_list between threads? Here's an example program, if you compile it on a 32 bit machine (or even with -m32) it prints out both strings ok, but on amd64 it will print nulls for the threaded case. $ gcc -m64 -g -lpthread test.c $ ./a.out hello world debug: hello world tdebug: hello world $ gcc -m64 -g -lpthread test.c $ ./a.out hello world debug: hello world tdebug: (null) (null) #include <stdarg.h> #include <pthread.h> typedef struct log_s { const char *format; va_list ap; } log_t; log_t mylog; pthread_mutex_t m; pthread_cond_t c; void printlog() { vprintf(mylog.format, mylog.ap); } void *tprintlog() { pthread_mutex_lock(&m); pthread_cond_wait(&c, &m); vprintf(mylog.format, mylog.ap); pthread_mutex_unlock(&m); } void debug(const char *format, ...) { va_list ap; mylog.format = format; va_start(ap, format); va_copy(mylog.ap, ap); printlog(); va_end(ap); } void tdebug(const char *format, ...) { va_list ap; pthread_mutex_lock(&m); mylog.format = format; va_start(ap, format); va_copy(mylog.ap, ap); pthread_cond_signal(&c); pthread_mutex_unlock(&m); } int main(int argc, char *argv[]) { pthread_t t; debug("debug: %s %s\n", argv[1], argv[2]); pthread_mutex_init(&m, NULL); pthread_cond_init(&c, NULL); pthread_create(&t, NULL, tprintlog, NULL); sleep(1); tdebug("tdebug: %s %s\n", argv[1], argv[2]); sleep(1); }