Get mmap() working with WIN32 MSVC

- We have pretty high quality POSIX polyfills now
- We no longer need to override malloc()

Tracked by issue #91
Improves upon #341
This commit is contained in:
Justine Tunney
2023-03-28 09:27:41 -07:00
parent e4881686b4
commit cbddf4661b
8 changed files with 700 additions and 751 deletions

280
mmap.h
View File

@@ -1,32 +1,17 @@
#pragma once
// portable mmap() implementation
//
// - supports win32 (needs vista+)
// - supports posix.1 (no map_anonymous)
//
// notes on windows
//
// - no errno support
// - not designed to support mprotect()
// - very poor support for memory intervals
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined(_MSC_VER) || defined(__MINGW32__)
#ifndef __MINGW32__
#include <Windows.h>
#include <strsafe.h>
#else
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _MSC_VER
#define NEED_WIN32_MMAP
#include <Windows.h>
#include <io.h>
#include <atomic>
#include <map>
#ifndef PROT_READ
#define PROT_READ 1
@@ -89,6 +74,26 @@
#define MADV_WILLNEED 3
#endif
#ifndef MS_ASYNC
#define MS_ASYNC 1
#endif
#ifndef MS_INVALIDATE
#define MS_INVALIDATE 2
#endif
#ifndef MS_SYNC
#define MS_SYNC 4
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#ifndef SEEK_END
#define SEEK_END 2
#endif
#ifndef mmap
#define mmap WinMap
#endif
@@ -101,8 +106,11 @@
#ifndef close
#define close _close
#endif
#ifndef fstat
#define fstat _fstati64
#ifndef lseek
#define lseek WinSeek
#endif
#ifndef msync
#define msync WinMsync
#endif
#ifndef madvise
#define madvise WinMadvise
@@ -111,219 +119,27 @@
#define ftruncate WinFtruncate
#endif
static std::atomic<unsigned> g_winlock;
static std::map<LPVOID, HANDLE> g_winmap;
uint64_t WinSeek(int, uint64_t, int);
int WinMsync(void *, uintptr_t, int);
int WinMadvise(void *, uintptr_t, int);
int WinFtruncate(int, uint64_t);
int WinUnmap(void *, uintptr_t);
void *WinMap(void *, uintptr_t, int, int, int, uint64_t);
#else // _MSC_VER
static void WinLock(void) {
while (!g_winlock.exchange(1, std::memory_order_acquire));
}
static void WunLock(void) {
g_winlock.store(0, std::memory_order_release);
}
static int WinMadvise(char* fd, size_t length, int flags) {
auto p_handle = GetCurrentProcess();
struct _WIN32_MEMORY_RANGE_ENTRY entry((void*)fd, length);
bool success = PrefetchVirtualMemory(p_handle, 1, &entry, 0);
if (!success) {
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD error_code = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + 256) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
error_code, lpMsgBuf);
}
return 0;
}
static int WinFtruncate(int fd, uint64_t length) {
return _chsize_s(fd, length) ? -1 : 0;
}
static int WinUnmap(void *addr, size_t length) {
HANDLE hand;
WinLock();
hand = g_winmap[addr];
g_winmap[addr] = 0;
WunLock();
if (hand) {
UnmapViewOfFile(addr);
CloseHandle(hand);
return 0;
} else {
return -1;
}
}
static void *WinMap(void *addr, size_t length, int prot,
int flags, int fd, uint64_t offset) {
HANDLE hFile;
DWORD winprot;
DWORD access = 0;
HANDLE hand = NULL;
LPVOID res = NULL;
if (prot & PROT_READ) {
access |= FILE_MAP_READ;
}
if (prot & PROT_WRITE) {
access |= FILE_MAP_WRITE;
}
if (prot & PROT_EXEC) {
access |= FILE_MAP_EXECUTE;
}
if (flags & MAP_PRIVATE) {
// private mapping
if (prot & PROT_EXEC) {
if (prot & PROT_WRITE) {
if (flags & MAP_ANONYMOUS) {
winprot = PAGE_EXECUTE_READWRITE;
} else {
winprot = PAGE_EXECUTE_WRITECOPY;
}
} else {
winprot = PAGE_EXECUTE_READ;
}
} else if (prot & PROT_WRITE) {
if (flags & MAP_ANONYMOUS) {
winprot = PAGE_READWRITE;
} else {
winprot = PAGE_WRITECOPY;
}
} else {
winprot = PAGE_READONLY;
}
} else {
// shared mapping
if (prot & PROT_EXEC) {
if (prot & PROT_WRITE) {
winprot = PAGE_EXECUTE_READWRITE;
} else {
winprot = PAGE_EXECUTE_READ;
}
} else if (prot & PROT_WRITE) {
winprot = PAGE_READWRITE;
} else {
winprot = PAGE_READONLY;
}
}
if (flags & MAP_ANONYMOUS) {
hFile = INVALID_HANDLE_VALUE;
offset = 0;
} else {
hFile = (HANDLE)_get_osfhandle(fd);
if (hFile == INVALID_HANDLE_VALUE) {
return MAP_FAILED;
}
}
if (flags & MAP_FIXED) {
if (!addr) {
return MAP_FAILED;
} else {
WinUnmap(addr, length);
}
}
hand = CreateFileMapping(hFile, 0, winprot,
(offset + length) >> 32,
(offset + length), 0);
if (!hand) {
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD error_code = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + 256) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
error_code, lpMsgBuf);
return MAP_FAILED;
}
if (winprot == PAGE_WRITECOPY) {
access = FILE_MAP_COPY;
}
res = MapViewOfFileEx(hand, access, offset >> 32,
offset, length, addr);
if (!res) {
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD error_code = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("failed with error %d: %s"),
error_code, lpMsgBuf);
fprintf(stderr, (char*)lpDisplayBuf);
CloseHandle(hand);
return MAP_FAILED;
}
WinLock();
g_winmap[res] = hand;
WunLock();
return res;
}
#else
#include <unistd.h>
#include <sys/mman.h>
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS 0x10000000
static void *PosixMmap(void *addr, size_t length, int prot,
int flags, int fd, uint64_t offset) {
int tfd;
void *res;
char path[] = "/tmp/llama.dat.XXXXXX";
if (~flags & MAP_ANONYMOUS) {
res = mmap(addr, length, prot, flags, fd, offset);
} else if ((tfd = mkstemp(path)) != -1) {
unlink(path);
if (!ftruncate(tfd, length)) {
res = mmap(addr, length, prot, flags & ~MAP_ANONYMOUS, tfd, 0);
} else {
res = MAP_FAILED;
}
close(tfd);
} else {
res = MAP_FAILED;
}
return res;
}
#ifndef mmap
#define NEED_POSIX_MMAP
#define mmap PosixMmap
#endif
#define MAP_ANONYMOUS 0x10000000
void *PosixMmap(void*, size_t, int, int, int, off_t);
#endif // MAP_ANONYMOUS
#endif // _MSC_VER
#ifdef __cplusplus
}
#endif