From 290896b01007b326936ecb2e4b3002b5159abe5d Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Mon, 13 May 2019 09:47:59 +0200 Subject: [PATCH 1/7] Add asyncronous support for dotnet. --- src/dotnet/FileSystemHost.cs | 83 ++++++++++++++++++++++++++++++++++++ src/dotnet/Interop.cs | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/src/dotnet/FileSystemHost.cs b/src/dotnet/FileSystemHost.cs index ace67292..3f8bd2fb 100644 --- a/src/dotnet/FileSystemHost.cs +++ b/src/dotnet/FileSystemHost.cs @@ -381,6 +381,89 @@ namespace Fsp { return Api.GetVersion(); } + /// + /// Returns a RequestHint to reference the current operation asynchronously. + /// + public UInt64 GetOperationRequestHint() + { + return Api.FspFileSystemGetOperationRequestHint(); + } + /// + /// Asynchronously complete a Write operation. + /// + /// + /// A reference to the operation to complete. + /// + /// + /// STATUS_SUCCESS or error code. + /// + /// + /// The number of bytes written. + /// + /// + /// Updated file information. + /// + public void SendWriteResponse(UInt64 RequestHint, UInt32 Status, UInt32 BytesTransferred, ref FileInfo FileInfo) + { + var Response = new FspFsctlTransactRsp() + { + Size = 128, + Kind = (UInt32) FspFsctlTransact.WriteKind, + Hint = RequestHint + }; + Response.IoStatus.Information = BytesTransferred; + Response.IoStatus.Status = Status; + Response.WriteFileInfo = FileInfo; + Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response); + } + /// + /// Asynchronously complete a Read operation. + /// + /// + /// A reference to the operation to complete. + /// + /// + /// STATUS_SUCCESS or error code. + /// + /// + /// Number of bytes read. + /// + public void SendReadResponse(UInt64 RequestHint, UInt32 Status, UInt32 BytesTransferred) + { + var Response = new FspFsctlTransactRsp() + { + Size = 128, + Kind = (UInt32) FspFsctlTransact.ReadKind, + Hint = RequestHint + }; + Response.IoStatus.Information = BytesTransferred; + Response.IoStatus.Status = Status; + Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response); + } + /// + /// Asynchronously complete a ReadDirectory operation. + /// + /// + /// A reference to the operation to complete. + /// + /// + /// STATUS_SUCCESS or error code. + /// + /// + /// Number of bytes read. + /// + public void SendReadDirectoryResponse(UInt64 RequestHint, UInt32 Status, UInt32 BytesTransferred) + { + var Response = new FspFsctlTransactRsp() + { + Size = 128, + Kind = (UInt32) FspFsctlTransact.QueryDirectoryKind, + Hint = RequestHint + }; + Response.IoStatus.Information = BytesTransferred; + Response.IoStatus.Status = Status; + Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response); + } /* FSP_FILE_SYSTEM_INTERFACE */ private static Byte[] ByteBufferNotNull = new Byte[0]; diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index a6b41ddf..08079c92 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -354,6 +354,65 @@ namespace Fsp.Interop public IntPtr Information; } + [StructLayout(LayoutKind.Sequential)] + internal struct IoStatus + { + internal UInt32 Information; + internal UInt32 Status; + } + + internal enum FspFsctlTransact + { + ReadKind = 5, + WriteKind = 6, + QueryDirectoryKind = 14 + } + + [StructLayout(LayoutKind.Explicit)] + internal struct FspFsctlTransactReq + { + [FieldOffset(0)] + internal UInt16 Version; + [FieldOffset(2)] + internal UInt16 Size; + [FieldOffset(4)] + internal UInt32 Kind; + [FieldOffset(8)] + internal UInt64 Hint; + + [FieldOffset(0)] + internal unsafe fixed Byte Padding[88]; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct FspFsctlTransactRsp + { + [FieldOffset(0)] + internal UInt16 Version; + [FieldOffset(2)] + internal UInt16 Size; + [FieldOffset(4)] + internal UInt32 Kind; + [FieldOffset(8)] + internal UInt64 Hint; + + [FieldOffset(16)] + internal IoStatus IoStatus; + + [FieldOffset(24)] + internal FileInfo WriteFileInfo; + + [FieldOffset(0)] + internal unsafe fixed Byte Padding[128]; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct FspFileSystemOperationContext + { + internal FspFsctlTransactReq *Request; + internal FspFsctlTransactRsp *Response; + } + [StructLayout(LayoutKind.Sequential)] internal struct FileSystemInterface { @@ -662,6 +721,12 @@ namespace Fsp.Interop internal delegate Int32 FspFileSystemStopDispatcher( IntPtr FileSystem); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void FspFileSystemSendResponse( + IntPtr FileSystem, + ref FspFsctlTransactRsp Response); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate ref FspFileSystemOperationContext FspFileSystemGetOperationContext(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr FspFileSystemMountPointF( IntPtr FileSystem); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -846,6 +911,8 @@ namespace Fsp.Interop internal static Proto.FspFileSystemRemoveMountPoint FspFileSystemRemoveMountPoint; internal static Proto.FspFileSystemStartDispatcher FspFileSystemStartDispatcher; internal static Proto.FspFileSystemStopDispatcher FspFileSystemStopDispatcher; + internal static Proto.FspFileSystemSendResponse FspFileSystemSendResponse; + internal static Proto.FspFileSystemGetOperationContext FspFileSystemGetOperationContext; internal static Proto.FspFileSystemMountPointF FspFileSystemMountPoint; internal static Proto.FspFileSystemSetOperationGuardStrategyF FspFileSystemSetOperationGuardStrategy; internal static Proto.FspFileSystemSetDebugLogF FspFileSystemSetDebugLog; @@ -892,6 +959,12 @@ namespace Fsp.Interop else return _FspFileSystemSetMountPointEx(FileSystem, MountPoint, IntPtr.Zero); } + internal static unsafe UInt64 FspFileSystemGetOperationRequestHint() + { + FspFileSystemOperationContext Context = FspFileSystemGetOperationContext(); + FspFsctlTransactReq Request = *Context.Request; + return Request.Hint; + } internal static unsafe Boolean FspFileSystemAddDirInfo( ref DirInfo DirInfo, IntPtr Buffer, @@ -1240,6 +1313,8 @@ namespace Fsp.Interop FspFileSystemRemoveMountPoint = GetEntryPoint(Module); FspFileSystemStartDispatcher = GetEntryPoint(Module); FspFileSystemStopDispatcher = GetEntryPoint(Module); + FspFileSystemSendResponse = GetEntryPoint(Module); + FspFileSystemGetOperationContext = GetEntryPoint(Module); FspFileSystemMountPoint = GetEntryPoint(Module); FspFileSystemSetOperationGuardStrategy = GetEntryPoint(Module); FspFileSystemSetDebugLog = GetEntryPoint(Module); From 1d619e0874aaa663fe821cee9fa063c3113c973c Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Tue, 14 May 2019 11:38:55 +0200 Subject: [PATCH 2/7] Use pointers instead of references. To avoid copying structs needlessly. --- src/dotnet/Interop.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index 08079c92..af3cfa6b 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -725,7 +725,7 @@ namespace Fsp.Interop IntPtr FileSystem, ref FspFsctlTransactRsp Response); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate ref FspFileSystemOperationContext FspFileSystemGetOperationContext(); + internal unsafe delegate FspFileSystemOperationContext *FspFileSystemGetOperationContext(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr FspFileSystemMountPointF( IntPtr FileSystem); @@ -961,9 +961,7 @@ namespace Fsp.Interop } internal static unsafe UInt64 FspFileSystemGetOperationRequestHint() { - FspFileSystemOperationContext Context = FspFileSystemGetOperationContext(); - FspFsctlTransactReq Request = *Context.Request; - return Request.Hint; + return FspFileSystemGetOperationContext()->Request->Hint; } internal static unsafe Boolean FspFileSystemAddDirInfo( ref DirInfo DirInfo, From af7e5432a783b3db69014f95b14d522dce99d5d3 Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Wed, 15 May 2019 14:09:08 +0200 Subject: [PATCH 3/7] Let the Status argument be a signed integer. The constants are defined as negative numbers, which would have required a cast to unsigned for each call. --- src/dotnet/FileSystemHost.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dotnet/FileSystemHost.cs b/src/dotnet/FileSystemHost.cs index 3f8bd2fb..590d5996 100644 --- a/src/dotnet/FileSystemHost.cs +++ b/src/dotnet/FileSystemHost.cs @@ -403,7 +403,7 @@ namespace Fsp /// /// Updated file information. /// - public void SendWriteResponse(UInt64 RequestHint, UInt32 Status, UInt32 BytesTransferred, ref FileInfo FileInfo) + public void SendWriteResponse(UInt64 RequestHint, Int32 Status, UInt32 BytesTransferred, ref FileInfo FileInfo) { var Response = new FspFsctlTransactRsp() { @@ -412,7 +412,7 @@ namespace Fsp Hint = RequestHint }; Response.IoStatus.Information = BytesTransferred; - Response.IoStatus.Status = Status; + Response.IoStatus.Status = (UInt32) Status; Response.WriteFileInfo = FileInfo; Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response); } @@ -428,7 +428,7 @@ namespace Fsp /// /// Number of bytes read. /// - public void SendReadResponse(UInt64 RequestHint, UInt32 Status, UInt32 BytesTransferred) + public void SendReadResponse(UInt64 RequestHint, Int32 Status, UInt32 BytesTransferred) { var Response = new FspFsctlTransactRsp() { @@ -437,7 +437,7 @@ namespace Fsp Hint = RequestHint }; Response.IoStatus.Information = BytesTransferred; - Response.IoStatus.Status = Status; + Response.IoStatus.Status = (UInt32) Status; Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response); } /// @@ -452,7 +452,7 @@ namespace Fsp /// /// Number of bytes read. /// - public void SendReadDirectoryResponse(UInt64 RequestHint, UInt32 Status, UInt32 BytesTransferred) + public void SendReadDirectoryResponse(UInt64 RequestHint, Int32 Status, UInt32 BytesTransferred) { var Response = new FspFsctlTransactRsp() { @@ -461,7 +461,7 @@ namespace Fsp Hint = RequestHint }; Response.IoStatus.Information = BytesTransferred; - Response.IoStatus.Status = Status; + Response.IoStatus.Status = (UInt32) Status; Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response); } From 879fa2464fa4201db92741aaa439e085d3b82b7b Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Tue, 4 Jun 2019 15:58:54 +0200 Subject: [PATCH 4/7] Add asynchronous I/O testing to memfs-dotnet. Make SeekableReadDirectory virtual, so that it can be overridden. --- src/dotnet/FileSystemBase.cs | 2 +- tst/memfs-dotnet/Program.cs | 164 ++++++++++++++++++++++++++++++++++- 2 files changed, 162 insertions(+), 4 deletions(-) diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index a0d4e5d7..e1ce2923 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -1291,7 +1291,7 @@ namespace Fsp ModificationDescriptor, ref ModifiedDescriptor); } - public Int32 SeekableReadDirectory( + public virtual Int32 SeekableReadDirectory( Object FileNode, Object FileDesc, String Pattern, diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index 3deb18bd..5782e3a2 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -19,12 +19,17 @@ * associated repository. */ +#define MEMFS_SLOWIO + using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Threading; +#if MEMFS_SLOWIO +using System.Threading.Tasks; +#endif using Fsp; using VolumeInfo = Fsp.Interop.VolumeInfo; @@ -232,15 +237,20 @@ namespace memfs class Memfs : FileSystemBase { + private FileSystemHost Host; public const UInt16 MEMFS_SECTOR_SIZE = 512; public const UInt16 MEMFS_SECTORS_PER_ALLOCATION_UNIT = 1; public Memfs( - Boolean CaseInsensitive, UInt32 MaxFileNodes, UInt32 MaxFileSize, String RootSddl) + Boolean CaseInsensitive, UInt32 MaxFileNodes, UInt32 MaxFileSize, String RootSddl, + UInt64 SlowioMaxDelay, UInt64 SlowioPercentDelay, UInt64 SlowioRarefyDelay) { this.FileNodeMap = new FileNodeMap(CaseInsensitive); this.MaxFileNodes = MaxFileNodes; this.MaxFileSize = MaxFileSize; + this.SlowioMaxDelay = SlowioMaxDelay; + this.SlowioPercentDelay = SlowioPercentDelay; + this.SlowioRarefyDelay = SlowioRarefyDelay; /* * Create root directory. @@ -259,7 +269,7 @@ namespace memfs public override Int32 Init(Object Host0) { - FileSystemHost Host = (FileSystemHost)Host0; + Host = (FileSystemHost)Host0; Host.SectorSize = Memfs.MEMFS_SECTOR_SIZE; Host.SectorsPerAllocationUnit = Memfs.MEMFS_SECTORS_PER_ALLOCATION_UNIT; Host.VolumeCreationTime = (UInt64)DateTime.Now.ToFileTimeUtc(); @@ -549,6 +559,89 @@ namespace memfs Interlocked.Decrement(ref FileNode.OpenCount); } +#if MEMFS_SLOWIO + private UInt64 Hash(UInt64 X) + { + X = (X ^ (X >> 30)) * 0xbf58476d1ce4e5b9ul; + X = (X ^ (X >> 27)) * 0x94d049bb133111ebul; + X = X ^ (X >> 31); + return X; + } + + private static int Spin = 0; + + private UInt64 PseudoRandom(UInt64 To) + { + /* John Oberschelp's PRNG */ + Interlocked.Increment(ref Spin); + return Hash((UInt64)Spin) % To; + } + + private bool SlowioReturnPending() + { + if (0 == SlowioMaxDelay) + { + return false; + } + return PseudoRandom(100) < SlowioPercentDelay; + } + + private void SlowioSnooze() + { + double Millis = PseudoRandom(SlowioMaxDelay + 1) >> (int) PseudoRandom(SlowioRarefyDelay + 1); + Thread.Sleep(TimeSpan.FromMilliseconds(Millis)); + } + + void SlowioReadTask( + Object FileNode0, + IntPtr Buffer, + UInt64 Offset, + UInt64 EndOffset, + UInt64 RequestHint) + { + SlowioSnooze(); + + UInt32 BytesTransferred = (UInt32)(EndOffset - Offset); + FileNode FileNode = (FileNode)FileNode0; + Marshal.Copy(FileNode.FileData, (int)Offset, Buffer, (int)BytesTransferred); + + Host.SendReadResponse(RequestHint, STATUS_SUCCESS, BytesTransferred); + } + + void SlowioWriteTask( + Object FileNode0, + IntPtr Buffer, + UInt64 Offset, + UInt64 EndOffset, + UInt64 RequestHint) + { + SlowioSnooze(); + + UInt32 BytesTransferred = (UInt32)(EndOffset - Offset); + FileNode FileNode = (FileNode)FileNode0; + FileInfo FileInfo = FileNode.GetFileInfo(); + Marshal.Copy(Buffer, FileNode.FileData, (int)Offset, (int)BytesTransferred); + + Host.SendWriteResponse(RequestHint, STATUS_SUCCESS, BytesTransferred, ref FileInfo); + } + + void SlowioReadDirectoryTask( + Object FileNode0, + Object FileDesc, + String Pattern, + String Marker, + IntPtr Buffer, + UInt32 Length, + UInt64 RequestHint) + { + SlowioSnooze(); + + UInt32 BytesTransferred; + var Status = base.SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); + Host.SendReadDirectoryResponse(RequestHint, Status, BytesTransferred); + } +#endif + public override Int32 Read( Object FileNode0, Object FileDesc, @@ -570,6 +663,17 @@ namespace memfs if (EndOffset > FileNode.FileInfo.FileSize) EndOffset = FileNode.FileInfo.FileSize; +#if MEMFS_SLOWIO + if (SlowioReturnPending()) + { + var Hint = Host.GetOperationRequestHint(); + Task.Run(() => SlowioReadTask(FileNode0, Buffer, Offset, EndOffset, Hint)).ConfigureAwait(false); + + BytesTransferred = 0; + return STATUS_PENDING; + } +#endif + BytesTransferred = (UInt32)(EndOffset - Offset); Marshal.Copy(FileNode.FileData, (int)Offset, Buffer, (int)BytesTransferred); @@ -619,6 +723,18 @@ namespace memfs } } +#if MEMFS_SLOWIO + if (SlowioReturnPending()) + { + var hint = Host.GetOperationRequestHint(); + Task.Run(() => SlowioWriteTask(FileNode0, Buffer, Offset, EndOffset, hint)).ConfigureAwait(false); + + BytesTransferred = 0; + FileInfo = default(FileInfo); + return STATUS_PENDING; + } +#endif + BytesTransferred = (UInt32)(EndOffset - Offset); Marshal.Copy(Buffer, FileNode.FileData, (int)Offset, (int)BytesTransferred); @@ -902,6 +1018,29 @@ namespace memfs return false; } +#if MEMFS_SLOWIO + public override int SeekableReadDirectory( + Object FileNode0, + Object FileDesc, + String Pattern, + String Marker, + IntPtr Buffer, + UInt32 Length, + out UInt32 BytesTransferred) + { + if (SlowioReturnPending()) + { + var Hint = Host.GetOperationRequestHint(); + Task.Run(() => SlowioReadDirectoryTask(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, Hint)); + BytesTransferred = 0; + + return STATUS_PENDING; + } + + return base.SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); + } +#endif + public override int GetDirInfoByName( Object ParentNode0, Object FileDesc, @@ -1140,6 +1279,9 @@ namespace memfs private FileNodeMap FileNodeMap; private UInt32 MaxFileNodes; private UInt32 MaxFileSize; + private UInt64 SlowioMaxDelay; + private UInt64 SlowioPercentDelay; + private UInt64 SlowioRarefyDelay; private String VolumeLabel; } @@ -1170,6 +1312,9 @@ namespace memfs UInt32 FileInfoTimeout = unchecked((UInt32)(-1)); UInt32 MaxFileNodes = 1024; UInt32 MaxFileSize = 16 * 1024 * 1024; + UInt32 SlowioMaxDelay = 0; + UInt32 SlowioPercentDelay = 0; + UInt32 SlowioRarefyDelay = 0; String FileSystemName = null; String VolumePrefix = null; String MountPoint = null; @@ -1202,9 +1347,18 @@ namespace memfs case 'm': argtos(Args, ref I, ref MountPoint); break; + case 'M': + argtol(Args, ref I, ref SlowioMaxDelay); + break; case 'n': argtol(Args, ref I, ref MaxFileNodes); break; + case 'P': + argtol(Args, ref I, ref SlowioPercentDelay); + break; + case 'R': + argtol(Args, ref I, ref SlowioRarefyDelay); + break; case 'S': argtos(Args, ref I, ref RootSddl); break; @@ -1233,7 +1387,8 @@ namespace memfs throw new CommandLineUsageException("cannot open debug log file"); Host = new FileSystemHost(Memfs = new Memfs( - CaseInsensitive, MaxFileNodes, MaxFileSize, RootSddl)); + CaseInsensitive, MaxFileNodes, MaxFileSize, RootSddl, + SlowioMaxDelay, SlowioPercentDelay, SlowioRarefyDelay)); Host.FileInfoTimeout = FileInfoTimeout; Host.Prefix = VolumePrefix; Host.FileSystemName = null != FileSystemName ? FileSystemName : "-MEMFS"; @@ -1262,6 +1417,9 @@ namespace memfs " -t FileInfoTimeout [millis]\n" + " -n MaxFileNodes\n" + " -s MaxFileSize [bytes]\n" + + " -M MaxDelay [maximum slow IO delay in millis]\n" + + " -P PercentDelay [percent of slow IO to make pending]\n" + + " -R RarefyDelay [adjust the rarity of pending slow IO]\n" + " -F FileSystemName\n" + " -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n" + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + From ce436fc29a58e0f6fb22e00462769d8572c5c875 Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Tue, 4 Jun 2019 17:10:33 +0200 Subject: [PATCH 5/7] Attempt to add a slowio test for memfs-dotnet. --- tools/run-tests.bat | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/run-tests.bat b/tools/run-tests.bat index c2199ef5..29fd5c3b 100755 --- a/tools/run-tests.bat +++ b/tools/run-tests.bat @@ -71,6 +71,7 @@ set dfl_tests=^ winfsp-tests-dotnet-external-share ^ fsx-memfs-dotnet-disk ^ fsx-memfs-dotnet-net ^ + fsx-memfs-dotnet-slowio ^ winfstest-memfs-dotnet-disk ^ winfstest-memfs-dotnet-net set opt_tests=^ @@ -498,6 +499,11 @@ call :__run_fsx_memfs_slowio_test memfs32-slowio memfs-x86 if !ERRORLEVEL! neq 0 goto fail exit /b 0 +:fsx-memfs-dotnet-slowio +call :__run_fsx_memfs_slowio_test memfs.net-slowio memfs-dotnet-msil +if !ERRORLEVEL! neq 0 goto fail +exit /b 0 + :__run_fsx_memfs_slowio_test set RunSampleTestExit=0 call "%ProjRoot%\tools\fsreg" %1 "%ProjRoot%\build\VStudio\build\%Configuration%\%2.exe" "-u %%%%1 -m %%%%2 -M 50 -P 10 -R 5" "D:P(A;;RPWPLC;;;WD)" From 02fd6906c2869238c3cd192de110544d72719ea4 Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Wed, 5 Jun 2019 09:24:27 +0200 Subject: [PATCH 6/7] Revert making SeekableReadDirectory virtual. This would be an API-breaking change that is actually pointless. Override ReadDirectory instead, as intended. --- src/dotnet/FileSystemBase.cs | 2 +- tst/memfs-dotnet/Program.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index e1ce2923..a0d4e5d7 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -1291,7 +1291,7 @@ namespace Fsp ModificationDescriptor, ref ModifiedDescriptor); } - public virtual Int32 SeekableReadDirectory( + public Int32 SeekableReadDirectory( Object FileNode, Object FileDesc, String Pattern, diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index 5782e3a2..15a5ff04 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -637,7 +637,7 @@ namespace memfs SlowioSnooze(); UInt32 BytesTransferred; - var Status = base.SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); + var Status = SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); Host.SendReadDirectoryResponse(RequestHint, Status, BytesTransferred); } #endif @@ -1019,7 +1019,7 @@ namespace memfs } #if MEMFS_SLOWIO - public override int SeekableReadDirectory( + public override int ReadDirectory( Object FileNode0, Object FileDesc, String Pattern, @@ -1037,7 +1037,7 @@ namespace memfs return STATUS_PENDING; } - return base.SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); + return SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); } #endif From bfd8dca62d4e084ea43410eb0f0c59ca558732bd Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Thu, 20 Jun 2019 15:49:02 +0200 Subject: [PATCH 7/7] Delay unmounting until all Slowio tasks are done. Preventing a crash when unmounting a filesystem with pending Slowio. --- tst/memfs-dotnet/Program.cs | 69 ++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index 15a5ff04..3ceff8b9 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -287,6 +287,20 @@ namespace memfs return STATUS_SUCCESS; } +#if MEMFS_SLOWIO + public override int Mounted(object Host) + { + SlowioTasksRunning = 0; + return STATUS_SUCCESS; + } + + public override void Unmounted(object Host) + { + while (SlowioTasksRunning != 0) + Thread.Sleep(1000); + } +#endif + public override Int32 GetVolumeInfo( out VolumeInfo VolumeInfo) { @@ -592,7 +606,7 @@ namespace memfs Thread.Sleep(TimeSpan.FromMilliseconds(Millis)); } - void SlowioReadTask( + private void SlowioReadTask( Object FileNode0, IntPtr Buffer, UInt64 Offset, @@ -606,9 +620,10 @@ namespace memfs Marshal.Copy(FileNode.FileData, (int)Offset, Buffer, (int)BytesTransferred); Host.SendReadResponse(RequestHint, STATUS_SUCCESS, BytesTransferred); + Interlocked.Decrement(ref SlowioTasksRunning); } - void SlowioWriteTask( + private void SlowioWriteTask( Object FileNode0, IntPtr Buffer, UInt64 Offset, @@ -623,9 +638,10 @@ namespace memfs Marshal.Copy(Buffer, FileNode.FileData, (int)Offset, (int)BytesTransferred); Host.SendWriteResponse(RequestHint, STATUS_SUCCESS, BytesTransferred, ref FileInfo); + Interlocked.Decrement(ref SlowioTasksRunning); } - void SlowioReadDirectoryTask( + private void SlowioReadDirectoryTask( Object FileNode0, Object FileDesc, String Pattern, @@ -638,7 +654,9 @@ namespace memfs UInt32 BytesTransferred; var Status = SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); + Host.SendReadDirectoryResponse(RequestHint, Status, BytesTransferred); + Interlocked.Decrement(ref SlowioTasksRunning); } #endif @@ -667,10 +685,18 @@ namespace memfs if (SlowioReturnPending()) { var Hint = Host.GetOperationRequestHint(); - Task.Run(() => SlowioReadTask(FileNode0, Buffer, Offset, EndOffset, Hint)).ConfigureAwait(false); + try + { + Interlocked.Increment(ref SlowioTasksRunning); + Task.Run(() => SlowioReadTask(FileNode0, Buffer, Offset, EndOffset, Hint)).ConfigureAwait(false); - BytesTransferred = 0; - return STATUS_PENDING; + BytesTransferred = 0; + return STATUS_PENDING; + } + catch (Exception) + { + Interlocked.Decrement(ref SlowioTasksRunning); + } } #endif @@ -727,11 +753,19 @@ namespace memfs if (SlowioReturnPending()) { var hint = Host.GetOperationRequestHint(); - Task.Run(() => SlowioWriteTask(FileNode0, Buffer, Offset, EndOffset, hint)).ConfigureAwait(false); + try + { + Interlocked.Increment(ref SlowioTasksRunning); + Task.Run(() => SlowioWriteTask(FileNode0, Buffer, Offset, EndOffset, hint)).ConfigureAwait(false); - BytesTransferred = 0; - FileInfo = default(FileInfo); - return STATUS_PENDING; + BytesTransferred = 0; + FileInfo = default(FileInfo); + return STATUS_PENDING; + } + catch (Exception) + { + Interlocked.Decrement(ref SlowioTasksRunning); + } } #endif @@ -1031,10 +1065,18 @@ namespace memfs if (SlowioReturnPending()) { var Hint = Host.GetOperationRequestHint(); - Task.Run(() => SlowioReadDirectoryTask(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, Hint)); - BytesTransferred = 0; + try + { + Interlocked.Increment(ref SlowioTasksRunning); + Task.Run(() => SlowioReadDirectoryTask(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, Hint)); + BytesTransferred = 0; - return STATUS_PENDING; + return STATUS_PENDING; + } + catch (Exception) + { + Interlocked.Decrement(ref SlowioTasksRunning); + } } return SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); @@ -1282,6 +1324,7 @@ namespace memfs private UInt64 SlowioMaxDelay; private UInt64 SlowioPercentDelay; private UInt64 SlowioRarefyDelay; + private volatile Int32 SlowioTasksRunning; private String VolumeLabel; }