From c2e8973c625dfb890dab3aa86d7d5a6be636cbde Mon Sep 17 00:00:00 2001 From: Danny Friar Date: Sat, 26 Jun 2021 20:29:30 +0100 Subject: [PATCH] Expose underlying unix file descriptor in SafeMemoryMappedFileHandle (#53538) * expose unix file descriptor safe memory map * store handle after DangerousAddRef * add test verifying handle matches filestream handle * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs Co-authored-by: Stephen Toub * Dispose MemoryMappedFile in MapHandleMatchesFileStreamHandle test * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update MemoryMappedFile.CreateFromFile.Tests.cs * Move mmf in test to using block * Fix test Co-authored-by: Danny Friar Co-authored-by: Stephen Toub --- .../Win32/SafeMemoryMappedFileHandle.Unix.cs | 38 +++++++++++++++++----- .../IO/MemoryMappedFiles/MemoryMappedView.Unix.cs | 4 +-- .../tests/MemoryMappedFile.CreateFromFile.Tests.cs | 17 ++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs b/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs index 32e76bd..3428b01 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs @@ -11,15 +11,14 @@ namespace Microsoft.Win32.SafeHandles { public sealed partial class SafeMemoryMappedFileHandle : SafeHandleZeroOrMinusOneIsInvalid { - /// Counter used to produce a unique handle value. - private static long s_counter; - /// /// The underlying FileStream. May be null. We hold onto the stream rather than just /// onto the underlying handle to ensure that logic associated with disposing the stream /// (e.g. deleting the file for DeleteOnClose) happens at the appropriate time. /// - internal readonly FileStream? _fileStream; + private readonly FileStream? _fileStream; + /// The FileStream's handle, cached to avoid repeated accesses to FileStream.SafeFileHandle that could, in theory, change. + internal SafeFileHandle? _fileStreamHandle; /// Whether this SafeHandle owns the _fileStream and should Dispose it when disposed. internal readonly bool _ownsFileStream; @@ -59,9 +58,22 @@ namespace Microsoft.Win32.SafeHandles _options = options; _capacity = capacity; - // Fake a unique int handle value > 0. - int nextHandleValue = (int)((Interlocked.Increment(ref s_counter) % (int.MaxValue - 1)) + 1); - SetHandle(new IntPtr(nextHandleValue)); + IntPtr handlePtr; + + if (fileStream != null) + { + bool ignored = false; + SafeFileHandle handle = fileStream.SafeFileHandle; + handle.DangerousAddRef(ref ignored); + _fileStreamHandle = handle; + handlePtr = handle.DangerousGetHandle(); + } + else + { + handlePtr = IntPtr.MaxValue; + } + + SetHandle(handlePtr); } protected override void Dispose(bool disposing) @@ -74,7 +86,17 @@ namespace Microsoft.Win32.SafeHandles base.Dispose(disposing); } - protected override bool ReleaseHandle() => true; // Nothing to clean up. We unlinked immediately after creating the backing store. + protected override bool ReleaseHandle() + { + if (_fileStreamHandle != null) + { + SetHandle((IntPtr) (-1)); + _fileStreamHandle.DangerousRelease(); + _fileStreamHandle = null; + } + + return true; + } public override bool IsInvalid => (long)handle <= 0; } diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs index b5e5a54..917058a 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs @@ -56,10 +56,10 @@ namespace System.IO.MemoryMappedFiles // If we have a file handle, get the file descriptor from it. If the handle is null, // we'll use an anonymous backing store for the map. SafeFileHandle fd; - if (memMappedFileHandle._fileStream != null) + if (memMappedFileHandle._fileStreamHandle != null) { // Get the file descriptor from the SafeFileHandle - fd = memMappedFileHandle._fileStream.SafeFileHandle; + fd = memMappedFileHandle._fileStreamHandle; Debug.Assert(!fd.IsInvalid); } else diff --git a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs index 58cc8b3..824f9b9 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs @@ -951,5 +951,22 @@ namespace System.IO.MemoryMappedFiles.Tests // ENODEV Operation not supported by device. catch (IOException ex) when (ex.HResult == 19) { }; } + + /// + /// Test to verify that the MemoryMappedFile has the same underlying handle as the FileStream it's created from + /// + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public void MapHandleMatchesFileStreamHandle() + { + using (FileStream fs = File.OpenWrite(GetTestFilePath())) + { + using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, null, 4096, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false)) + { + SafeMemoryMappedFileHandle handle = mmf.SafeMemoryMappedFileHandle; + Assert.Equal(fs.SafeFileHandle.DangerousGetHandle(), handle.DangerousGetHandle()); + } + } + } } } -- 2.7.4