diff --git a/include/llvm/Support/Process.h b/include/llvm/Support/Process.h index bb7467bfc83..861667927d8 100644 --- a/include/llvm/Support/Process.h +++ b/include/llvm/Support/Process.h @@ -192,6 +192,15 @@ public: // components should not call this. static std::error_code FixupStandardFileDescriptors(); + // This function safely closes a file descriptor. It is not safe to retry + // close(2) when it returns with errno equivalent to EINTR; this is because + // *nixen cannot agree if the file descriptor is, in fact, closed when this + // occurs. + // + // N.B. Some operating systems, due to thread cancellation, cannot properly + // guarantee that it will or will not be closed one way or the other! + static std::error_code SafelyCloseFileDescriptor(int FD); + /// This function determines if the standard input is connected directly /// to a user's input (keyboard probably), rather than coming from a file /// or pipe. diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc index 01c78cbc0da..d39443b1124 100644 --- a/lib/Support/Unix/Process.inc +++ b/lib/Support/Unix/Process.inc @@ -258,6 +258,30 @@ std::error_code Process::FixupStandardFileDescriptors() { return std::error_code(); } +std::error_code Process::SafelyCloseFileDescriptor(int FD) { + // Create a signal set filled with *all* signals. + sigset_t FullSet; + if (sigfillset(&FullSet) < 0) + return std::error_code(errno, std::generic_category()); + // Atomically swap our current signal mask with a full mask. + sigset_t SavedSet; + if (int EC = pthread_sigmask(SIG_SETMASK, &FullSet, &SavedSet)) + return std::error_code(EC, std::generic_category()); + // Attempt to close the file descriptor. + // We need to save the error, if one occurs, because our subsequent call to + // pthread_sigmask might tamper with errno. + int ErrnoFromClose = 0; + if (::close(FD) < 0) + ErrnoFromClose = errno; + // Restore the signal mask back to what we saved earlier. + int EC = pthread_sigmask(SIG_SETMASK, &SavedSet, nullptr); + // The error code from close takes precedence over the one from + // pthread_sigmask. + if (ErrnoFromClose) + return std::error_code(ErrnoFromClose, std::generic_category()); + return std::error_code(EC, std::generic_category()); +} + bool Process::StandardInIsUserInput() { return FileDescriptorIsDisplayed(STDIN_FILENO); } diff --git a/lib/Support/Windows/Process.inc b/lib/Support/Windows/Process.inc index db87d8ed60e..3819e638c72 100644 --- a/lib/Support/Windows/Process.inc +++ b/lib/Support/Windows/Process.inc @@ -277,6 +277,12 @@ std::error_code Process::FixupStandardFileDescriptors() { return std::error_code(); } +std::error_code Process::SafelyCloseFileDescriptor(int FD) { + if (::close(FD) < 0) + return std::error_code(errno, std::generic_category()); + return std::error_code(); +} + bool Process::StandardInIsUserInput() { return FileDescriptorIsDisplayed(0); } diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp index c2c55cff7ed..bbbbe4ae8c6 100644 --- a/lib/Support/raw_ostream.cpp +++ b/lib/Support/raw_ostream.cpp @@ -536,12 +536,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered) raw_fd_ostream::~raw_fd_ostream() { if (FD >= 0) { flush(); - if (ShouldClose) - while (::close(FD) != 0) - if (errno != EINTR) { - error_detected(); - break; - } + if (ShouldClose && sys::Process::SafelyCloseFileDescriptor(FD)) + error_detected(); } #ifdef __MINGW32__ @@ -615,11 +611,8 @@ void raw_fd_ostream::close() { assert(ShouldClose); ShouldClose = false; flush(); - while (::close(FD) != 0) - if (errno != EINTR) { - error_detected(); - break; - } + if (sys::Process::SafelyCloseFileDescriptor(FD)) + error_detected(); FD = -1; }