------- Comment #5 from daney at gcc dot gnu dot org 2007-05-19 18:12 ------- UNIX/Linux sucks.
There are likely races with read/write and close all over libgcj: ================================================================= Thread 1 Thread 2 Thread 3 load fd from memory (a field). close fd Open new fd with same # read from fd ================================================================= The main problem being that file descriptors are reused as soon as possible. This makes multi-thread safe file operations very difficult. One solution: At libgcj initialization time do: ===================================== int pe[2]; pipe(pe); close(pe[0]); int global_always_error_fd = pe[1]; ===================================== All Classes that use descriptors have: ====================== int fileDesInUse; int fileDes; int cleanupFD = -1; ====================== A read operation is like this: =============================== synchronized { fileDesInUse++; } // use the descriptor read(fileDes, ....); synchronized { fileDesInUse--; if (cleanupFD != -1 && fileDesInUse == 0) { close(cleanupFD); cleanupFD = -1; } } ======================== And close is like this: ======================== cleanupFD = dup(fileDes) dup2(global_always_error_fd, fileDes); synchronized { if (fileDesInUse == 0) { close(cleanupFD); cleanupFD = -1; } } ======================== dup2 is an atomic operation that assures that the file descriptor can not be reused by another thread while we might be using it. The real close is done on a descriptor that is not in use by any other object. The extra descriptors created with all the duping are closed when it is certian that nothing is using them. Yuck. -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29604