Index: include/llvm/Support/Errno.h =================================================================== --- include/llvm/Support/Errno.h +++ include/llvm/Support/Errno.h @@ -15,6 +15,7 @@ #define LLVM_SUPPORT_ERRNO_H #include +#include namespace llvm { namespace sys { @@ -28,6 +29,17 @@ /// Like the no-argument version above, but uses \p errnum instead of errno. std::string StrError(int errnum); +template +inline typename std::result_of::type +RetryAfterSignal(typename std::result_of::type Fail, + const Fun &F, const Args &... As) { + typename std::result_of::type Res; + do + Res = F(As...); + while (Res == Fail && errno == EINTR); + return Res; +} + } // namespace sys } // namespace llvm Index: lib/Support/MemoryBuffer.cpp =================================================================== --- lib/Support/MemoryBuffer.cpp +++ lib/Support/MemoryBuffer.cpp @@ -240,11 +240,9 @@ // 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 @@ 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: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -737,10 +737,8 @@ #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 @@ 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: lib/Support/Unix/Process.inc =================================================================== --- lib/Support/Unix/Process.inc +++ lib/Support/Unix/Process.inc @@ -207,13 +207,10 @@ 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 @@ 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: unittests/Support/CMakeLists.txt =================================================================== --- unittests/Support/CMakeLists.txt +++ unittests/Support/CMakeLists.txt @@ -21,6 +21,7 @@ DebugTest.cpp EndianStreamTest.cpp EndianTest.cpp + ErrnoTest.cpp ErrorOrTest.cpp ErrorTest.cpp FileOutputBufferTest.cpp Index: unittests/Support/ErrnoTest.cpp =================================================================== --- /dev/null +++ unittests/Support/ErrnoTest.cpp @@ -0,0 +1,33 @@ +//===- 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)); +}