Răsfoiți Sursa

Merge pull request #3796 from ntherning/windows-backend-for-MemoryMappedFile

MemoryMappedFile support on Windows
Rodrigo Kumpera 9 ani în urmă
părinte
comite
7bd7446abc

+ 4 - 0
mcs/class/System.Core/Microsoft.Win32.SafeHandles/SafeMemoryMappedViewHandle.cs

@@ -44,6 +44,10 @@ namespace Microsoft.Win32.SafeHandles
 			Initialize ((ulong)size);
 		}
 
+		internal void Flush () {
+			MemoryMapImpl.Flush (this.mmap_handle);
+		}
+
 		protected override bool ReleaseHandle () {
 			if (this.handle != (IntPtr) (-1))
 				return MemoryMapImpl.Unmap (this.mmap_handle);

+ 12 - 8
mcs/class/System.Core/System.IO.MemoryMappedFiles/MemoryMappedFile.cs

@@ -70,7 +70,7 @@ namespace System.IO.MemoryMappedFiles
 			case 1:
 				return new ArgumentException ("A positive capacity must be specified for a Memory Mapped File backed by an empty file.");
 			case 2:
-				return new ArgumentOutOfRangeException ("The capacity may not be smaller than the file size.");
+				return new ArgumentOutOfRangeException ("capacity", "The capacity may not be smaller than the file size.");
 			case 3:
 				return new FileNotFoundException (path);
 			case 4:
@@ -85,6 +85,10 @@ namespace System.IO.MemoryMappedFiles
 				return new ArgumentException ("Invalid FileMode value.");
 			case 9:
 				return new IOException ("Could not map file");
+			case 10:
+				return new UnauthorizedAccessException ("Access to the path is denied.");
+			case 11:
+				return new ArgumentOutOfRangeException ("capacity", "The capacity cannot be greater than the size of the system's logical address space.");
 			default:
 				return new IOException ("Failed with unknown error code " + error);
 			}
@@ -140,7 +144,7 @@ namespace System.IO.MemoryMappedFiles
 			if (mode == FileMode.Append)
 				throw new ArgumentException ("mode");
 
-			IntPtr handle = MemoryMapImpl.OpenFile (path, mode, null, out capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages);
+			IntPtr handle = MemoryMapImpl.OpenFile (path, mode, null, out capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None);
 
 			return new MemoryMappedFile () {
 				handle = handle,
@@ -172,7 +176,7 @@ namespace System.IO.MemoryMappedFiles
 			if (capacity < 0)
 				throw new ArgumentOutOfRangeException ("capacity");
 
-			IntPtr handle = MemoryMapImpl.OpenFile (path, mode, mapName, out capacity, access, MemoryMappedFileOptions.DelayAllocatePages);
+			IntPtr handle = MemoryMapImpl.OpenFile (path, mode, mapName, out capacity, access, MemoryMappedFileOptions.None);
 			
 			return new MemoryMappedFile () {
 				handle = handle,
@@ -193,7 +197,7 @@ namespace System.IO.MemoryMappedFiles
 			if ((!MonoUtil.IsUnix && capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
 				throw new ArgumentException ("capacity");
 
-			IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.DelayAllocatePages);
+			IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.None);
 			
 			MemoryMapImpl.ConfigureHandleInheritability (handle, inheritability);
 				
@@ -220,7 +224,7 @@ namespace System.IO.MemoryMappedFiles
 			if ((!MonoUtil.IsUnix && capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
 				throw new ArgumentException ("capacity");
 
-			IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.DelayAllocatePages);
+			IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.None);
 			
 			MemoryMapImpl.ConfigureHandleInheritability (handle, inheritability);
 				
@@ -258,13 +262,13 @@ namespace System.IO.MemoryMappedFiles
 		[MonoLimitation ("Named mappings scope is process local")]
 		public static MemoryMappedFile CreateNew (string mapName, long capacity)
 		{
-			return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, HandleInheritability.None);
+			return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, null, HandleInheritability.None);
 		}
 
 		[MonoLimitation ("Named mappings scope is process local")]
 		public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access) 
 		{
-			return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, HandleInheritability.None);
+			return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.None, null, HandleInheritability.None);
 		}
 
 		[MonoLimitation ("Named mappings scope is process local; options is ignored")]
@@ -290,7 +294,7 @@ namespace System.IO.MemoryMappedFiles
 		[MonoLimitation ("Named mappings scope is process local")]
 		public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
 		{
-			return CreateOrOpen (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, HandleInheritability.None);
+			return CreateOrOpen (mapName, capacity, access, MemoryMappedFileOptions.None, null, HandleInheritability.None);
 		}
 
 		[MonoLimitation ("Named mappings scope is process local")]

+ 1 - 1
mcs/class/System.Core/System.IO.MemoryMappedFiles/MemoryMappedView.cs

@@ -92,7 +92,7 @@ namespace System.IO.MemoryMappedFiles
 
 		public void Flush (IntPtr capacity)
 		{
-			MemoryMapImpl.Flush (m_viewHandle.DangerousGetHandle ());
+			m_viewHandle.Flush ();
 		}
 		
 		protected virtual void Dispose (bool disposing)

+ 68 - 28
mcs/class/System.Core/Test/System.IO.MemoryMappedFiles/MemoryMappedFileTest.cs

@@ -56,16 +56,28 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 			return "test-" + named_index++;
 		}
 
-
+		static string baseTempDir = Path.Combine (Path.GetTempPath (), typeof (MemoryMappedFileTest).FullName);
 		static string tempDir = Path.Combine (Path.GetTempPath (), typeof (MemoryMappedFileTest).FullName);
 
 		string fname;
 
-		[SetUp]
-		protected void SetUp () {
-			if (Directory.Exists (tempDir))
-				Directory.Delete (tempDir, true);
+		[TestFixtureSetUp]
+		public void FixtureSetUp ()
+		{
+			try {
+				// Try to cleanup from any previous NUnit run.
+				Directory.Delete (baseTempDir, true);
+			} catch (Exception) {
+			}
+		}
 
+		[SetUp]
+		public void SetUp ()
+		{
+			int i = 0;
+			do {
+				tempDir = Path.Combine (baseTempDir, (++i).ToString());
+			} while (Directory.Exists (tempDir));
 			Directory.CreateDirectory (tempDir);
 
 			fname = Path.Combine (tempDir, "basic.txt");
@@ -77,9 +89,14 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 		}
 
 		[TearDown]
-		protected void TearDown () {
-			if (Directory.Exists (tempDir))
+		public void TearDown ()
+		{
+			try {
+				// This throws an exception under MS.NET and Mono on Windows,
+				// since the directory contains open files.
 				Directory.Delete (tempDir, true);
+			} catch (Exception) {
+			}
 		}
 
 		[Test]
@@ -102,26 +119,16 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 		public void CreateNew ()
 		{
 			// This must succeed
-			MemoryMappedFile.CreateNew (Path.Combine (tempDir, "createNew.test"), 8192);
-		}
-
-		[Test]
-		[ExpectedException (typeof (IOException))]
-		public void CreateNew_OnExistingFile ()
-		{
-			// This must succeed
-			MemoryMappedFile.CreateNew (Path.Combine (tempDir, "createNew.test"), 8192);
-			
-			// This should fail, the file exists
-			MemoryMappedFile.CreateNew (Path.Combine (tempDir, "createNew.test"), 8192);
+			MemoryMappedFile.CreateNew (MkNamedMapping (), 8192);
 		}
 
 		// Call this twice, it should always work
 		[Test]
 		public void CreateOrOpen_Multiple ()
 		{
-			MemoryMappedFile.CreateOrOpen (Path.Combine (tempDir, "createOrOpen.test"), 8192);
-			MemoryMappedFile.CreateOrOpen (Path.Combine (tempDir, "createOrOpen.test"), 8192);
+			var name = MkNamedMapping ();
+			MemoryMappedFile.CreateOrOpen (name, 8192);
+			MemoryMappedFile.CreateOrOpen (name, 8192);
 		}
 
 		[Test]
@@ -134,7 +141,7 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 			// We are requesting fewer bytes to map.
 			MemoryMappedFile.CreateFromFile (f, FileMode.Open, "myMap", 4192);
 		}
-	
+
 		[Test]
 		public void CreateFromFile_Null () {
 			AssertThrows<ArgumentNullException> (delegate () {
@@ -252,6 +259,9 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 		[Test]
 		public void NamedMappingToInvalidFile ()
 		{
+			if (Environment.OSVersion.Platform != PlatformID.Unix) {
+				Assert.Ignore ("Backslashes in mapping names are disallowed on .NET and Mono on Windows");
+			}
 			var fileName = Path.Combine (tempDir, "temp_file_123");
 	        if (File.Exists (fileName))
 	            File.Delete (fileName);
@@ -352,7 +362,7 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 		}
 
 		[Test]
-		[ExpectedException(typeof(IOException))]
+		[ExpectedException(typeof(UnauthorizedAccessException))]
 		public void CreateViewStreamWithOffsetPastFileEnd ()
 		{
 			string f = Path.Combine (tempDir, "8192-file");
@@ -365,7 +375,7 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 		}
 
 		[Test]
-		[ExpectedException(typeof(IOException))]
+		[ExpectedException(typeof(UnauthorizedAccessException))]
 		public void CreateViewStreamWithOffsetPastFileEnd2 ()
 		{
 			string f = Path.Combine (tempDir, "8192-file");
@@ -394,7 +404,7 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 
 			MemoryMappedViewStream stream = mappedFile.CreateViewStream (pageSize * 2, 0, MemoryMappedFileAccess.ReadWrite);
 #if !MONOTOUCH
-			Assert.AreEqual (stream.Capacity, Environment.SystemPageSize);
+			Assert.AreEqual (Environment.SystemPageSize, stream.Capacity);
 #endif
 			stream.Write (new byte [pageSize], 0, pageSize);
 		}
@@ -407,9 +417,39 @@ namespace MonoTests.System.IO.MemoryMappedFiles {
 			File.WriteAllBytes (f, new byte [size]);
 
 			FileStream file = File.OpenRead (f);
-			MemoryMappedFile.CreateFromFile (file, null, size, MemoryMappedFileAccess.ReadExecute, null, 0, false);
+			MemoryMappedFile.CreateFromFile (file, null, size, MemoryMappedFileAccess.Read, null, 0, false);
 		}
-	}
-}
 
+		[Test]
+		[ExpectedException(typeof(ArgumentOutOfRangeException))]
+		public void CreateNewLargerThanLogicalAddressSpace ()
+		{
+			if (IntPtr.Size != 4) {
+				Assert.Ignore ("Only applies to 32-bit systems");
+			}
+			MemoryMappedFile.CreateNew (MkNamedMapping (), (long) uint.MaxValue + 1);
+		}
 
+		[Test]
+		[ExpectedException(typeof(ArgumentOutOfRangeException))]
+		public void CreateOrOpenLargerThanLogicalAddressSpace ()
+		{
+			if (IntPtr.Size != 4) {
+				Assert.Ignore ("Only applies to 32-bit systems");
+			}
+			MemoryMappedFile.CreateOrOpen (MkNamedMapping (), (long) uint.MaxValue + 1);
+		}
+
+		[Test]
+		public void NamedMappingWithDelayAllocatePages ()
+		{
+			var name = MkNamedMapping ();
+			using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, HandleInheritability.None)) {
+				using (MemoryMappedViewAccessor v0 = m0.CreateViewAccessor ()) {
+					Assert.AreEqual (0, v0.ReadInt32 (0));
+				}
+			}
+		}
+
+	}
+}

+ 14 - 6
mono/metadata/file-mmap-posix.c

@@ -63,7 +63,9 @@ enum {
 	COULD_NOT_OPEN,
 	CAPACITY_MUST_BE_POSITIVE,
 	INVALID_FILE_MODE,
-	COULD_NOT_MAP_MEMORY
+	COULD_NOT_MAP_MEMORY,
+	ACCESS_DENIED,
+	CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE
 };
 
 enum {
@@ -300,10 +302,16 @@ static void*
 open_memory_map (const char *c_mapName, int mode, gint64 *capacity, int access, int options, int *ioerror)
 {
 	MmapHandle *handle;
-	if (*capacity <= 1) {
+	if (*capacity <= 0) {
 		*ioerror = CAPACITY_MUST_BE_POSITIVE;
 		return NULL;
 	}
+#if SIZEOF_VOID_P == 4
+	if (*capacity > UINT32_MAX) {
+		*ioerror = 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)) {
 		*ioerror = INVALID_FILE_MODE;
@@ -499,8 +507,11 @@ mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mma
 	struct stat buf = { 0 };
 	fstat (fh->fd, &buf); //FIXME error handling
 
+	*mmap_handle = NULL;
+	*base_address = NULL;
+
 	if (offset > buf.st_size || ((eff_size + offset) > buf.st_size && !is_special_zero_size_file (&buf)))
-		goto error;
+		return ACCESS_DENIED;
 	/**
 	  * We use the file size if one of the following conditions is true:
 	  *  -input size is zero
@@ -522,9 +533,6 @@ mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mma
 		return 0;
 	}
 
-error:
-	*mmap_handle = NULL;
-	*base_address = NULL;
 	return COULD_NOT_MAP_MEMORY;
 }
 

+ 375 - 33
mono/metadata/file-mmap-windows.c

@@ -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.
  */
 
+/*
+ * 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>
 
 #ifdef HOST_WIN32
 
 #include <glib.h>
-#include <string.h>
-#include <errno.h>
-
 
-#include <mono/metadata/object.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;
 }
 
-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

+ 7 - 6
mono/utils/mono-codeman.c

@@ -329,7 +329,7 @@ new_codechunk (CodeChunk *last, int dynamic, int size)
 {
 	int minsize, flags = CODE_FLAG_MMAP;
 	int chunk_size, bsize = 0;
-	int pagesize;
+	int pagesize, valloc_granule;
 	CodeChunk *chunk;
 	void *ptr;
 
@@ -338,12 +338,13 @@ new_codechunk (CodeChunk *last, int dynamic, int size)
 #endif
 
 	pagesize = mono_pagesize ();
+	valloc_granule = mono_valloc_granule ();
 
 	if (dynamic) {
 		chunk_size = size;
 		flags = CODE_FLAG_MALLOC;
 	} else {
-		minsize = pagesize * MIN_PAGES;
+		minsize = MAX (pagesize * MIN_PAGES, valloc_granule);
 		if (size < minsize)
 			chunk_size = minsize;
 		else {
@@ -353,8 +354,8 @@ new_codechunk (CodeChunk *last, int dynamic, int size)
 			size += MIN_ALIGN - 1;
 			size &= ~(MIN_ALIGN - 1);
 			chunk_size = size;
-			chunk_size += pagesize - 1;
-			chunk_size &= ~ (pagesize - 1);
+			chunk_size += valloc_granule - 1;
+			chunk_size &= ~ (valloc_granule - 1);
 		}
 	}
 #ifdef BIND_ROOM
@@ -370,8 +371,8 @@ new_codechunk (CodeChunk *last, int dynamic, int size)
 	if (chunk_size - size < bsize) {
 		chunk_size = size + bsize;
 		if (!dynamic) {
-			chunk_size += pagesize - 1;
-			chunk_size &= ~ (pagesize - 1);
+			chunk_size += valloc_granule - 1;
+			chunk_size &= ~ (valloc_granule - 1);
 		}
 	}
 #endif

+ 13 - 1
mono/utils/mono-mmap-windows.c

@@ -27,10 +27,22 @@ mono_pagesize (void)
 	if (saved_pagesize)
 		return saved_pagesize;
 	GetSystemInfo (&info);
-	saved_pagesize = info.dwAllocationGranularity;
+	saved_pagesize = info.dwPageSize;
 	return saved_pagesize;
 }
 
+int
+mono_valloc_granule (void)
+{
+	SYSTEM_INFO info;
+	static int saved_valloc_granule = 0;
+	if (saved_valloc_granule)
+		return saved_valloc_granule;
+	GetSystemInfo (&info);
+	saved_valloc_granule = info.dwAllocationGranularity;
+	return saved_valloc_granule;
+}
+
 int
 mono_mmap_win_prot_from_flags (int flags)
 {

+ 12 - 0
mono/utils/mono-mmap.c

@@ -155,6 +155,12 @@ mono_pagesize (void)
 	return saved_pagesize;
 }
 
+int
+mono_valloc_granule (void)
+{
+	return mono_pagesize ();
+}
+
 static int
 prot_from_flags (int flags)
 {
@@ -364,6 +370,12 @@ mono_pagesize (void)
 	return 4096;
 }
 
+int
+mono_valloc_granule (void)
+{
+	return mono_pagesize ();
+}
+
 void*
 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
 {

+ 1 - 0
mono/utils/mono-mmap.h

@@ -49,6 +49,7 @@ MONO_API int          mono_file_map_fd    (MonoFileMap *fmap);
 MONO_API int          mono_file_map_close (MonoFileMap *fmap);
 
 MONO_API int   mono_pagesize   (void);
+MONO_API int   mono_valloc_granule (void);
 MONO_API void* mono_valloc     (void *addr, size_t length, int flags, MonoMemAccountType type);
 MONO_API void* mono_valloc_aligned (size_t length, size_t alignment, int flags, MonoMemAccountType type);
 MONO_API int   mono_vfree      (void *addr, size_t length, MonoMemAccountType type);