[llvm] r219189 - Support: Don't call close again if we get EINTR

David Majnemer david.majnemer at gmail.com
Mon Oct 6 22:48:41 PDT 2014


Author: majnemer
Date: Tue Oct  7 00:48:40 2014
New Revision: 219189

URL: http://llvm.org/viewvc/llvm-project?rev=219189&view=rev
Log:
Support: Don't call close again if we get EINTR

Most Unix-like operating systems guarantee that the file descriptor is
closed after a call to close(2), even if close comes back with EINTR.
For these systems, calling close _again_ will either do nothing or close
some other file descriptor open(2)'d by another thread. (Linux)

However, some operating systems do not have this behavior.  They require
at least another call to close(2) before guaranteeing that the
descriptor is closed. (HP-UX)

And some operating systems have an unpredictable blend of the two
behaviors! (xnu)

Avoid this disaster by blocking all signals before we call close(2).
This ensures that a signal will not be delivered to the thread and
close(2) will not give us back EINTR.  We restore the signal mask once
the operation is done.

N.B. This isn't a problem on Windows, it doesn't have a notion of EINTR
because signals always get delivered to dedicated signal handling
threads.

Modified:
    llvm/trunk/include/llvm/Support/Process.h
    llvm/trunk/lib/Support/Unix/Process.inc
    llvm/trunk/lib/Support/Windows/Process.inc
    llvm/trunk/lib/Support/raw_ostream.cpp

Modified: llvm/trunk/include/llvm/Support/Process.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Process.h?rev=219189&r1=219188&r2=219189&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Process.h (original)
+++ llvm/trunk/include/llvm/Support/Process.h Tue Oct  7 00:48:40 2014
@@ -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.

Modified: llvm/trunk/lib/Support/Unix/Process.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Unix/Process.inc?rev=219189&r1=219188&r2=219189&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Unix/Process.inc (original)
+++ llvm/trunk/lib/Support/Unix/Process.inc Tue Oct  7 00:48:40 2014
@@ -258,6 +258,30 @@ std::error_code Process::FixupStandardFi
   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);
 }

Modified: llvm/trunk/lib/Support/Windows/Process.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/Process.inc?rev=219189&r1=219188&r2=219189&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/Process.inc (original)
+++ llvm/trunk/lib/Support/Windows/Process.inc Tue Oct  7 00:48:40 2014
@@ -277,6 +277,12 @@ std::error_code Process::FixupStandardFi
   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);
 }

Modified: llvm/trunk/lib/Support/raw_ostream.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/raw_ostream.cpp?rev=219189&r1=219188&r2=219189&view=diff
==============================================================================
--- llvm/trunk/lib/Support/raw_ostream.cpp (original)
+++ llvm/trunk/lib/Support/raw_ostream.cpp Tue Oct  7 00:48:40 2014
@@ -536,12 +536,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, b
 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;
 }
 





More information about the llvm-commits mailing list