Merge pull request #3796 from ntherning/windows-backend-for-MemoryMappedFile
authorRodrigo Kumpera <kumpera@users.noreply.github.com>
Fri, 4 Nov 2016 21:59:47 +0000 (14:59 -0700)
committerGitHub <noreply@github.com>
Fri, 4 Nov 2016 21:59:47 +0000 (14:59 -0700)
MemoryMappedFile support on Windows

mcs/class/System.Core/Microsoft.Win32.SafeHandles/SafeMemoryMappedViewHandle.cs
mcs/class/System.Core/System.IO.MemoryMappedFiles/MemoryMappedFile.cs
mcs/class/System.Core/System.IO.MemoryMappedFiles/MemoryMappedView.cs
mcs/class/System.Core/Test/System.IO.MemoryMappedFiles/MemoryMappedFileTest.cs
mono/metadata/file-mmap-posix.c
mono/metadata/file-mmap-windows.c
mono/utils/mono-codeman.c
mono/utils/mono-mmap-windows.c
mono/utils/mono-mmap.c
mono/utils/mono-mmap.h

index f165f3f62796abe4f1b545373fa8790efda7696f..e48161a358af4bccd8f1119cab6092ebebe55df3 100644 (file)
@@ -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);
index fbc55fd73b5e62c356363f97aeb51b14fe2dbdc8..7a3cce164f61e2feffc9b46fcd751d1c75626e91 100644 (file)
@@ -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")]
index 9362f00b1626a3e4aab06a5dc47f34d51358a6e0..b0be2e4f1885c20f28947b8de734b76d478a96d8 100644 (file)
@@ -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)
index 27191c37ed2b7b121d2f1926471d459add1d013f..9c273b84a596e848680054d9889147050a6b2530 100644 (file)
@@ -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));
+                               }
+                       }
+               }
+
+       }
+}
index 84e3b4aa68d75000fbec036787887c36f1fe8930..4b9ce74499e04ec81a5b1ad1e422615d7c44636b 100644 (file)
@@ -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;
 }
 
index dba37472c34741355a3f5e1e954f4c54a86801ad..b0fd5d9b8962bbfbcfefa532774b6fdb94adf6dd 100644 (file)
 /*
- * 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
index 56948bdd901da23ae66edd34b6e7d19cf218c902..f29a67ca096690a978db2922ef8717ce7afa1920 100644 (file)
@@ -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
index 2c161c6c4ab6f192d7de9effde0c77e2dfe5238a..d0b6072bece42f0bf94070df2fdad50ed88d5df9 100644 (file)
@@ -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)
 {
index 91fe5a12def4517d7c1d364df7117cef53aa988f..10a190ff9b059383a5705e6e0e5d765a79a7122d 100644 (file)
@@ -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)
 {
index d441751c802da5bb60f36c29d638377b7f426c26..1b3b1d63af9fae39922ffe6aaceb51b3266bf3e6 100644 (file)
@@ -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);