]> granicus.if.org Git - llvm/commitdiff
Recommit "[Support] Add RetryAfterSignal helper function"
authorPavel Labath <labath@google.com>
Thu, 29 Jun 2017 13:15:31 +0000 (13:15 +0000)
committerPavel Labath <labath@google.com>
Thu, 29 Jun 2017 13:15:31 +0000 (13:15 +0000)
The difference from the previous version is the use of decltype, as the
implementation of std::result_of in libc++ did not work correctly for
variadic function like open(2).

Original summary:
This function retries an operation if it was interrupted by a signal
(failed with EINTR). It's inspired by the TEMP_FAILURE_RETRY macro in
glibc, but I've turned that into a template function. I've also added a
fail-value argument, to enable the function to be used with e.g.
fopen(3), which is documented to fail for any reason that open(2) can
fail (which includes EINTR).

The main user of this function will be lldb, but there were also a
couple of uses within llvm that I could simplify using this function.

Reviewers: zturner, silvas, joerg

Subscribers: mgorny, llvm-commits

Differential Revision: https://reviews.llvm.org/D33895

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@306671 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Support/Errno.h
lib/Support/MemoryBuffer.cpp
lib/Support/Unix/Path.inc
lib/Support/Unix/Process.inc
unittests/Support/CMakeLists.txt
unittests/Support/ErrnoTest.cpp [new file with mode: 0644]

index 4ce65e7dc83c5c65d6d992d8c6e62aeca3ccf109..35dc1ea7cf84fe2ff65d4b8ff88d6d77a080f8c5 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <cerrno>
 #include <string>
+#include <type_traits>
 
 namespace llvm {
 namespace sys {
@@ -29,6 +30,16 @@ std::string StrError();
 /// Like the no-argument version above, but uses \p errnum instead of errno.
 std::string StrError(int errnum);
 
+template <typename FailT, typename Fun, typename... Args>
+inline auto RetryAfterSignal(const FailT &Fail, const Fun &F,
+                             const Args &... As) -> decltype(F(As...)) {
+  decltype(F(As...)) Res;
+  do
+    Res = F(As...);
+  while (Res == Fail && errno == EINTR);
+  return Res;
+}
+
 }  // namespace sys
 }  // namespace llvm
 
index 227e792d83dc477a97bfdb396b030c8712be7fb8..85e782b2c048ac33861defb4e92989692d58287f 100644 (file)
@@ -240,11 +240,9 @@ getMemoryBufferForStream(int FD, const Twine &BufferName) {
   // Read into Buffer until we hit EOF.
   do {
     Buffer.reserve(Buffer.size() + ChunkSize);
-    ReadBytes = read(FD, Buffer.end(), ChunkSize);
-    if (ReadBytes == -1) {
-      if (errno == EINTR) continue;
+    ReadBytes = sys::RetryAfterSignal(-1, read, FD, Buffer.end(), ChunkSize);
+    if (ReadBytes == -1)
       return std::error_code(errno, std::generic_category());
-    }
     Buffer.set_size(Buffer.size() + ReadBytes);
   } while (ReadBytes != 0);
 
@@ -391,13 +389,12 @@ getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
 
   while (BytesLeft) {
 #ifdef HAVE_PREAD
-    ssize_t NumRead = ::pread(FD, BufPtr, BytesLeft, MapSize-BytesLeft+Offset);
+    ssize_t NumRead = sys::RetryAfterSignal(-1, ::pread, FD, BufPtr, BytesLeft,
+                                            MapSize - BytesLeft + Offset);
 #else
-    ssize_t NumRead = ::read(FD, BufPtr, BytesLeft);
+    ssize_t NumRead = sys::RetryAfterSignal(-1, ::read, FD, BufPtr, BytesLeft);
 #endif
     if (NumRead == -1) {
-      if (errno == EINTR)
-        continue;
       // Error while reading.
       return std::error_code(errno, std::generic_category());
     }
index b6774692595bd1a5892047228c505f7105e7fb98..45097eb918b7c9fb018c0366c0f80f1090e458a5 100644 (file)
@@ -737,10 +737,8 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD,
 #ifdef O_CLOEXEC
   OpenFlags |= O_CLOEXEC;
 #endif
-  while ((ResultFD = open(P.begin(), OpenFlags)) < 0) {
-    if (errno != EINTR)
-      return std::error_code(errno, std::generic_category());
-  }
+  if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags)) < 0)
+    return std::error_code(errno, std::generic_category());
 #ifndef O_CLOEXEC
   int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
   (void)r;
@@ -800,10 +798,8 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
 
   SmallString<128> Storage;
   StringRef P = Name.toNullTerminatedStringRef(Storage);
-  while ((ResultFD = open(P.begin(), OpenFlags, Mode)) < 0) {
-    if (errno != EINTR)
-      return std::error_code(errno, std::generic_category());
-  }
+  if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags, Mode)) < 0)
+    return std::error_code(errno, std::generic_category());
 #ifndef O_CLOEXEC
   int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
   (void)r;
index 1d0143c6716e00edd9d99054644fd139e60ed45f..2d4662094682fad2adabec0c8d8ea35cd5a3787c 100644 (file)
@@ -207,13 +207,10 @@ std::error_code Process::FixupStandardFileDescriptors() {
   for (int StandardFD : StandardFDs) {
     struct stat st;
     errno = 0;
-    while (fstat(StandardFD, &st) < 0) {
+    if (RetryAfterSignal(-1, fstat, StandardFD, &st) < 0) {
       assert(errno && "expected errno to be set if fstat failed!");
       // fstat should return EBADF if the file descriptor is closed.
-      if (errno == EBADF)
-        break;
-      // retry fstat if we got EINTR, otherwise bubble up the failure.
-      if (errno != EINTR)
+      if (errno != EBADF)
         return std::error_code(errno, std::generic_category());
     }
     // if fstat succeeds, move on to the next FD.
@@ -222,11 +219,8 @@ std::error_code Process::FixupStandardFileDescriptors() {
     assert(errno == EBADF && "expected errno to have EBADF at this point!");
 
     if (NullFD < 0) {
-      while ((NullFD = open("/dev/null", O_RDWR)) < 0) {
-        if (errno == EINTR)
-          continue;
+      if ((NullFD = RetryAfterSignal(-1, open, "/dev/null", O_RDWR)) < 0)
         return std::error_code(errno, std::generic_category());
-      }
     }
 
     if (NullFD == StandardFD)
index e2a6561089bc2f4c8f2735b97a7831684b32a3e9..641163e39ed3c91f6d31b0e0939d5a11259b1c16 100644 (file)
@@ -21,6 +21,7 @@ add_llvm_unittest(SupportTests
   DebugTest.cpp
   EndianStreamTest.cpp
   EndianTest.cpp
+  ErrnoTest.cpp
   ErrorOrTest.cpp
   ErrorTest.cpp
   FileOutputBufferTest.cpp
diff --git a/unittests/Support/ErrnoTest.cpp b/unittests/Support/ErrnoTest.cpp
new file mode 100644 (file)
index 0000000..67f834a
--- /dev/null
@@ -0,0 +1,36 @@
+//===- ErrnoTest.cpp - Error handling unit tests --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Errno.h"
+#include "gtest/gtest.h"
+
+using namespace llvm::sys;
+
+TEST(ErrnoTest, RetryAfterSignal) {
+  EXPECT_EQ(1, RetryAfterSignal(-1, [] { return 1; }));
+
+  EXPECT_EQ(-1, RetryAfterSignal(-1, [] {
+    errno = EAGAIN;
+    return -1;
+  }));
+  EXPECT_EQ(EAGAIN, errno);
+
+  unsigned calls = 0;
+  EXPECT_EQ(1, RetryAfterSignal(-1, [&calls] {
+              errno = EINTR;
+              ++calls;
+              return calls == 1 ? -1 : 1;
+            }));
+  EXPECT_EQ(2u, calls);
+
+  EXPECT_EQ(1, RetryAfterSignal(-1, [](int x) { return x; }, 1));
+
+  std::unique_ptr<int> P(RetryAfterSignal(nullptr, [] { return new int(47); }));
+  EXPECT_EQ(47, *P);
+}