|
@@ -1,71 +1,413 @@
|
|
|
/*
|
|
/*
|
|
|
- * file-mmap-posix.c: File mmap internal calls
|
|
|
|
|
|
|
+ * file-mmap-windows.c: MemoryMappedFile internal calls for Windows
|
|
|
*
|
|
*
|
|
|
- * Author:
|
|
|
|
|
- * Rodrigo Kumpera
|
|
|
|
|
- *
|
|
|
|
|
- * Copyright 2014 Xamarin Inc (http://www.xamarin.com)
|
|
|
|
|
|
|
+ * Copyright 2016 Microsoft
|
|
|
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
|
|
+/*
|
|
|
|
|
+ * The code in this file has been inspired by the CoreFX MemoryMappedFile Windows implementation contained in the files
|
|
|
|
|
+ *
|
|
|
|
|
+ * https://github.com/dotnet/corefx/blob/master/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Windows.cs
|
|
|
|
|
+ * https://github.com/dotnet/corefx/blob/master/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
#include <config.h>
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#ifdef HOST_WIN32
|
|
#ifdef HOST_WIN32
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
#include <glib.h>
|
|
|
-#include <string.h>
|
|
|
|
|
-#include <errno.h>
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
-#include <mono/metadata/object.h>
|
|
|
|
|
#include <mono/metadata/file-mmap.h>
|
|
#include <mono/metadata/file-mmap.h>
|
|
|
|
|
|
|
|
-void *
|
|
|
|
|
-mono_mmap_open_file (MonoString *path, int mode, MonoString *mapName, gint64 *capacity, int access, int options, int *error)
|
|
|
|
|
|
|
+// These control the retry behaviour when lock violation errors occur during Flush:
|
|
|
|
|
+#define MAX_FLUSH_WAITS 15 // must be <=30
|
|
|
|
|
+#define MAX_FLUSH_RETIRES_PER_WAIT 20
|
|
|
|
|
+
|
|
|
|
|
+typedef struct {
|
|
|
|
|
+ void *address;
|
|
|
|
|
+ size_t length;
|
|
|
|
|
+} MmapInstance;
|
|
|
|
|
+
|
|
|
|
|
+enum {
|
|
|
|
|
+ BAD_CAPACITY_FOR_FILE_BACKED = 1,
|
|
|
|
|
+ CAPACITY_SMALLER_THAN_FILE_SIZE,
|
|
|
|
|
+ FILE_NOT_FOUND,
|
|
|
|
|
+ FILE_ALREADY_EXISTS,
|
|
|
|
|
+ PATH_TOO_LONG,
|
|
|
|
|
+ COULD_NOT_OPEN,
|
|
|
|
|
+ CAPACITY_MUST_BE_POSITIVE,
|
|
|
|
|
+ INVALID_FILE_MODE,
|
|
|
|
|
+ COULD_NOT_MAP_MEMORY,
|
|
|
|
|
+ ACCESS_DENIED,
|
|
|
|
|
+ CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+enum {
|
|
|
|
|
+ FILE_MODE_CREATE_NEW = 1,
|
|
|
|
|
+ FILE_MODE_CREATE = 2,
|
|
|
|
|
+ FILE_MODE_OPEN = 3,
|
|
|
|
|
+ FILE_MODE_OPEN_OR_CREATE = 4,
|
|
|
|
|
+ FILE_MODE_TRUNCATE = 5,
|
|
|
|
|
+ FILE_MODE_APPEND = 6,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+enum {
|
|
|
|
|
+ MMAP_FILE_ACCESS_READ_WRITE = 0,
|
|
|
|
|
+ MMAP_FILE_ACCESS_READ = 1,
|
|
|
|
|
+ MMAP_FILE_ACCESS_WRITE = 2,
|
|
|
|
|
+ MMAP_FILE_ACCESS_COPY_ON_WRITE = 3,
|
|
|
|
|
+ MMAP_FILE_ACCESS_READ_EXECUTE = 4,
|
|
|
|
|
+ MMAP_FILE_ACCESS_READ_WRITE_EXECUTE = 5,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+static DWORD get_page_access (int access)
|
|
|
{
|
|
{
|
|
|
- g_error ("No windows backend");
|
|
|
|
|
- return NULL;
|
|
|
|
|
|
|
+ switch (access) {
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ:
|
|
|
|
|
+ return PAGE_READONLY;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_WRITE:
|
|
|
|
|
+ return PAGE_READWRITE;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_COPY_ON_WRITE:
|
|
|
|
|
+ return PAGE_WRITECOPY;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_EXECUTE:
|
|
|
|
|
+ return PAGE_EXECUTE_READ;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE:
|
|
|
|
|
+ return PAGE_EXECUTE_READWRITE;
|
|
|
|
|
+ default:
|
|
|
|
|
+ g_error ("unknown MemoryMappedFileAccess %d", access);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void *
|
|
|
|
|
-mono_mmap_open_handle (void *handle, MonoString *mapName, gint64 *capacity, int access, int options, int *error)
|
|
|
|
|
|
|
+static DWORD get_file_access (int access)
|
|
|
{
|
|
{
|
|
|
- g_error ("No windows backend");
|
|
|
|
|
- return NULL;
|
|
|
|
|
|
|
+ switch (access) {
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ:
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_EXECUTE:
|
|
|
|
|
+ return GENERIC_READ;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_WRITE:
|
|
|
|
|
+ case MMAP_FILE_ACCESS_COPY_ON_WRITE:
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE:
|
|
|
|
|
+ return GENERIC_READ | GENERIC_WRITE;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_WRITE:
|
|
|
|
|
+ return GENERIC_WRITE;
|
|
|
|
|
+ default:
|
|
|
|
|
+ g_error ("unknown MemoryMappedFileAccess %d", access);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
-mono_mmap_close (void *mmap_handle)
|
|
|
|
|
|
|
+static int get_file_map_access (int access)
|
|
|
{
|
|
{
|
|
|
- g_error ("No windows backend");
|
|
|
|
|
|
|
+ switch (access) {
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ:
|
|
|
|
|
+ return FILE_MAP_READ;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_WRITE:
|
|
|
|
|
+ return FILE_MAP_WRITE;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_WRITE:
|
|
|
|
|
+ return FILE_MAP_READ | FILE_MAP_WRITE;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_COPY_ON_WRITE:
|
|
|
|
|
+ return FILE_MAP_COPY;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_EXECUTE:
|
|
|
|
|
+ return FILE_MAP_EXECUTE | FILE_MAP_READ;
|
|
|
|
|
+ case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE:
|
|
|
|
|
+ return FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE;
|
|
|
|
|
+ default:
|
|
|
|
|
+ g_error ("unknown MemoryMappedFileAccess %d", access);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
-mono_mmap_configure_inheritability (void *mmap_handle, gboolean inheritability)
|
|
|
|
|
|
|
+static int convert_win32_error (int error, int def)
|
|
|
{
|
|
{
|
|
|
- g_error ("No windows backend");
|
|
|
|
|
|
|
+ switch (error) {
|
|
|
|
|
+ case ERROR_FILE_NOT_FOUND:
|
|
|
|
|
+ return FILE_NOT_FOUND;
|
|
|
|
|
+ case ERROR_FILE_EXISTS:
|
|
|
|
|
+ case ERROR_ALREADY_EXISTS:
|
|
|
|
|
+ return FILE_ALREADY_EXISTS;
|
|
|
|
|
+ case ERROR_ACCESS_DENIED:
|
|
|
|
|
+ return ACCESS_DENIED;
|
|
|
|
|
+ }
|
|
|
|
|
+ return def;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
-mono_mmap_flush (void *mmap_handle)
|
|
|
|
|
|
|
+static void *open_handle (void *handle, MonoString *mapName, int mode, gint64 *capacity, int access, int options, int *error)
|
|
|
{
|
|
{
|
|
|
- g_error ("No windows backend");
|
|
|
|
|
|
|
+ g_assert (handle != NULL);
|
|
|
|
|
+
|
|
|
|
|
+ wchar_t *w_mapName = NULL;
|
|
|
|
|
+ HANDLE result = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
|
+ if (*capacity <= 0) {
|
|
|
|
|
+ *error = CAPACITY_MUST_BE_POSITIVE;
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+#if SIZEOF_VOID_P == 4
|
|
|
|
|
+ if (*capacity > UINT32_MAX) {
|
|
|
|
|
+ *error = CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE;
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+ if (!(mode == FILE_MODE_CREATE_NEW || mode == FILE_MODE_OPEN_OR_CREATE || mode == FILE_MODE_OPEN)) {
|
|
|
|
|
+ *error = INVALID_FILE_MODE;
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ FILE_STANDARD_INFO info;
|
|
|
|
|
+ if (!GetFileInformationByHandleEx ((HANDLE) handle, FileStandardInfo, &info, sizeof (FILE_STANDARD_INFO))) {
|
|
|
|
|
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (*capacity == 0) {
|
|
|
|
|
+ if (info.EndOfFile.QuadPart == 0) {
|
|
|
|
|
+ *error = CAPACITY_SMALLER_THAN_FILE_SIZE;
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (*capacity < info.EndOfFile.QuadPart) {
|
|
|
|
|
+ *error = CAPACITY_SMALLER_THAN_FILE_SIZE;
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ w_mapName = mapName ? mono_string_to_utf16 (mapName) : NULL;
|
|
|
|
|
+
|
|
|
|
|
+ if (mode == FILE_MODE_CREATE_NEW || handle != INVALID_HANDLE_VALUE) {
|
|
|
|
|
+ result = CreateFileMappingW ((HANDLE)handle, NULL, get_page_access (access) | options, (DWORD)(((guint64)*capacity) >> 32), (DWORD)*capacity, w_mapName);
|
|
|
|
|
+ if (result && GetLastError () == ERROR_ALREADY_EXISTS) {
|
|
|
|
|
+ CloseHandle (result);
|
|
|
|
|
+ result = NULL;
|
|
|
|
|
+ *error = FILE_ALREADY_EXISTS;
|
|
|
|
|
+ } else if (!result && GetLastError () != NO_ERROR) {
|
|
|
|
|
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (mode == FILE_MODE_OPEN || mode == FILE_MODE_OPEN_OR_CREATE && access == MMAP_FILE_ACCESS_WRITE) {
|
|
|
|
|
+ result = OpenFileMappingW (get_file_map_access (access), FALSE, w_mapName);
|
|
|
|
|
+ if (!result) {
|
|
|
|
|
+ if (mode == FILE_MODE_OPEN_OR_CREATE && GetLastError () == ERROR_FILE_NOT_FOUND) {
|
|
|
|
|
+ *error = INVALID_FILE_MODE;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (mode == FILE_MODE_OPEN_OR_CREATE) {
|
|
|
|
|
+
|
|
|
|
|
+ // This replicates how CoreFX does MemoryMappedFile.CreateOrOpen ().
|
|
|
|
|
+
|
|
|
|
|
+ /// Try to open the file if it exists -- this requires a bit more work. Loop until we can
|
|
|
|
|
+ /// either create or open a memory mapped file up to a timeout. CreateFileMapping may fail
|
|
|
|
|
+ /// if the file exists and we have non-null security attributes, in which case we need to
|
|
|
|
|
+ /// use OpenFileMapping. But, there exists a race condition because the memory mapped file
|
|
|
|
|
+ /// may have closed between the two calls -- hence the loop.
|
|
|
|
|
+ ///
|
|
|
|
|
+ /// The retry/timeout logic increases the wait time each pass through the loop and times
|
|
|
|
|
+ /// out in approximately 1.4 minutes. If after retrying, a MMF handle still hasn't been opened,
|
|
|
|
|
+ /// throw an InvalidOperationException.
|
|
|
|
|
+
|
|
|
|
|
+ guint32 waitRetries = 14; //((2^13)-1)*10ms == approximately 1.4mins
|
|
|
|
|
+ guint32 waitSleep = 0;
|
|
|
|
|
+
|
|
|
|
|
+ while (waitRetries > 0) {
|
|
|
|
|
+ result = CreateFileMappingW ((HANDLE)handle, NULL, get_page_access (access) | options, (DWORD)(((guint64)*capacity) >> 32), (DWORD)*capacity, w_mapName);
|
|
|
|
|
+ if (result)
|
|
|
|
|
+ break;
|
|
|
|
|
+ if (GetLastError() != ERROR_ACCESS_DENIED) {
|
|
|
|
|
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ result = OpenFileMappingW (get_file_map_access (access), FALSE, w_mapName);
|
|
|
|
|
+ if (result)
|
|
|
|
|
+ break;
|
|
|
|
|
+ if (GetLastError () != ERROR_FILE_NOT_FOUND) {
|
|
|
|
|
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ // increase wait time
|
|
|
|
|
+ --waitRetries;
|
|
|
|
|
+ if (waitSleep == 0) {
|
|
|
|
|
+ waitSleep = 10;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ mono_thread_info_sleep (waitSleep, NULL);
|
|
|
|
|
+ waitSleep *= 2;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!result) {
|
|
|
|
|
+ *error = COULD_NOT_OPEN;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (w_mapName)
|
|
|
|
|
+ g_free (w_mapName);
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+void *mono_mmap_open_file (MonoString *path, int mode, MonoString *mapName, gint64 *capacity, int access, int options, int *error)
|
|
|
|
|
+{
|
|
|
|
|
+ g_assert (path != NULL || mapName != NULL);
|
|
|
|
|
+
|
|
|
|
|
+ wchar_t *w_path = NULL;
|
|
|
|
|
+ HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
|
+ HANDLE result = NULL;
|
|
|
|
|
+ gboolean delete_on_error = FALSE;
|
|
|
|
|
|
|
|
|
|
+ if (path) {
|
|
|
|
|
+ w_path = mono_string_to_utf16 (path);
|
|
|
|
|
+ WIN32_FILE_ATTRIBUTE_DATA file_attrs;
|
|
|
|
|
+ gboolean existed = GetFileAttributesExW (w_path, GetFileExInfoStandard, &file_attrs);
|
|
|
|
|
+ if (!existed && mode == FILE_MODE_CREATE_NEW && *capacity == 0) {
|
|
|
|
|
+ *error = CAPACITY_SMALLER_THAN_FILE_SIZE;
|
|
|
|
|
+ goto done;
|
|
|
|
|
+ }
|
|
|
|
|
+ hFile = CreateFileW (w_path, get_file_access (access), FILE_SHARE_READ, NULL, mode, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
|
+ if (hFile == INVALID_HANDLE_VALUE) {
|
|
|
|
|
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
|
|
|
|
|
+ goto done;
|
|
|
|
|
+ }
|
|
|
|
|
+ delete_on_error = !existed;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
-int
|
|
|
|
|
-mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mmap_handle, void **base_address)
|
|
|
|
|
|
|
+ result = open_handle (hFile, mapName, mode, capacity, access, options, error);
|
|
|
|
|
+
|
|
|
|
|
+done:
|
|
|
|
|
+ if (!result && delete_on_error)
|
|
|
|
|
+ DeleteFileW (w_path);
|
|
|
|
|
+ if (w_path)
|
|
|
|
|
+ g_free (w_path);
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void *mono_mmap_open_handle (void *handle, MonoString *mapName, gint64 *capacity, int access, int options, int *error)
|
|
|
{
|
|
{
|
|
|
- g_error ("No windows backend");
|
|
|
|
|
|
|
+ g_assert (handle != NULL);
|
|
|
|
|
+
|
|
|
|
|
+ return open_handle (handle, mapName, FILE_MODE_OPEN, capacity, access, options, error);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void mono_mmap_close (void *mmap_handle)
|
|
|
|
|
+{
|
|
|
|
|
+ g_assert (mmap_handle);
|
|
|
|
|
+ CloseHandle ((HANDLE) mmap_handle);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void mono_mmap_configure_inheritability (void *mmap_handle, gboolean inheritability)
|
|
|
|
|
+{
|
|
|
|
|
+ g_assert (mmap_handle);
|
|
|
|
|
+ if (!SetHandleInformation ((HANDLE) mmap_handle, HANDLE_FLAG_INHERIT, inheritability ? HANDLE_FLAG_INHERIT : 0)) {
|
|
|
|
|
+ g_error ("mono_mmap_configure_inheritability: SetHandleInformation failed with error %d!", GetLastError ());
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void mono_mmap_flush (void *mmap_handle)
|
|
|
|
|
+{
|
|
|
|
|
+ g_assert (mmap_handle);
|
|
|
|
|
+ MmapInstance *h = (MmapInstance *)mmap_handle;
|
|
|
|
|
+
|
|
|
|
|
+ if (FlushViewOfFile (h->address, h->length))
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ // This replicates how CoreFX does MemoryMappedView.Flush ().
|
|
|
|
|
+
|
|
|
|
|
+ // It is a known issue within the NTFS transaction log system that
|
|
|
|
|
+ // causes FlushViewOfFile to intermittently fail with ERROR_LOCK_VIOLATION
|
|
|
|
|
+ // As a workaround, we catch this particular error and retry the flush operation
|
|
|
|
|
+ // a few milliseconds later. If it does not work, we give it a few more tries with
|
|
|
|
|
+ // increasing intervals. Eventually, however, we need to give up. In ad-hoc tests
|
|
|
|
|
+ // this strategy successfully flushed the view after no more than 3 retries.
|
|
|
|
|
+
|
|
|
|
|
+ if (GetLastError () != ERROR_LOCK_VIOLATION)
|
|
|
|
|
+ // TODO: Propagate error to caller
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ for (int w = 0; w < MAX_FLUSH_WAITS; w++) {
|
|
|
|
|
+ int pause = (1 << w); // MaxFlushRetries should never be over 30
|
|
|
|
|
+ mono_thread_info_sleep (pause, NULL);
|
|
|
|
|
+
|
|
|
|
|
+ for (int r = 0; r < MAX_FLUSH_RETIRES_PER_WAIT; r++) {
|
|
|
|
|
+ if (FlushViewOfFile (h->address, h->length))
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ if (GetLastError () != ERROR_LOCK_VIOLATION)
|
|
|
|
|
+ // TODO: Propagate error to caller
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ mono_thread_info_yield ();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // We got to here, so there was no success:
|
|
|
|
|
+ // TODO: Propagate error to caller
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+int mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mmap_handle, void **base_address)
|
|
|
|
|
+{
|
|
|
|
|
+ static DWORD allocationGranularity = 0;
|
|
|
|
|
+ if (allocationGranularity == 0) {
|
|
|
|
|
+ SYSTEM_INFO info;
|
|
|
|
|
+ GetSystemInfo (&info);
|
|
|
|
|
+ allocationGranularity = info.dwAllocationGranularity;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ gint64 extraMemNeeded = offset % allocationGranularity;
|
|
|
|
|
+ guint64 newOffset = offset - extraMemNeeded;
|
|
|
|
|
+ gint64 nativeSize = (*size != 0) ? *size + extraMemNeeded : 0;
|
|
|
|
|
+
|
|
|
|
|
+#if SIZEOF_VOID_P == 4
|
|
|
|
|
+ if (nativeSize > UINT32_MAX)
|
|
|
|
|
+ return CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE;
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ void *address = MapViewOfFile ((HANDLE) handle, get_file_map_access (access), (DWORD) (newOffset >> 32), (DWORD) newOffset, (SIZE_T) nativeSize);
|
|
|
|
|
+ if (!address)
|
|
|
|
|
+ return convert_win32_error (GetLastError (), COULD_NOT_MAP_MEMORY);
|
|
|
|
|
+
|
|
|
|
|
+ // Query the view for its size and allocation type
|
|
|
|
|
+ MEMORY_BASIC_INFORMATION viewInfo;
|
|
|
|
|
+ VirtualQuery (address, &viewInfo, sizeof (MEMORY_BASIC_INFORMATION));
|
|
|
|
|
+ guint64 viewSize = (guint64) viewInfo.RegionSize;
|
|
|
|
|
+
|
|
|
|
|
+ // Allocate the pages if we were using the MemoryMappedFileOptions.DelayAllocatePages option
|
|
|
|
|
+ // OR check if the allocated view size is smaller than the expected native size
|
|
|
|
|
+ // If multiple overlapping views are created over the file mapping object, the pages in a given region
|
|
|
|
|
+ // could have different attributes(MEM_RESERVE OR MEM_COMMIT) as MapViewOfFile preserves coherence between
|
|
|
|
|
+ // views created on a mapping object backed by same file.
|
|
|
|
|
+ // In which case, the viewSize will be smaller than nativeSize required and viewState could be MEM_COMMIT
|
|
|
|
|
+ // but more pages may need to be committed in the region.
|
|
|
|
|
+ // This is because, VirtualQuery function(that internally invokes VirtualQueryEx function) returns the attributes
|
|
|
|
|
+ // and size of the region of pages with matching attributes starting from base address.
|
|
|
|
|
+ // VirtualQueryEx: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366907(v=vs.85).aspx
|
|
|
|
|
+ if (((viewInfo.State & MEM_RESERVE) != 0) || viewSize < (guint64) nativeSize) {
|
|
|
|
|
+ void *tempAddress = VirtualAlloc (address, nativeSize != 0 ? nativeSize : viewSize, MEM_COMMIT, get_page_access (access));
|
|
|
|
|
+ if (!tempAddress) {
|
|
|
|
|
+ return convert_win32_error (GetLastError (), COULD_NOT_MAP_MEMORY);
|
|
|
|
|
+ }
|
|
|
|
|
+ // again query the view for its new size
|
|
|
|
|
+ VirtualQuery (address, &viewInfo, sizeof (MEMORY_BASIC_INFORMATION));
|
|
|
|
|
+ viewSize = (guint64) viewInfo.RegionSize;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (*size == 0)
|
|
|
|
|
+ *size = viewSize - extraMemNeeded;
|
|
|
|
|
+
|
|
|
|
|
+ MmapInstance *h = g_malloc0 (sizeof (MmapInstance));
|
|
|
|
|
+ h->address = address;
|
|
|
|
|
+ h->length = *size + extraMemNeeded;
|
|
|
|
|
+ *mmap_handle = h;
|
|
|
|
|
+ *base_address = (char*) address + (offset - newOffset);
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-gboolean
|
|
|
|
|
-mono_mmap_unmap (void *mmap_handle)
|
|
|
|
|
|
|
+gboolean mono_mmap_unmap (void *mmap_handle)
|
|
|
{
|
|
{
|
|
|
- g_error ("No windows backend");
|
|
|
|
|
- return TRUE;
|
|
|
|
|
|
|
+ g_assert (mmap_handle);
|
|
|
|
|
+
|
|
|
|
|
+ MmapInstance *h = (MmapInstance *) mmap_handle;
|
|
|
|
|
+
|
|
|
|
|
+ gboolean result = UnmapViewOfFile (h->address);
|
|
|
|
|
+
|
|
|
|
|
+ g_free (h);
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
#endif
|