Skip to content

Commit 286f1fe

Browse files
[Windows] [mmap_data_loader.cpp] mmap equivalent for Windows (#8916)
* [Windows Build] Implement MMAP for mmap_data_loader.cpp There is no sys/mman.h or posix-compatible mmap() implementation on Windows. The extension data loaders use it to map in data files, so adding an implementation. Test-run, & .\cmake-out\extension\data_loader\test\Debug\extension_data_loader_test.exe --gtest_brief=1 --gtest_filter=MmapDataLoader* Running main() from ...\executorch\third-party\googletest\googletest\src\gtest_main.cc [==========] 8 tests from 1 test suite ran. (50 ms total) [ PASSED ] 8 tests. * apply code suggestions * fix src -> srcs typo * try to fix build * fix src/headers brackets --------- Co-authored-by: Jeff Whiteside <jwhites@microsoft.com>
1 parent 5ac4a3c commit 286f1fe

File tree

6 files changed

+386
-9
lines changed

6 files changed

+386
-9
lines changed

extension/data_loader/mman.h

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
// This file ensures that mman.h compatible functions are defined in the global
10+
// namespace for windows and posix environments.
11+
12+
#pragma once
13+
14+
#include <executorch/runtime/platform/compiler.h>
15+
16+
#ifndef _WIN32
17+
18+
#include <sys/mman.h>
19+
#include <unistd.h>
20+
21+
ET_INLINE size_t get_os_page_size() {
22+
return sysconf(_SC_PAGESIZE);
23+
}
24+
25+
#else
26+
27+
#define NOMINMAX
28+
#include <windows.h>
29+
#undef NOMINMAX
30+
#include <io.h>
31+
32+
#include <executorch/extension/data_loader/mman_windows.h>
33+
34+
ET_INLINE long get_os_page_size() {
35+
SYSTEM_INFO si;
36+
GetSystemInfo(&si);
37+
long pagesize = si.dwAllocationGranularity > si.dwPageSize
38+
? si.dwAllocationGranularity
39+
: si.dwPageSize;
40+
return pagesize;
41+
}
42+
43+
#endif
+251
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/*
2+
* Copyright (c) Google Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license.
6+
*/
7+
8+
/*
9+
* Adapted from: https://code.google.com/archive/p/mman-win32/
10+
*
11+
* mman-win32
12+
* mman library for Windows
13+
*
14+
* A light implementation of the mmap functions for MinGW.
15+
*
16+
* The mmap-win32 library implements a wrapper for mmap functions around the
17+
* memory mapping Windows API.
18+
*/
19+
20+
#include <executorch/extension/data_loader/mman_windows.h>
21+
22+
#include <errno.h>
23+
#include <io.h>
24+
#include <windows.h>
25+
26+
#ifndef STATUS_SECTION_TOO_BIG
27+
#define STATUS_SECTION_TOO_BIG ((NTSTATUS)0xC0000040L)
28+
#endif
29+
30+
#ifndef FILE_MAP_EXECUTE
31+
#define FILE_MAP_EXECUTE 0x0020
32+
#endif /* FILE_MAP_EXECUTE */
33+
34+
#define RETURN_IF_FAILED(hr) \
35+
do { \
36+
if (FAILED((hr))) { \
37+
return hr; \
38+
} \
39+
} while (false)
40+
41+
namespace {
42+
43+
HRESULT try_grow_process_memory_working_set(DWORD dwSizeRequired) {
44+
// Get current working set
45+
size_t minWorkingSetInitial;
46+
size_t maxWorkingSet;
47+
if (!GetProcessWorkingSetSize(
48+
GetCurrentProcess(), &minWorkingSetInitial, &maxWorkingSet)) {
49+
return GetLastError();
50+
}
51+
52+
// Calculate new sizes
53+
size_t minWorkingSet = minWorkingSetInitial + dwSizeRequired;
54+
if (minWorkingSet < minWorkingSetInitial) {
55+
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
56+
}
57+
58+
if (maxWorkingSet < minWorkingSet) {
59+
maxWorkingSet = minWorkingSet;
60+
}
61+
62+
// Grow working set
63+
if (!SetProcessWorkingSetSize(
64+
GetCurrentProcess(), minWorkingSet, maxWorkingSet)) {
65+
return GetLastError();
66+
}
67+
return S_OK;
68+
}
69+
70+
HRESULT virtual_lock(void* pMem, DWORD dwSize) {
71+
if (!VirtualLock(pMem, dwSize)) {
72+
return GetLastError();
73+
}
74+
return S_OK;
75+
}
76+
77+
HRESULT virtual_lock_allowing_working_set_growth(void* pMem, DWORD dwSize) {
78+
HRESULT hr = virtual_lock(pMem, dwSize);
79+
80+
if (hr == HRESULT_FROM_WIN32(STATUS_SECTION_TOO_BIG)) {
81+
// Attempt to grow the process working set and try again
82+
RETURN_IF_FAILED(try_grow_process_memory_working_set(dwSize));
83+
RETURN_IF_FAILED(virtual_lock(pMem, dwSize));
84+
}
85+
86+
return hr;
87+
}
88+
89+
static int __map_mman_error(const DWORD err, const int deferr) {
90+
if (err == 0) {
91+
return 0;
92+
}
93+
// TODO: implement
94+
return err;
95+
}
96+
97+
static DWORD __map_mmap_prot_page(const int prot) {
98+
DWORD protect = 0;
99+
100+
if (prot == PROT_NONE) {
101+
return protect;
102+
}
103+
if ((prot & PROT_EXEC) != 0) {
104+
protect =
105+
((prot & PROT_WRITE) != 0) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
106+
} else {
107+
protect = ((prot & PROT_WRITE) != 0) ? PAGE_READWRITE : PAGE_READONLY;
108+
}
109+
return protect;
110+
}
111+
112+
static DWORD __map_mmap_prot_file(const int prot) {
113+
DWORD desiredAccess = 0;
114+
115+
if (prot == PROT_NONE) {
116+
return desiredAccess;
117+
}
118+
if ((prot & PROT_READ) != 0) {
119+
desiredAccess |= FILE_MAP_READ;
120+
}
121+
if ((prot & PROT_WRITE) != 0) {
122+
desiredAccess |= FILE_MAP_WRITE;
123+
}
124+
if ((prot & PROT_EXEC) != 0) {
125+
desiredAccess |= FILE_MAP_EXECUTE;
126+
}
127+
return desiredAccess;
128+
}
129+
130+
} // namespace
131+
132+
void* mmap(void* addr, size_t len, int prot, int flags, int fildes, off_t off) {
133+
HANDLE fm, h;
134+
135+
void* map = MAP_FAILED;
136+
137+
#ifdef _MSC_VER
138+
#pragma warning(push)
139+
#pragma warning(disable : 4293)
140+
#endif
141+
142+
const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD))
143+
? (DWORD)off
144+
: (DWORD)(off & 0xFFFFFFFFL);
145+
const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD))
146+
? (DWORD)0
147+
: (DWORD)((off >> 32) & 0xFFFFFFFFL);
148+
const DWORD protect = __map_mmap_prot_page(prot);
149+
const DWORD desiredAccess = __map_mmap_prot_file(prot);
150+
151+
const off_t maxSize = off + (off_t)len;
152+
153+
const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD))
154+
? (DWORD)maxSize
155+
: (DWORD)(maxSize & 0xFFFFFFFFL);
156+
const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD))
157+
? (DWORD)0
158+
: (DWORD)((maxSize >> 32) & 0xFFFFFFFFL);
159+
160+
#ifdef _MSC_VER
161+
#pragma warning(pop)
162+
#endif
163+
164+
errno = 0;
165+
166+
if (len == 0
167+
/* Unsupported flag combinations */
168+
|| (flags & MAP_FIXED) != 0
169+
/* Usupported protection combinations */
170+
|| prot == PROT_EXEC) {
171+
errno = EINVAL;
172+
return MAP_FAILED;
173+
}
174+
175+
h = ((flags & MAP_ANONYMOUS) == 0) ? (HANDLE)_get_osfhandle(fildes)
176+
: INVALID_HANDLE_VALUE;
177+
178+
if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) {
179+
errno = EBADF;
180+
return MAP_FAILED;
181+
}
182+
183+
fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL);
184+
185+
if (fm == NULL) {
186+
errno = __map_mman_error(GetLastError(), EPERM);
187+
return MAP_FAILED;
188+
}
189+
190+
map =
191+
MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len);
192+
193+
CloseHandle(fm);
194+
195+
if (map == NULL) {
196+
errno = __map_mman_error(GetLastError(), EPERM);
197+
return MAP_FAILED;
198+
}
199+
200+
return map;
201+
}
202+
203+
int munmap(void* addr, size_t len) {
204+
if (UnmapViewOfFile(addr))
205+
return 0;
206+
207+
errno = __map_mman_error(GetLastError(), EPERM);
208+
209+
return -1;
210+
}
211+
212+
int mprotect(void* addr, size_t len, int prot) {
213+
DWORD newProtect = __map_mmap_prot_page(prot);
214+
DWORD oldProtect = 0;
215+
216+
if (VirtualProtect(addr, len, newProtect, &oldProtect))
217+
return 0;
218+
219+
errno = __map_mman_error(GetLastError(), EPERM);
220+
221+
return -1;
222+
}
223+
224+
int msync(void* addr, size_t len, int flags) {
225+
if (FlushViewOfFile(addr, len))
226+
return 0;
227+
228+
errno = __map_mman_error(GetLastError(), EPERM);
229+
230+
return -1;
231+
}
232+
233+
int mlock(const void* addr, size_t len) {
234+
HRESULT hr = virtual_lock_allowing_working_set_growth((LPVOID)addr, len);
235+
if (SUCCEEDED(hr)) {
236+
return 0;
237+
}
238+
239+
errno = __map_mman_error(hr, EPERM);
240+
241+
return -1;
242+
}
243+
244+
int munlock(const void* addr, size_t len) {
245+
if (VirtualUnlock((LPVOID)addr, len))
246+
return 0;
247+
248+
errno = __map_mman_error(GetLastError(), EPERM);
249+
250+
return -1;
251+
}

extension/data_loader/mman_windows.h

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) Google Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license.
6+
*/
7+
8+
/*
9+
* Adapted from: https://code.google.com/archive/p/mman-win32/
10+
*
11+
* mman-win32
12+
* mman library for Windows
13+
*
14+
* A light implementation of the mmap functions for MinGW.
15+
*
16+
* The mmap-win32 library implements a wrapper for mmap functions around the
17+
* memory mapping Windows API.
18+
*/
19+
20+
#pragma once
21+
22+
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
23+
#define _WIN32_WINNT \
24+
0x0501 // Change this to the appropriate value to target other versions of
25+
// Windows.
26+
#endif
27+
28+
/* All the headers include this file. */
29+
#ifndef _MSC_VER
30+
#include <_mingw.h>
31+
#endif
32+
33+
#include <sys/types.h>
34+
35+
#ifdef __cplusplus
36+
extern "C" {
37+
#endif
38+
39+
#define PROT_NONE 0
40+
#define PROT_READ 1
41+
#define PROT_WRITE 2
42+
#define PROT_EXEC 4
43+
44+
#define MAP_FILE 0
45+
#define MAP_SHARED 1
46+
#define MAP_PRIVATE 2
47+
#define MAP_TYPE 0xf
48+
#define MAP_FIXED 0x10
49+
#define MAP_ANONYMOUS 0x20
50+
#define MAP_ANON MAP_ANONYMOUS
51+
52+
#define MAP_FAILED ((void*)-1)
53+
54+
/* Flags for msync. */
55+
#define MS_ASYNC 1
56+
#define MS_SYNC 2
57+
#define MS_INVALIDATE 4
58+
59+
void* mmap(void* addr, size_t len, int prot, int flags, int fildes, off_t off);
60+
int munmap(void* addr, size_t len);
61+
int mprotect(void* addr, size_t len, int prot);
62+
int msync(void* addr, size_t len, int flags);
63+
int mlock(const void* addr, size_t len);
64+
int munlock(const void* addr, size_t len);
65+
66+
#ifdef __cplusplus
67+
};
68+
#endif

0 commit comments

Comments
 (0)