Skip to content

Commit

Permalink
[Support] Add RetryAfterSignal helper function
Browse files Browse the repository at this point in the history
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

llvm-svn: 305892
  • Loading branch information
labath committed Jun 21, 2017
1 parent 71d7213 commit 1f6aea2
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 25 deletions.
13 changes: 13 additions & 0 deletions llvm/include/llvm/Support/Errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define LLVM_SUPPORT_ERRNO_H

#include <string>
#include <type_traits>

namespace llvm {
namespace sys {
Expand All @@ -28,6 +29,18 @@ std::string StrError();
/// Like the no-argument version above, but uses \p errnum instead of errno.
std::string StrError(int errnum);

template <typename Fun, typename... Args,
typename ResultT =
typename std::result_of<Fun const &(const Args &...)>::type>
inline ResultT RetryAfterSignal(ResultT Fail, const Fun &F,
const Args &... As) {
ResultT Res;
do
Res = F(As...);
while (Res == Fail && errno == EINTR);
return Res;
}

} // namespace sys
} // namespace llvm

Expand Down
13 changes: 5 additions & 8 deletions llvm/lib/Support/MemoryBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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());
}
Expand Down
12 changes: 4 additions & 8 deletions llvm/lib/Support/Unix/Path.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 3 additions & 9 deletions llvm/lib/Support/Unix/Process.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_llvm_unittest(SupportTests
DebugTest.cpp
EndianStreamTest.cpp
EndianTest.cpp
ErrnoTest.cpp
ErrorOrTest.cpp
ErrorTest.cpp
FileOutputBufferTest.cpp
Expand Down
33 changes: 33 additions & 0 deletions llvm/unittests/Support/ErrnoTest.cpp
Original file line number Diff line number Diff line change
@@ -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));
}

0 comments on commit 1f6aea2

Please sign in to comment.