A regular exit (unlike _exit for example) should do all the usual atexit , output flush, etc., to work. In some cases, you can build code that hangs, but I had to make an βobvious problemβ to show it. If the library routine (for example, the internal stdio fflush ) tries to capture a lock (for example, in the stdio stream) in an output stream that holds some other thread, it may be possible to get a similar widget even without your own atexit . Since you did not show your code, I am just thinking.
Here's a test program (with a deliberate, obvious problem) that freezes when reported, at least on FreeBSD. (Formatting is difficult because I cut and paste tabs, but then I had to edit some in spaces ...)
#include <pthread.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> pthread_mutex_t global_mtx; void die(int error, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if (error) fprintf(stderr, ": %s\n", strerror(error)); else putc('\n', stderr); fflush(stderr); _exit(0); } enum behavior { NORMAL, EXIT_WO_HANG, EXIT_W_HANG }; struct behave { enum behavior how; pthread_mutex_t lock; pthread_cond_t cond; int th1_entered; int th2_entered; }; void hanger(void); void *th1_main(void *); void *th2_main(void *); #define WR(x) (void)write(1, x, sizeof(x) - 1) int main(int argc, char **argv) { int error; struct behave how; pthread_t th1, th2; error = pthread_mutex_init(&global_mtx, NULL); if (error) die(error, "pthread_mutex_init global_mtx"); error = pthread_mutex_init(&how.lock, NULL); if (error) die(error, "pthread_mutex_init how.lock"); error = pthread_cond_init(&how.cond, NULL); if (error) die(error, "pthread_cond_init how.cond"); how.how = NORMAL; how.th1_entered = 0; how.th2_entered = 0; if (argc > 1) { if (strcmp(argv[1], "exit") == 0) how.how = EXIT_WO_HANG; else if (strcmp(argv[1], "hang") == 0) how.how = EXIT_W_HANG; else if (strcmp(argv[1], "normal") != 0) die(0, "usage: example [normal|exit|hang]"); } atexit(hanger); error = pthread_create(&th1, NULL, th1_main, &how); if (error) die(error, "pthread_create th1"); error = pthread_create(&th2, NULL, th2_main, &how); if (error) die(error, "pthread_create th2"); /* now wait for threads */ error = pthread_join(th1, NULL); error = pthread_join(th2, NULL); printf("joined, normal exit\n"); return 0; } void *th1_main(void *arg) { struct behave *how = arg; WR("thread 1 start\n"); (void) pthread_mutex_lock(&global_mtx); (void) pthread_mutex_lock(&how->lock); how->th1_entered = 1; pthread_cond_signal(&how->cond); while (how->th2_entered == 0) (void) pthread_cond_wait(&how->cond, &how->lock); WR("thread 1 sees thread 2 started\n"); (void) pthread_mutex_unlock(&how->lock); if (how->how == EXIT_W_HANG) WR("thread 1 not unlocking\n"); else (void) pthread_mutex_unlock(&global_mtx); return NULL; } void *th2_main(void *arg) { struct behave *how = arg; WR("thread 2 start\n"); (void) pthread_mutex_lock(&how->lock); how->th2_entered = 1; pthread_cond_signal(&how->cond); while (how->th1_entered == 0) (void) pthread_cond_wait(&how->cond, &how->lock); WR("thread 2 sees thread 1 started\n"); (void) pthread_mutex_unlock(&how->lock); if (how->how != NORMAL) { WR("thread 2 exit()\n"); exit(1); } return NULL; } void hanger(void) { /* this is what will cause us to hang, in the one case */ WR("hanger start\n"); pthread_mutex_lock(&global_mtx); WR("hanger got global mutex\n"); pthread_mutex_unlock(&global_mtx); WR("hanger finish\n"); }