Files
pytorch/torch/nativert/common/FileUtil.cpp
Yuanyuan Chen 36871622f1 [2/N] Mark unused parameters in C++ code (#165121)
This is follow-up of #164912 to mark unused C++ parameters to improve code readability.

Pull Request resolved: https://github.com/pytorch/pytorch/pull/165121
Approved by: https://github.com/Skylion007
2025-10-15 03:04:39 +00:00

204 lines
5.3 KiB
C++

#include <torch/nativert/common/FileUtil.h>
#ifdef _WIN32
#include <io.h>
#define open _open
#define read _read
#define write _write
#define fileno _fileno
#define dup _dup
#else
#include <unistd.h>
#endif
#include <cerrno>
#include <c10/util/Exception.h>
#include <fmt/core.h>
namespace torch::nativert {
namespace {
int unistd_close(int fh) {
#ifdef _WIN32
return ::_close(fh);
#else
return ::close(fh);
#endif
}
inline void incr(ssize_t /*unused*/) {}
template <typename Offset>
inline void incr(ssize_t n, Offset& offset) {
offset += static_cast<Offset>(n);
}
// Wrap call to read/pread/write/pwrite(fd, buf, count, offset?) to retry on
// incomplete reads / writes. The variadic argument magic is there to support
// an additional argument (offset) for pread / pwrite; see the incr() functions
// above which do nothing if the offset is not present and increment it if it
// is.
template <class F, class... Offset>
ssize_t wrapFull(F f, int fd, void* buf, size_t count, Offset... offset) {
char* b = static_cast<char*>(buf);
ssize_t totalBytes = 0;
ssize_t r = -1;
do {
r = f(fd, b, count, offset...);
if (r == -1) {
if (errno == EINTR) {
continue;
}
return r;
}
totalBytes += r;
b += r;
count -= r;
incr(r, offset...);
} while (r != 0 && count); // 0 means EOF
return totalBytes;
}
int filterCloseReturn(int r) {
// Ignore EINTR. On Linux, close() may only return EINTR after the file
// descriptor has been closed, so you must not retry close() on EINTR --
// in the best case, you'll get EBADF, and in the worst case, you'll end up
// closing a different file (one opened from another thread).
//
// Interestingly enough, the Single Unix Specification says that the state
// of the file descriptor is unspecified if close returns EINTR. In that
// case, the safe thing to do is also not to retry close() -- leaking a file
// descriptor is definitely better than closing the wrong file.
if (r == -1 && errno == EINTR) {
return 0;
}
return r;
}
// The following wrapX() functions are private functions for wrapping file-io
// against interrupt and partial op completions.
// Wrap call to f(args) in loop to retry on EINTR
template <class F, class... Args>
ssize_t wrapNoInt(F f, Args... args) {
ssize_t r = -1;
do {
r = f(std::forward<Args>(args)...);
} while (r == -1 && errno == EINTR);
return r;
}
} // namespace
int openNoInt(const char* name, int flags, mode_t mode) {
// Android NDK bionic with FORTIFY has this definition:
// https://android.googlesource.com/platform/bionic/+/9349b9e51b/libc/include/bits/fortify/fcntl.h
// ```
// __BIONIC_ERROR_FUNCTION_VISIBILITY
// int open(const char* pathname, int flags, mode_t modes, ...) __overloadable
// __errorattr(__open_too_many_args_error);
// ```
// This is originally to prevent open() with incorrect parameters.
//
// However, combined with folly wrapNotInt, template deduction will fail.
// In this case, we create a custom lambda to bypass the error.
// The solution is referenced from
// https://github.com/llvm/llvm-project/commit/0a0e411204a2baa520fd73a8d69b664f98b428ba
//
auto openWrapper = [&] { return open(name, flags, mode); };
return int(wrapNoInt(openWrapper));
}
int closeNoInt(int fd) {
return filterCloseReturn(unistd_close(fd));
}
ssize_t writeFull(int fd, const void* buf, size_t count) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
return wrapFull(write, fd, const_cast<void*>(buf), count);
}
ssize_t readFull(int fd, void* buf, size_t count) {
return wrapFull(read, fd, buf, count);
}
File::File(int fd, bool ownsFd) noexcept : fd_(fd), ownsFd_(ownsFd) {
TORCH_CHECK(fd >= -1, "fd must be -1 or non-negative");
TORCH_CHECK(fd != -1 || !ownsFd, "cannot own -1");
}
File::File(std::string_view name, int flags, mode_t mode)
: fd_(::open(std::string(name).c_str(), flags, mode)), ownsFd_(false) {
TORCH_CHECK(
fd_ != 1,
"open(\"",
name,
"\", ",
flags,
", 0",
mode,
") returned stdout.")
ownsFd_ = true;
}
File::File(File&& other) noexcept : fd_(other.fd_), ownsFd_(other.ownsFd_) {
other.release();
}
File& File::operator=(File&& other) noexcept {
closeNoThrow();
swap(other);
return *this;
}
File::~File() {
auto fd = fd_;
if (!closeNoThrow()) { // ignore most errors
TORCH_CHECK(
errno != EBADF,
"closing fd ",
fd,
", it may already ",
"have been closed. Another time, this might close the wrong FD.");
}
}
/* static */ File File::temporary() {
// make a temp file with tmpfile(), dup the fd, then return it in a File.
FILE* tmpFile = tmpfile();
TORCH_CHECK(tmpFile != nullptr, "tmpfile() failed");
auto guard = c10::make_scope_exit([&]() { fclose(tmpFile); });
int fd = ::dup(fileno(tmpFile));
TORCH_CHECK(fd != -1, "dup() failed");
return File(fd, true);
}
int File::release() noexcept {
int released = fd_;
fd_ = -1;
ownsFd_ = false;
return released;
}
void File::swap(File& other) noexcept {
using std::swap;
swap(fd_, other.fd_);
swap(ownsFd_, other.ownsFd_);
}
void File::close() {
TORCH_CHECK(closeNoThrow(), "close() failed");
}
[[nodiscard]] bool File::closeNoThrow() {
int r = ownsFd_ ? unistd_close(fd_) : 0;
release();
return r == 0;
}
} // namespace torch::nativert