Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
104 changes: 104 additions & 0 deletions onnxruntime/core/platform/windows/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ namespace onnxruntime {

namespace {

class UnmapFileParam {
public:
void* addr;
size_t len;
};

static void UnmapFile(void* param) noexcept {
UnmapFileParam* p = reinterpret_cast<UnmapFileParam*>(param);
bool ret = UnmapViewOfFile(p->addr);
if (!ret) {
const auto error_code = GetLastError();
LOGS_DEFAULT(ERROR) << "unmap view of file failed. error code: " << error_code
<< " error msg: " << std::system_category().message(error_code);
}
delete p;
}

std::wstring Basename(const std::wstring& path) {
auto basename_index = path.find_last_of(L"/\\") + 1; // results in 0 if no separator is found
return path.substr(basename_index);
Expand Down Expand Up @@ -320,8 +337,95 @@ class WindowsEnv : public Env {
return Status::OK();
}

/**
Status MapFileIntoMemory(_In_z_ const ORTCHAR_T*, FileOffsetType, size_t, MappedMemoryPtr&) const override {
return ORT_MAKE_STATUS(ONNXRUNTIME, NOT_IMPLEMENTED, "MapFileIntoMemory is not implemented on Windows.");
}*/

Status MapFileIntoMemory(_In_z_ const ORTCHAR_T* file_path,
FileOffsetType offset,
size_t length,
MappedMemoryPtr& mapped_memory) const override {
ORT_RETURN_IF_NOT(file_path, "file_path == nullptr");
ORT_RETURN_IF_NOT(offset >= 0, "offset < 0");

if (length == 0) {
mapped_memory = MappedMemoryPtr{};
return Status::OK();
}

#if WINVER >= _WIN32_WINNT_WIN8
wil::unique_hfile file_handle{
CreateFile2(file_path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, NULL)};
#else
wil::unique_hfile file_handle{
CreateFileW(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)};
#endif
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 NTDDI_VERSION >= NTDDI_WIN10_RS5
wil::unique_hfile file_mapping_handle{
CreateFileMapping2(file_handle.get(),
nullptr,
FILE_MAP_READ,
PAGE_READONLY,
SEC_COMMIT,
0,
nullptr,
nullptr,
0)};
#else
wil::unique_hfile file_mapping_handle{
CreateFileMappingW(file_handle.get(),
nullptr,
PAGE_READONLY,
0,
0,
nullptr)};
#endif
if (file_mapping_handle.get() == INVALID_HANDLE_VALUE) {
const auto error_code = GetLastError();
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL,
"open file mapping ", ToUTF8String(Basename(file_path)),
" fail, errcode = ", error_code,
" - ", std::system_category().message(error_code));
}

SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);

static const DWORD page_size = sysinfo.dwPageSize;
static const DWORD allocation_granularity = sysinfo.dwAllocationGranularity;
const FileOffsetType offset_to_page = offset % static_cast<FileOffsetType>(page_size);
const size_t mapped_length = length + static_cast<size_t>(offset_to_page);
const FileOffsetType mapped_offset = offset - offset_to_page;
if (mapped_offset % allocation_granularity != 0) {
const auto error_code = GetLastError();
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL,
"mapped offset must be a multiple of the allocation granularity",
" , mapped_offset = ", mapped_offset,
" , allocation_granularity = ", allocation_granularity,
" , errcode = ", error_code,
" - ", std::system_category().message(error_code));
}

void* const mapped_base = MapViewOfFile(file_mapping_handle.get(),
FILE_MAP_READ,
0,
static_cast<DWORD>(mapped_offset),
mapped_length);

mapped_memory =
MappedMemoryPtr{reinterpret_cast<char*>(mapped_base) + offset_to_page,
OrtCallbackInvoker{OrtCallback{UnmapFile, new UnmapFileParam{mapped_base, mapped_length}}}};

return Status::OK();
}

bool FolderExists(const std::wstring& path) const override {
Expand Down
59 changes: 59 additions & 0 deletions onnxruntime/test/platform/file_io_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#ifndef _WIN32
#include <unistd.h> // for sysconf() and _SC_PAGESIZE
#else
#include <Windows.h>
#endif

#include "gsl/gsl"
Expand Down Expand Up @@ -61,7 +63,11 @@ std::vector<char> GenerateData(size_t length, uint32_t seed = 0) {
}

void WriteDataToFile(gsl::span<const char> data, const PathString& path) {
#ifndef _WIN32
std::ofstream out{path, std::ios_base::out | std::ios_base::trunc};
#else
std::ofstream out{path, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary};
Comment thread
snnn marked this conversation as resolved.
#endif
out.write(data.data(), data.size());
}

Expand Down Expand Up @@ -144,6 +150,59 @@ TEST(FileIoTest, MapFileIntoMemory) {
ASSERT_FALSE(Env::Default().MapFileIntoMemory(tmp.path.c_str(), -1, 0, mapped_memory).IsOK());
}
}
#else
TEST(FileIoTest, MapFileIntoMemory) {
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
static const auto page_size = sysinfo.dwPageSize;
static const auto allocation_granularity = sysinfo.dwAllocationGranularity;
ASSERT_GT(page_size, static_cast<DWORD>(0));

TempFilePath tmp(ORT_TSTR("map_file_test_"));
const auto expected_data = GenerateData(page_size * 3 / 2);
WriteDataToFile(gsl::make_span(expected_data), tmp.path);

const auto offsets_and_lengths = GenerateValidOffsetLengthPairs(
0, expected_data.size(), page_size / 10);

for (const auto& offset_and_length : offsets_and_lengths) {
const auto offset = offset_and_length.first;
const auto length = offset_and_length.second;

// The offset must be a multiple of the allocation granularity
if (offset % allocation_granularity != 0) {
continue;
}

Env::MappedMemoryPtr mapped_memory{};
auto status = Env::Default().MapFileIntoMemory(
tmp.path.c_str(), offset, length, mapped_memory);
ASSERT_TRUE(status.IsOK())
<< "MapFileIntoMemory failed for offset " << offset << " and length " << length
<< " with error: " << status.ErrorMessage();

auto mapped_span = gsl::make_span(mapped_memory.get(), length);

auto expected_data_span = gsl::make_span(expected_data.data() + offset, length);

ASSERT_EQ(mapped_span, expected_data_span);
}

{
Env::MappedMemoryPtr mapped_memory{};

// invalid - offset is not a multiple of the allocation granularity
ASSERT_FALSE(Env::Default().MapFileIntoMemory(
tmp.path.c_str(), allocation_granularity * 3 / 2, page_size / 10, mapped_memory).IsOK());
}

{
Env::MappedMemoryPtr mapped_memory{};

// invalid - negative offset
ASSERT_FALSE(Env::Default().MapFileIntoMemory(tmp.path.c_str(), -1, 0, mapped_memory).IsOK());
}
}
#endif

} // namespace test
Expand Down