/* Demonstrate a bug on Mac OS X whereby the process can hang while simultaneously trying write() with a large buffer and close(), from separate threads on the write and read fds respectively of a pipe. See the constant size at the top of main(): The problem has only been seen with size of 17 or larger (buffer 128K or larger). So, a workaround is to limit the write buffer to 64K. Problem has been seen on Mac OS X 10.5. The problem has not been seen on 32-bit Ubuntu or 64-bit Red Hat. By default, this script cycles for 2000 tries, or until the hang, printing out the cycle count and line-by-line records of the code that's been executed. All output is to stderr. As with all such race condition-type bugs, the logging code, etc can have an effect on how likely the problem is to occur. To reproduce: $ gcc -Wall -Werror -lpthread -o os_pipe_write_close_bug os_pipe_write_close_bug.c $ ./os_pipe_write_close_bug 2> os_pipe_write_close_bug.log ... it hangs for me on Mac OS X 10.5 When the process hangs, according to 'ps -l' it's in an uninteruptible wait 'U+', so it's slightly tough to kill. If run from bash, Ctrl-Z followed by 'kill %1 %1' followed by two newlines does the trick. The file os_pipe_write_close_bug.log will show you where the two threads had got to. */ #include #include #include #include #include #include #include char * bytes = NULL; int nbytes = 0; int fds[2]; void * writer_thread_func() { int err2; int errno2; fprintf(stderr, " writer_thread_func: enter\n"); assert( bytes ); assert( nbytes > 0 ); err2 = write(fds[1], bytes, nbytes); errno2 = errno; fprintf(stderr, " writer_thread_func: write\n"); assert( err2 >= 0 || errno2 == EPIPE ); err2 = close(fds[1]); errno2 = errno; fprintf(stderr, " writer_thread_func: close\n"); assert( err2 >= 0 ); fprintf(stderr, " writer_thread_func: exit\n"); return NULL; } void go() { int err1; int errno1; fprintf(stderr, " go: enter\n"); err1 = pipe(fds); assert( err1 == 0 ); pthread_t writer_thread = 0x00; err1 = pthread_create(&writer_thread, NULL, &writer_thread_func, NULL); fprintf(stderr, " go: pthread_create\n"); assert( err1 == 0 ); err1 = close(fds[0]); errno1 = errno; fprintf(stderr, " go: close\n"); assert( err1 == 0 ); err1 = pthread_join(writer_thread, NULL); fprintf(stderr, " go: pthread_join\n"); assert( err1 == 0 ); fprintf(stderr, " go: exit\n"); } int main(int argc, char *argv[]) { int const size = 17; int const loop = 2000; int i; typedef void (*sighandler_t)(int); sighandler_t const sighandler = signal(SIGPIPE, SIG_IGN); assert( sighandler != SIG_ERR ); nbytes = 1 << size; bytes = malloc(nbytes); assert( bytes ); for( i = nbytes; --i >= 0; bytes[i] = (char)i ); for( i = 0; i < loop; ++i ) { fprintf(stderr, "cycle: %d\n", i); go(); } return 0; }