From 879fa2464fa4201db92741aaa439e085d3b82b7b Mon Sep 17 00:00:00 2001 From: "Felix A. Croes" Date: Tue, 4 Jun 2019 15:58:54 +0200 Subject: [PATCH] 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" +