BUG: Lucene.Net.Store.MMapDirectory: Fixed issue with FileStream not being disposed when the file length is 0.
Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/8ab834f3 Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/8ab834f3 Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/8ab834f3 Branch: refs/heads/master Commit: 8ab834f3525aa55af334c10b9a5dfa1a759ec44a Parents: 9c66bd1 Author: Shad Storhaug <[email protected]> Authored: Thu Sep 21 23:24:12 2017 +0700 Committer: Shad Storhaug <[email protected]> Committed: Fri Sep 22 12:45:18 2017 +0700 ---------------------------------------------------------------------- src/Lucene.Net.Tests/Store/TestMultiMMap.cs | 21 ++++++++++++ src/Lucene.Net/Store/MMapDirectory.cs | 42 ++++++++++++++++-------- 2 files changed, 49 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucenenet/blob/8ab834f3/src/Lucene.Net.Tests/Store/TestMultiMMap.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests/Store/TestMultiMMap.cs b/src/Lucene.Net.Tests/Store/TestMultiMMap.cs index 81aa60b..00c18d0 100644 --- a/src/Lucene.Net.Tests/Store/TestMultiMMap.cs +++ b/src/Lucene.Net.Tests/Store/TestMultiMMap.cs @@ -3,6 +3,7 @@ using Lucene.Net.Support; using NUnit.Framework; using System; using System.IO; +using System.Text; namespace Lucene.Net.Store { @@ -435,5 +436,25 @@ namespace Lucene.Net.Store reader.Dispose(); dir.Dispose(); } + + + [Test, LuceneNetSpecific] + public void TestDisposeIndexInput() + { + string name = "foobar"; + var dir = CreateTempDir("testDisposeIndexInput"); + string fileName = Path.Combine(dir.FullName, name); + + // Create a zero byte file, and close it immediately + File.WriteAllText(fileName, string.Empty, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false) /* No BOM */); + + MMapDirectory mmapDir = new MMapDirectory(dir); + using (var indexInput = mmapDir.OpenInput(name, NewIOContext(Random()))) + { + } // Dispose + + // Now it should be possible to delete the file. This is the condition we are testing for. + File.Delete(fileName); + } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/lucenenet/blob/8ab834f3/src/Lucene.Net/Store/MMapDirectory.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net/Store/MMapDirectory.cs b/src/Lucene.Net/Store/MMapDirectory.cs index b116b90..cb2dc0b 100644 --- a/src/Lucene.Net/Store/MMapDirectory.cs +++ b/src/Lucene.Net/Store/MMapDirectory.cs @@ -220,16 +220,13 @@ namespace Lucene.Net.Store { EnsureOpen(); var file = new FileInfo(Path.Combine(Directory.FullName, name)); - - var c = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - - return new MMapIndexInput(this, "MMapIndexInput(path=\"" + file + "\")", c); + var fc = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return new MMapIndexInput(this, "MMapIndexInput(path=\"" + file + "\")", fc); } public override IndexInputSlicer CreateSlicer(string name, IOContext context) { var full = (MMapIndexInput)OpenInput(name, context); - return new IndexInputSlicerAnonymousInnerClassHelper(this, full); } @@ -269,30 +266,47 @@ namespace Lucene.Net.Store public sealed class MMapIndexInput : ByteBufferIndexInput { - private readonly bool useUnmapHack; + //private readonly bool useUnmapHack; //private string mapName; // LUCENENET NOTE: Not used internal MemoryMappedFile memoryMappedFile; // .NET port: this is equivalent to FileChannel.map + private readonly FileStream fc; private readonly MMapDirectory outerInstance; internal MMapIndexInput(MMapDirectory outerInstance, string resourceDescription, FileStream fc) : base(resourceDescription, null, fc.Length, outerInstance.chunkSizePower, outerInstance.UseUnmap) { this.outerInstance = outerInstance; - this.useUnmapHack = outerInstance.UseUnmap; + this.fc = fc ?? throw new ArgumentNullException("fc"); + //this.useUnmapHack = outerInstance.UseUnmap; this.SetBuffers(outerInstance.Map(this, fc, 0, fc.Length)); } - protected override /*sealed*/ void Dispose(bool disposing) // LUCENENET specific - not marked sealed in case subclass needs to dispose + protected override sealed void Dispose(bool disposing) { - if (disposing) + try { - if (null != this.memoryMappedFile) + if (disposing) { - this.memoryMappedFile.Dispose(); - this.memoryMappedFile = null; + try + { + if (this.memoryMappedFile != null) + { + this.memoryMappedFile.Dispose(); + this.memoryMappedFile = null; + } + } + finally + { + // LUCENENET: If the file is 0 length we will not create a memoryMappedFile above + // so we must always ensure the FileStream is explicitly disposed. + this.fc.Dispose(); + } } } - base.Dispose(disposing); + finally + { + base.Dispose(disposing); + } } /// <summary> @@ -344,7 +358,7 @@ namespace Lucene.Net.Store memoryMappedFileSecurity: null, #endif inheritability: HandleInheritability.Inheritable, - leaveOpen: false); + leaveOpen: true); // LUCENENET: We explicitly dispose the FileStream separately. } long bufferStart = 0L;
