/* Failing test program from Gentoo Bug # 234762 http://bugs.gentoo.org/show_bug.cgi?id=234762 */ #include #include #include #include #include #include #include #include #include #define TEMP_FILENAME_1 "/tmp/thread_test.1.XXXXXX" #define TEMP_FILENAME_2 "/tmp/thread_test.2.XXXXXX" static void func_call_1(void); static void func_call_2(void); void safe_unlink(char const *); static char *temp_filename_1; static char *temp_filename_2; static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; static volatile int thread1_done = 0; static volatile int thread2_done = 0; static volatile int errno1_set = 0; static volatile int errno2_set = 0; void safe_unlink(char const *fso) { assert(fso); unlink(fso); } int main(int argc, char *argv[]) { pthread_t thread1, thread2; int fd; if (argc > 1) { fprintf(stderr, "Usage: %s\n", argv[0]); return 1; } /* Make temp filenames, might not have strdup() */ temp_filename_1 = malloc(strlen(TEMP_FILENAME_1) + 1); strcpy(temp_filename_1, TEMP_FILENAME_1); fd = mkstemp(temp_filename_1); close(fd); temp_filename_2 = malloc(strlen(TEMP_FILENAME_2) + 1); strcpy(temp_filename_2, TEMP_FILENAME_2); fd = mkstemp(temp_filename_2); close(fd); /* Hold lock until we are ready for the child threads to exit. */ pthread_mutex_lock(&init_mutex); pthread_create(&thread1, NULL, (void *(*) (void *)) func_call_1, NULL); pthread_create(&thread2, NULL, (void *(*) (void *)) func_call_2, NULL); while (thread1_done == 0 || thread2_done == 0) sched_yield(); /* if this is a portability problem, remove it */ pthread_mutex_unlock(&init_mutex); /* let children exit */ pthread_join(thread1, NULL); /* clean up children */ pthread_join(thread2, NULL); printf("Your errno is thread-safe.\n"); return EXIT_SUCCESS; } static void func_call_1(void) { safe_unlink(temp_filename_1); /* create, then try to fail on exclusive create open */ if (open(temp_filename_1, O_RDWR | O_CREAT, 0600) < 0 || open(temp_filename_1, O_RDWR | O_CREAT | O_EXCL, 0600) >= 0) { fprintf(stderr, "Could not create file in /tmp or\n"); fprintf(stderr, "Could not generate failure for create file in /tmp **\nexiting\n"); exit(1); } /* * Wait for other thread to set errno. We can't use thread-specific * locking here because it might affect errno. */ errno1_set = 1; while (errno2_set == 0) sched_yield(); if (errno != EEXIST) { fprintf(stderr, "errno not thread-safe **\nexiting\n"); safe_unlink(temp_filename_1); exit(1); } printf("Filename 1: '%s'\n", temp_filename_1); safe_unlink(temp_filename_1); thread1_done = 1; pthread_mutex_lock(&init_mutex); /* wait for parent to test */ pthread_mutex_unlock(&init_mutex); } static void func_call_2(void) { safe_unlink(temp_filename_2); /* open non-existant file */ if (open(temp_filename_2, O_RDONLY, 0600) >= 0) { fprintf(stderr, "Read-only open succeeded without create **\nexiting\n"); exit(1); } /* * Wait for other thread to set errno. We can't use thread-specific * locking here because it might affect errno. */ errno2_set = 1; while (errno1_set == 0) sched_yield(); if (errno != ENOENT) { fprintf(stderr, "errno not thread-safe **\nexiting\n"); safe_unlink(temp_filename_2); exit(1); } printf("Filename 2: '%s'\n", temp_filename_2); safe_unlink(temp_filename_2); thread2_done = 1; pthread_mutex_lock(&init_mutex); /* wait for parent to test */ pthread_mutex_unlock(&init_mutex); }