Skip to content

[Windows] [mmap_data_loader.cpp] mmap equivalent for Windows #8916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 5, 2025
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
43 changes: 43 additions & 0 deletions extension/data_loader/mman.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

// This file ensures that mman.h compatible functions are defined in the global
// namespace for windows and posix environments.

#pragma once

#include <executorch/runtime/platform/compiler.h>

#ifndef _WIN32

#include <sys/mman.h>
#include <unistd.h>

ET_INLINE size_t get_os_page_size() {
return sysconf(_SC_PAGESIZE);
}

#else

#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#include <io.h>

#include <executorch/extension/data_loader/mman_windows.h>

ET_INLINE long get_os_page_size() {
SYSTEM_INFO si;
GetSystemInfo(&si);
long pagesize = si.dwAllocationGranularity > si.dwPageSize
? si.dwAllocationGranularity
: si.dwPageSize;
return pagesize;
}

#endif
251 changes: 251 additions & 0 deletions extension/data_loader/mman_windows.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/*
* Copyright (c) Google Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the MIT license.
*/

/*
* Adapted from: https://code.google.com/archive/p/mman-win32/
*
* mman-win32
* mman library for Windows
*
* A light implementation of the mmap functions for MinGW.
*
* The mmap-win32 library implements a wrapper for mmap functions around the
* memory mapping Windows API.
*/

#include <executorch/extension/data_loader/mman_windows.h>

#include <errno.h>
#include <io.h>
#include <windows.h>

#ifndef STATUS_SECTION_TOO_BIG
#define STATUS_SECTION_TOO_BIG ((NTSTATUS)0xC0000040L)
#endif

#ifndef FILE_MAP_EXECUTE
#define FILE_MAP_EXECUTE 0x0020
#endif /* FILE_MAP_EXECUTE */

#define RETURN_IF_FAILED(hr) \
do { \
if (FAILED((hr))) { \
return hr; \
} \
} while (false)

namespace {

HRESULT try_grow_process_memory_working_set(DWORD dwSizeRequired) {
// Get current working set
size_t minWorkingSetInitial;
size_t maxWorkingSet;
if (!GetProcessWorkingSetSize(
GetCurrentProcess(), &minWorkingSetInitial, &maxWorkingSet)) {
return GetLastError();
}

// Calculate new sizes
size_t minWorkingSet = minWorkingSetInitial + dwSizeRequired;
if (minWorkingSet < minWorkingSetInitial) {
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
}

if (maxWorkingSet < minWorkingSet) {
maxWorkingSet = minWorkingSet;
}

// Grow working set
if (!SetProcessWorkingSetSize(
GetCurrentProcess(), minWorkingSet, maxWorkingSet)) {
return GetLastError();
}
return S_OK;
}

HRESULT virtual_lock(void* pMem, DWORD dwSize) {
if (!VirtualLock(pMem, dwSize)) {
return GetLastError();
}
return S_OK;
}

HRESULT virtual_lock_allowing_working_set_growth(void* pMem, DWORD dwSize) {
HRESULT hr = virtual_lock(pMem, dwSize);

if (hr == HRESULT_FROM_WIN32(STATUS_SECTION_TOO_BIG)) {
// Attempt to grow the process working set and try again
RETURN_IF_FAILED(try_grow_process_memory_working_set(dwSize));
RETURN_IF_FAILED(virtual_lock(pMem, dwSize));
}

return hr;
}

static int __map_mman_error(const DWORD err, const int deferr) {
if (err == 0) {
return 0;
}
// TODO: implement
return err;
}

static DWORD __map_mmap_prot_page(const int prot) {
DWORD protect = 0;

if (prot == PROT_NONE) {
return protect;
}
if ((prot & PROT_EXEC) != 0) {
protect =
((prot & PROT_WRITE) != 0) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
} else {
protect = ((prot & PROT_WRITE) != 0) ? PAGE_READWRITE : PAGE_READONLY;
}
return protect;
}

static DWORD __map_mmap_prot_file(const int prot) {
DWORD desiredAccess = 0;

if (prot == PROT_NONE) {
return desiredAccess;
}
if ((prot & PROT_READ) != 0) {
desiredAccess |= FILE_MAP_READ;
}
if ((prot & PROT_WRITE) != 0) {
desiredAccess |= FILE_MAP_WRITE;
}
if ((prot & PROT_EXEC) != 0) {
desiredAccess |= FILE_MAP_EXECUTE;
}
return desiredAccess;
}

} // namespace

void* mmap(void* addr, size_t len, int prot, int flags, int fildes, off_t off) {
HANDLE fm, h;

void* map = MAP_FAILED;

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4293)
#endif

const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD))
? (DWORD)off
: (DWORD)(off & 0xFFFFFFFFL);
const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD))
? (DWORD)0
: (DWORD)((off >> 32) & 0xFFFFFFFFL);
const DWORD protect = __map_mmap_prot_page(prot);
const DWORD desiredAccess = __map_mmap_prot_file(prot);

const off_t maxSize = off + (off_t)len;

const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD))
? (DWORD)maxSize
: (DWORD)(maxSize & 0xFFFFFFFFL);
const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD))
? (DWORD)0
: (DWORD)((maxSize >> 32) & 0xFFFFFFFFL);

#ifdef _MSC_VER
#pragma warning(pop)
#endif

errno = 0;

if (len == 0
/* Unsupported flag combinations */
|| (flags & MAP_FIXED) != 0
/* Usupported protection combinations */
|| prot == PROT_EXEC) {
errno = EINVAL;
return MAP_FAILED;
}

h = ((flags & MAP_ANONYMOUS) == 0) ? (HANDLE)_get_osfhandle(fildes)
: INVALID_HANDLE_VALUE;

if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return MAP_FAILED;
}

fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL);

if (fm == NULL) {
errno = __map_mman_error(GetLastError(), EPERM);
return MAP_FAILED;
}

map =
MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len);

CloseHandle(fm);

if (map == NULL) {
errno = __map_mman_error(GetLastError(), EPERM);
return MAP_FAILED;
}

return map;
}

int munmap(void* addr, size_t len) {
if (UnmapViewOfFile(addr))
return 0;

errno = __map_mman_error(GetLastError(), EPERM);

return -1;
}

int mprotect(void* addr, size_t len, int prot) {
DWORD newProtect = __map_mmap_prot_page(prot);
DWORD oldProtect = 0;

if (VirtualProtect(addr, len, newProtect, &oldProtect))
return 0;

errno = __map_mman_error(GetLastError(), EPERM);

return -1;
}

int msync(void* addr, size_t len, int flags) {
if (FlushViewOfFile(addr, len))
return 0;

errno = __map_mman_error(GetLastError(), EPERM);

return -1;
}

int mlock(const void* addr, size_t len) {
HRESULT hr = virtual_lock_allowing_working_set_growth((LPVOID)addr, len);
if (SUCCEEDED(hr)) {
return 0;
}

errno = __map_mman_error(hr, EPERM);

return -1;
}

int munlock(const void* addr, size_t len) {
if (VirtualUnlock((LPVOID)addr, len))
return 0;

errno = __map_mman_error(GetLastError(), EPERM);

return -1;
}
68 changes: 68 additions & 0 deletions extension/data_loader/mman_windows.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) Google Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the MIT license.
*/

/*
* Adapted from: https://code.google.com/archive/p/mman-win32/
*
* mman-win32
* mman library for Windows
*
* A light implementation of the mmap functions for MinGW.
*
* The mmap-win32 library implements a wrapper for mmap functions around the
* memory mapping Windows API.
*/

#pragma once

#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT \
0x0501 // Change this to the appropriate value to target other versions of
// Windows.
#endif

/* All the headers include this file. */
#ifndef _MSC_VER
#include <_mingw.h>
#endif

#include <sys/types.h>

#ifdef __cplusplus
extern "C" {
#endif

#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4

#define MAP_FILE 0
#define MAP_SHARED 1
#define MAP_PRIVATE 2
#define MAP_TYPE 0xf
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS

#define MAP_FAILED ((void*)-1)

/* Flags for msync. */
#define MS_ASYNC 1
#define MS_SYNC 2
#define MS_INVALIDATE 4

void* mmap(void* addr, size_t len, int prot, int flags, int fildes, off_t off);
int munmap(void* addr, size_t len);
int mprotect(void* addr, size_t len, int prot);
int msync(void* addr, size_t len, int flags);
int mlock(const void* addr, size_t len);
int munlock(const void* addr, size_t len);

#ifdef __cplusplus
};
#endif
Loading
Loading