Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ jobs:
run: |
set -e -x
BINARY_SIZE_THRESHOLD_ARGS=""
echo "Binary size threshold in bytes: 1585152"
BINARY_SIZE_THRESHOLD_ARGS="--threshold_size_in_bytes 1585152"
echo "Binary size threshold in bytes: 1589248"
BINARY_SIZE_THRESHOLD_ARGS="--threshold_size_in_bytes 1589248"

# Ensure ANDROID_NDK_HOME is available and get its real path
if [ -z "$ANDROID_NDK_HOME" ]; then
Expand Down
40 changes: 40 additions & 0 deletions onnxruntime/core/platform/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,35 @@ std::ostream& operator<<(std::ostream& os, gsl::span<const LogicalProcessors>);
/// <returns>errno and the error message string if errno indicates an error.</returns>
std::pair<int, std::string> GetErrnoInfo();

/**
* An owned open file supporting concurrent positional reads.
*
* Reads and length queries refer to the same file even if its pathname is replaced.
* This is not a snapshot: callers must not modify the file in place while reading it.
* Keep the object alive until all callers have finished. Its destruction closes the file.
*/
class RandomAccessFile {
public:
virtual ~RandomAccessFile() = default;

// Query the open file, leaving length unchanged on failure.
virtual common::Status GetLength(size_t& length) const = 0;

/**
* Fill buffer starting at offset without changing a shared file position.
* Concurrent calls must use disjoint buffers. Returns only after all I/O has completed.
* Negative offsets, unrepresentable ranges, and unexpected EOF are errors.
* An empty buffer succeeds for any nonnegative offset. On failure, buffer may be partially written.
*/
virtual common::Status Read(FileOffsetType offset, gsl::span<char> buffer) const = 0;

protected:
RandomAccessFile() = default;

private:
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(RandomAccessFile);
};

/// \brief An interface used by the onnxruntime implementation to
/// access operating system functionality like the filesystem etc.
///
Expand Down Expand Up @@ -286,6 +315,17 @@ class Env {
// Returns empty string if there is no such environment variable available
virtual std::string GetEnvironmentVar(const std::string& var_name) const = 0;

/**
* Open a regular file for positional reads. Leaves file unchanged on failure.
* Retain the returned object across every read that must use the same file identity,
* for example throughout loading a tensor or all tensors from one external-data file.
* Custom environments can override this to supply their own file implementation.
*/
virtual common::Status OpenRandomAccessFile(const ORTCHAR_T* /*file_path*/,
std::unique_ptr<RandomAccessFile>& /*file*/) const {
return ORT_MAKE_STATUS(ONNXRUNTIME, NOT_IMPLEMENTED, "This environment does not support random-access files.");
}

protected:
Env();

Expand Down
109 changes: 86 additions & 23 deletions onnxruntime/core/platform/posix/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,72 @@
return result;
}

common::Status ReportSystemError(const char* operation_name, const std::string& path) {
auto [err_no, err_msg] = GetErrnoInfo();
std::ostringstream oss;
oss << operation_name << " file \"" << path << "\" failed: " << err_msg;
return common::Status(common::SYSTEM, err_no, oss.str());
}

common::Status GetFileLength(int fd, size_t& file_size) {
if (fd < 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Invalid fd was supplied: ", fd);
}

struct stat buf;
if (TempFailureRetry(fstat, fd, &buf) < 0) {
return ReportSystemError("fstat", "");
}
if (buf.st_size < 0) {
return ORT_MAKE_STATUS(SYSTEM, FAIL, "Received negative size from stat call");
}
if (static_cast<uintmax_t>(buf.st_size) > std::numeric_limits<size_t>::max()) {
return ORT_MAKE_STATUS(SYSTEM, FAIL, "File is too large.");
}

file_size = static_cast<size_t>(buf.st_size);
return common::Status::OK();
}

class PosixRandomAccessFile final : public RandomAccessFile {
public:
PosixRandomAccessFile(ScopedFileDescriptor descriptor, std::string path)
: descriptor_(std::move(descriptor)), path_(std::move(path)) {}

common::Status GetLength(size_t& length) const override {
return GetFileLength(descriptor_.Get(), length);
}

common::Status Read(FileOffsetType offset, gsl::span<char> buffer) const override {
if (offset < 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "RandomAccessFile::Read: offset must be nonnegative.");
}
if (static_cast<uintmax_t>(buffer.size()) >
static_cast<uintmax_t>(std::numeric_limits<FileOffsetType>::max() - offset)) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "RandomAccessFile::Read: file range is not representable.");
}

size_t total_bytes_read = 0;
while (total_bytes_read < buffer.size()) {
constexpr size_t kMaxBytesToRead = 1 << 30;
const auto bytes_to_read = std::min(buffer.size() - total_bytes_read, kMaxBytesToRead);
const auto bytes_read = TempFailureRetry(pread, descriptor_.Get(), buffer.data() + total_bytes_read,
bytes_to_read, offset + static_cast<FileOffsetType>(total_bytes_read));
if (bytes_read < 0) {
return ReportSystemError("pread", path_);
}
ORT_RETURN_IF(bytes_read == 0, "RandomAccessFile::Read: unexpected end of file: ", path_);
total_bytes_read += static_cast<size_t>(bytes_read);
}
return common::Status::OK();
}

private:
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(PosixRandomAccessFile);
ScopedFileDescriptor descriptor_;
const std::string path_;
};

// nftw() callback to remove a file
int nftw_remove(
const char* fpath, const struct stat* /*sb*/,
Expand Down Expand Up @@ -371,26 +437,30 @@
}

common::Status GetFileLength(int fd, /*out*/ size_t& file_size) const override {
using namespace common;
if (fd < 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Invalid fd was supplied: ", fd);
}
return onnxruntime::GetFileLength(fd, file_size);
}

struct stat buf;
int rc = fstat(fd, &buf);
if (rc < 0) {
return ReportSystemError("fstat", "");
common::Status OpenRandomAccessFile(const ORTCHAR_T* file_path,
std::unique_ptr<RandomAccessFile>& file) const override {
if (file_path == nullptr) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "file_path == nullptr");
}

if (buf.st_size < 0) {
return ORT_MAKE_STATUS(SYSTEM, FAIL, "Received negative size from stat call");
// Nonblocking open lets us reject FIFOs without waiting for a writer.
int flags = O_RDONLY | O_NONBLOCK;
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif
// Android's fortified open is overloaded; resolve the call inside a lambda.
ScopedFileDescriptor descriptor{static_cast<int>(TempFailureRetry([&] { return open(file_path, flags); }))};
if (!descriptor.IsValid()) {
return ReportSystemError("open", file_path);
}

if (static_cast<unsigned long long>(buf.st_size) > std::numeric_limits<size_t>::max()) {
return ORT_MAKE_STATUS(SYSTEM, FAIL, "File is too large.");
struct stat info;
if (TempFailureRetry(fstat, descriptor.Get(), &info) < 0) {
return ReportSystemError("fstat", file_path);
}

file_size = static_cast<size_t>(buf.st_size);
ORT_RETURN_IF_NOT(S_ISREG(info.st_mode), "Random-access reads require a regular file: ", file_path);
file = std::make_unique<PosixRandomAccessFile>(std::move(descriptor), file_path);

Check warning on line 463 in onnxruntime/core/platform/posix/env.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <memory> for make_unique<> [build/include_what_you_use] [4] Raw Output: onnxruntime/core/platform/posix/env.cc:463: Add #include <memory> for make_unique<> [build/include_what_you_use] [4]
return Status::OK();
}

Expand Down Expand Up @@ -486,13 +556,6 @@
return Status::OK();
}

static common::Status ReportSystemError(const char* operation_name, const std::string& path) {
auto [err_no, err_msg] = GetErrnoInfo();
std::ostringstream oss;
oss << operation_name << " file \"" << path << "\" failed: " << err_msg;
return common::Status(common::SYSTEM, err_no, oss.str());
}

bool FolderExists(const std::string& path) const override {
struct stat sb;
if (stat(path.c_str(), &sb)) {
Expand Down
117 changes: 117 additions & 0 deletions onnxruntime/core/platform/windows/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ limitations under the License.
#include <iostream>
#include <fstream>
#include <filesystem>
#include <limits>
#include <optional>
#include <string>
#include <thread>
Expand Down Expand Up @@ -357,6 +358,122 @@ common::Status WindowsEnv::GetFileLength(int fd, /*out*/ size_t& file_size) cons
return Status::OK();
}

namespace {

class WindowsRandomAccessFile final : public RandomAccessFile {
public:
explicit WindowsRandomAccessFile(wil::unique_hfile file_handle) : file_handle_(std::move(file_handle)) {}
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(WindowsRandomAccessFile);

Status GetLength(size_t& length) const override {
LARGE_INTEGER file_size{};
if (!GetFileSizeEx(file_handle_.get(), &file_size)) {
return FileError("GetFileSizeEx", GetLastError());
}
if (file_size.QuadPart < 0 ||
static_cast<ULONGLONG>(file_size.QuadPart) > std::numeric_limits<size_t>::max()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "RandomAccessFile: invalid or unrepresentable file length");
}
length = static_cast<size_t>(file_size.QuadPart);
return Status::OK();
}

Status Read(FileOffsetType offset, gsl::span<char> buffer) const override {
if (offset < 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "RandomAccessFile: offset < 0");
}
if (buffer.size() > static_cast<uint64_t>(std::numeric_limits<FileOffsetType>::max() - offset)) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "RandomAccessFile: offset + length overflows");
}
if (buffer.empty()) {
return Status::OK();
}

// Each caller owns its event and OVERLAPPED; neither the file cursor nor another caller's event is used.
wil::unique_handle event{CreateEventExW(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS)};
if (!event) {
return FileError("CreateEventExW", GetLastError());
}

size_t total_bytes_read = 0;
while (total_bytes_read < buffer.size()) {
OVERLAPPED overlapped{};
const auto current_offset = static_cast<uint64_t>(offset) + total_bytes_read;
overlapped.Offset = static_cast<DWORD>(current_offset & 0xFFFFFFFF);
overlapped.OffsetHigh = static_cast<DWORD>(current_offset >> 32);
overlapped.hEvent = event.get();
constexpr size_t kMaxBytesToRead = 1 << 30;
const DWORD bytes_to_read =
static_cast<DWORD>(std::min(buffer.size() - total_bytes_read, kMaxBytesToRead));
if (!ReadFile(file_handle_.get(), buffer.data() + total_bytes_read, bytes_to_read, nullptr, &overlapped)) {
const auto error_code = GetLastError();
if (error_code != ERROR_IO_PENDING) {
return FileError("ReadFile", error_code);
}
}

DWORD bytes_read = 0;
if (!GetOverlappedResult(file_handle_.get(), &overlapped, &bytes_read, TRUE)) {
const auto error_code = GetLastError();
// A failed wait must not let outstanding I/O outlive the buffer, OVERLAPPED, or event.
if (!HasOverlappedIoCompleted(&overlapped)) {
(void)CancelIoEx(file_handle_.get(), &overlapped);
do {
(void)GetOverlappedResult(file_handle_.get(), &overlapped, &bytes_read, TRUE);
} while (!HasOverlappedIoCompleted(&overlapped));
}
return FileError("GetOverlappedResult", error_code);
}
if (bytes_read == 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "RandomAccessFile: unexpected end of file");
}
total_bytes_read += bytes_read;
}
return Status::OK();
}

private:
static Status FileError(const char* operation, DWORD error_code) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "RandomAccessFile: ", operation, " failed, errcode = ",
error_code, " - ", std::system_category().message(error_code));
}

wil::unique_hfile file_handle_;
};

} // namespace

Status WindowsEnv::OpenRandomAccessFile(_In_z_ const ORTCHAR_T* file_path,
std::unique_ptr<RandomAccessFile>& file) const {
if (file_path == nullptr) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "OpenRandomAccessFile: file_path == nullptr");
}
CREATEFILE2_EXTENDED_PARAMETERS parameters{};
parameters.dwSize = sizeof(parameters);
parameters.dwFileFlags = FILE_FLAG_OVERLAPPED;
wil::unique_hfile file_handle{
CreateFile2(file_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, OPEN_EXISTING, &parameters)};
if (file_handle.get() == INVALID_HANDLE_VALUE) {
const auto error_code = GetLastError();
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "open file ", ToUTF8String(Basename(file_path)),
" fail, errcode = ", error_code, " - ", std::system_category().message(error_code));
}
if (GetFileType(file_handle.get()) != FILE_TYPE_DISK) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "OpenRandomAccessFile: expected a disk file");
}
BY_HANDLE_FILE_INFORMATION information{};
if (!GetFileInformationByHandle(file_handle.get(), &information)) {
const auto error_code = GetLastError();
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "GetFileInformationByHandle failed, errcode = ",
error_code, " - ", std::system_category().message(error_code));
}
if ((information.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "OpenRandomAccessFile: expected a regular file");
}
file = std::make_unique<WindowsRandomAccessFile>(std::move(file_handle));
return Status::OK();
}

Status WindowsEnv::ReadFileIntoBuffer(_In_z_ const ORTCHAR_T* const file_path, const FileOffsetType offset, const size_t length,
const gsl::span<char> buffer) const {
ORT_RETURN_IF_NOT(file_path, "file_path == nullptr");
Expand Down
2 changes: 2 additions & 0 deletions onnxruntime/core/platform/windows/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
PIDType GetSelfPid() const override;
Status GetFileLength(_In_z_ const ORTCHAR_T* file_path, size_t& length) const override;
common::Status GetFileLength(int fd, /*out*/ size_t& file_size) const override;
Status OpenRandomAccessFile(_In_z_ const ORTCHAR_T* file_path,
std::unique_ptr<RandomAccessFile>& file) const override;

Check warning on line 65 in onnxruntime/core/platform/windows/env.h

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <memory> for unique_ptr<> [build/include_what_you_use] [4] Raw Output: onnxruntime/core/platform/windows/env.h:65: Add #include <memory> for unique_ptr<> [build/include_what_you_use] [4]
Status ReadFileIntoBuffer(_In_z_ const ORTCHAR_T* const file_path, const FileOffsetType offset, const size_t length,
const gsl::span<char> buffer) const override;
Status MapFileIntoMemory(_In_z_ const ORTCHAR_T* file_path,
Expand Down
Loading
Loading