From fb129b9408716515a3874ecd150e7b264d608c41 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Wed, 6 May 2026 08:10:12 -0600 Subject: [PATCH 01/20] Fix IndexWriter.Dispose leaking file handles on CommitInternal failure, #1284 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport of LUCENE-5871 (upstream commit 2cfcdcc on branch_4x). If anything before the cleanup steps in the close path throws — for example CommitInternal due to an I/O failure — the old straight-line CloseInternal skipped DropAll, deleter.Dispose(), and writeLock.Dispose(), leaking the reader pool, deleter, and write.lock file handle. Replaces CloseInternal with a Shutdown(bool) method that runs Flush, FinishMerges, CommitInternal, then RollbackInternal in a try/finally so RollbackInternal always runs and releases all resources. Also moves the numDocsInRAM accounting in DocumentsWriter into a finally block so a partially-applied addDocument is still counted correctly. Tests added: - TestHasUncommittedChangesAfterException, TestDoubleClose, TestRollbackThenClose, TestCloseThenRollback, TestRollbackWhileMergeIsRunning, TestCloseDuringCommit (TestIndexWriter) - TestOutOfMemoryErrorRollback (TestIndexWriterExceptions) - TestDisposeDoesNotLeakWriteLockOnCommitFailure ([LuceneNetSpecific] regression test that explicitly verifies write.lock is released after a CommitInternal IOException) TestOtherFiles2 was removed in the upstream commit — its assertion no longer holds because a graceful close now checkpoints unreferenced files. Kept commented out at its original location for porting reference. Co-Authored-By: Claude Haiku 4.5 --- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 365 ++++++++++++++++-- .../Index/TestIndexWriterExceptions.cs | 71 ++++ src/Lucene.Net/Index/DocumentsWriter.cs | 13 +- src/Lucene.Net/Index/IndexWriter.cs | 248 ++---------- 4 files changed, 453 insertions(+), 244 deletions(-) diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 651c22777f..37ab04e5d6 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -2,6 +2,7 @@ using J2N.Threading; using Lucene.Net.Analysis; using Lucene.Net.Analysis.TokenAttributes; +using Lucene.Net.Attributes; using Lucene.Net.Codecs; using Lucene.Net.Codecs.SimpleText; using Lucene.Net.Diagnostics; @@ -1206,10 +1207,10 @@ public override void Run() MockDirectoryWrapper dir = new MockDirectoryWrapper(random, new RAMDirectory()); //var dir = new RAMDirectory(); - // When interrupt arrives in w.Dispose(), when it's - // writing liveDocs, this can lead to double-write of - // _X_N.del: - //dir.setPreventDoubleWrite(false); + // When interrupt arrives in w.Dispose(), this can + // lead to double-write of files: + dir.PreventDoubleWrite = false; + IndexWriter w = null; while (!finish) { @@ -2429,35 +2430,44 @@ public virtual void TestStopwordsPosIncHole2() dir.Dispose(); } - // here we do better, there is no current segments file, so we don't delete anything. - // however, if you actually go and make a commit, the next time you run indexwriter - // this file will be gone. - [Test] - public virtual void TestOtherFiles2() - { - Directory dir = NewDirectory(); - try - { - // Create my own random file: - IndexOutput @out = dir.CreateOutput("_a.frq", NewIOContext(Random)); - @out.WriteByte((byte)42); - @out.Dispose(); - - new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).Dispose(); - - Assert.IsTrue(SlowFileExists(dir, "_a.frq")); - - IndexWriter iw = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - iw.AddDocument(new Document()); - iw.Dispose(); - - Assert.IsFalse(SlowFileExists(dir, "_a.frq")); - } - finally - { - dir.Dispose(); - } - } + // LUCENENET: this test was removed upstream in commit 2cfcdcc (LUCENE-5871), + // which is the backport that fixes #1284. The fix changes Dispose() so that a + // graceful close runs a final CommitInternal followed by RollbackInternal, + // which checkpoints the deleter and removes unreferenced files. The original + // assertion `Assert.IsTrue(SlowFileExists(dir, "_a.frq"))` after the first + // Dispose() therefore no longer holds — _a.frq is now deleted on the very + // first close, before the second IndexWriter is opened. We keep the test + // commented out at its original location for porting reference. + // + //// here we do better, there is no current segments file, so we don't delete anything. + //// however, if you actually go and make a commit, the next time you run indexwriter + //// this file will be gone. + //[Test] + //public virtual void TestOtherFiles2() + //{ + // Directory dir = NewDirectory(); + // try + // { + // // Create my own random file: + // IndexOutput @out = dir.CreateOutput("_a.frq", NewIOContext(Random)); + // @out.WriteByte((byte)42); + // @out.Dispose(); + // + // new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).Dispose(); + // + // Assert.IsTrue(SlowFileExists(dir, "_a.frq")); + // + // IndexWriter iw = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); + // iw.AddDocument(new Document()); + // iw.Dispose(); + // + // Assert.IsFalse(SlowFileExists(dir, "_a.frq")); + // } + // finally + // { + // dir.Dispose(); + // } + //} // LUCENE-4398 [Test] @@ -2940,6 +2950,153 @@ public virtual void TestDeleteSameTermAcrossFields() dir.Dispose(); } + [Test] + public virtual void TestHasUncommittedChangesAfterException() + { + Analyzer analyzer = new MockAnalyzer(Random); + AssumeTrue("requires doc values", DefaultCodecSupportsDocValues); + + Directory directory = NewDirectory(); + // we don't use RandomIndexWriter because it might add more docvalues than we expect !!!! + IndexWriterConfig iwc = NewIndexWriterConfig(TEST_VERSION_CURRENT, analyzer); + iwc.SetMergePolicy(NewLogMergePolicy()); + IndexWriter iwriter = new IndexWriter(directory, iwc); + Document doc = new Document(); + doc.Add(new SortedDocValuesField("dv", new BytesRef("foo!"))); + doc.Add(new SortedDocValuesField("dv", new BytesRef("bar!"))); + try + { + iwriter.AddDocument(doc); + Assert.Fail("didn't hit expected exception"); + } + catch (Exception expected) when (expected.IsIllegalArgumentException()) + { + // expected + } + iwriter.Commit(); + Assert.IsFalse(iwriter.HasUncommittedChanges()); + iwriter.Dispose(); + directory.Dispose(); + } + + [Test] + public virtual void TestDoubleClose() + { + Directory dir = NewDirectory(); + IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); + w.AddDocument(new Document()); + w.Dispose(); + // Close again should have no effect + w.Dispose(); + dir.Dispose(); + } + + [Test] + public virtual void TestRollbackThenClose() + { + Directory dir = NewDirectory(); + IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); + w.AddDocument(new Document()); + w.Rollback(); + // Close after rollback should have no effect + w.Dispose(); + dir.Dispose(); + } + + [Test] + public virtual void TestCloseThenRollback() + { + Directory dir = NewDirectory(); + IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); + w.AddDocument(new Document()); + w.Dispose(); + // Rollback after close should have no effect + w.Rollback(); + dir.Dispose(); + } + + [Test] + public virtual void TestRollbackWhileMergeIsRunning() + { + Directory dir = NewDirectory(); + + CountdownEvent mergeStarted = new CountdownEvent(1); + CountdownEvent closeStarted = new CountdownEvent(1); + + IndexWriterConfig iwc = NewIndexWriterConfig(Random, TEST_VERSION_CURRENT, new MockAnalyzer(Random)); + LogDocMergePolicy mp = new LogDocMergePolicy(); + mp.MergeFactor = 2; + iwc.SetMergePolicy(mp); + iwc.SetInfoStream(new RollbackWhileMergeInfoStream(closeStarted)); + + iwc.SetMergeScheduler(new RollbackWhileMergeScheduler(mergeStarted, closeStarted)); + IndexWriter w = new IndexWriter(dir, iwc); + w.AddDocument(new Document()); + w.Commit(); + w.AddDocument(new Document()); + w.Commit(); + w.Rollback(); + dir.Dispose(); + } + + private sealed class RollbackWhileMergeInfoStream : InfoStream + { + private readonly CountdownEvent closeStarted; + + public RollbackWhileMergeInfoStream(CountdownEvent closeStarted) + { + this.closeStarted = closeStarted; + } + + public override bool IsEnabled(string component) + { + return true; + } + + public override void Message(string component, string message) + { + if (message.Equals("rollback", StringComparison.Ordinal)) + { + closeStarted.Signal(); + } + } + + protected override void Dispose(bool disposing) + { + } + } + + private sealed class RollbackWhileMergeScheduler : ConcurrentMergeScheduler + { + private readonly CountdownEvent mergeStarted; + private readonly CountdownEvent closeStarted; + + public RollbackWhileMergeScheduler(CountdownEvent mergeStarted, CountdownEvent closeStarted) + { + this.mergeStarted = mergeStarted; + this.closeStarted = closeStarted; + } + + protected internal override void DoMerge(MergePolicy.OneMerge merge) + { + mergeStarted.Signal(); + try + { + closeStarted.Wait(); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + Thread.CurrentThread.Interrupt(); + throw RuntimeException.Create(ie); + } + base.DoMerge(merge); + } + + protected override void Dispose(bool disposing) + { + } + } + // LUCENE-5574 [Test] public virtual void TestClosingNRTReaderDoesNotCorruptYourIndex() @@ -2984,6 +3141,148 @@ public virtual void TestClosingNRTReaderDoesNotCorruptYourIndex() r.Dispose(); dir.Dispose(); } + + /// + /// Make sure that close waits for any still-running commits. + [Test] + public virtual void TestCloseDuringCommit() + { + CountdownEvent startCommit = new CountdownEvent(1); + CountdownEvent finishCommit = new CountdownEvent(1); + + Directory dir = NewDirectory(); + IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + // infostream that "takes a long time" to commit + iwc.SetInfoStream(new SlowCommittingInfoStream(startCommit)); + IndexWriter iw = new IndexWriter(dir, iwc); + Document doc = new Document(); + new ThreadAnonymousClassForCloseDuringCommit(iw, finishCommit).Start(); + startCommit.Wait(); + try + { + iw.Dispose(); + } + catch (Exception ise) when (ise.IsIllegalStateException()) + { + // OK, but not required (depends on thread scheduling) + } + finishCommit.Wait(); + iw.Dispose(); + dir.Dispose(); + } + + private sealed class SlowCommittingInfoStream : InfoStream + { + private readonly CountdownEvent startCommit; + + public SlowCommittingInfoStream(CountdownEvent startCommit) + { + this.startCommit = startCommit; + } + + public override void Message(string component, string message) + { + if (message.Equals("finishStartCommit", StringComparison.Ordinal)) + { + startCommit.Signal(); + try + { + Thread.Sleep(10); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } + } + } + + public override bool IsEnabled(string component) + { + return true; + } + + protected override void Dispose(bool disposing) + { + } + } + + private sealed class ThreadAnonymousClassForCloseDuringCommit : ThreadJob + { + private readonly IndexWriter iw; + private readonly CountdownEvent finishCommit; + + public ThreadAnonymousClassForCloseDuringCommit(IndexWriter iw, CountdownEvent finishCommit) + { + this.iw = iw; + this.finishCommit = finishCommit; + } + + public override void Run() + { + try + { + iw.Commit(); + finishCommit.Signal(); + } + catch (IOException ioe) + { + throw RuntimeException.Create(ioe); + } + } + } + + // LUCENENET specific: regression test for #1284 — IndexWriter.Dispose + // must not leak the write.lock file handle when CommitInternal throws + // an I/O exception during close. The upstream LUCENE-5871 backport + // (commit 2cfcdcc) covers the OOME path; this test additionally + // proves the I/O exception path releases the write lock so a fresh + // IndexWriter can be opened on the same directory. + [Test, LuceneNetSpecific] + public virtual void TestDisposeDoesNotLeakWriteLockOnCommitFailure() + { + MockDirectoryWrapper dir = NewMockDirectory(); + // Ensure we go through the lock factory rather than NoLockFactory. + dir.PreventDoubleWrite = false; + dir.FailOn(new FailOnceInCommitFinishCommit()); + + IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); + writer.AddDocument(new Document()); + + try + { + writer.Dispose(); + Assert.Fail("expected IOException from Dispose"); + } + catch (IOException expected) when (expected.Message != null && expected.Message.Contains("on purpose")) + { + // expected + } + + // The write lock must have been released even though CommitInternal threw. + // Pre-fix, this assertion fails because Dispose's straight-line cleanup + // skipped writeLock.Dispose() when CommitInternal threw. + Assert.IsFalse(dir.MakeLock(IndexWriter.WRITE_LOCK_NAME).IsLocked(), + "write.lock should be released even when CommitInternal throws"); + + dir.Dispose(); + } + + // Throws an IOException exactly once from inside IndexWriter.FinishCommit + // (which is on the Commit/CommitInternal call stack). After it has thrown + // once it disables itself so the subsequent rollback can complete. + private sealed class FailOnceInCommitFinishCommit : Lucene.Net.Store.Failure + { + private bool fired; + + public override void Eval(MockDirectoryWrapper dir) + { + if (!fired && StackTraceHelper.DoesStackTraceContainMethod(nameof(IndexWriter), "FinishCommit")) + { + fired = true; + throw new IOException("now failing on purpose"); + } + } + } #endif } } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs index df2759599b..be76f3d7eb 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs @@ -1263,6 +1263,77 @@ protected override void Dispose(bool disposing) } } + /// + /// If IW hits OOME during indexing, it should refuse to commit any further changes. + [Test] + public virtual void TestOutOfMemoryErrorRollback() + { + AtomicBoolean thrown = new AtomicBoolean(false); + Directory dir = NewDirectory(); + IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)) + .SetInfoStream(new TOOMRollbackInfoStreamAnonymousClass(thrown))); + writer.AddDocument(new Document()); + + try + { + writer.Commit(); + Assert.Fail("OutOfMemoryError expected"); + } + catch (Exception expected) when (expected.IsOutOfMemoryError()) + { + } + + try + { + writer.Dispose(); + } + catch (Exception ise) when (ise.IsIllegalStateException()) + { + // expected + } + + try + { + writer.AddDocument(new Document()); + } + catch (Exception ace) when (ace.IsAlreadyClosedException()) + { + // expected + } + + // IW should have done rollback() during close, since it hit OOME, and so no index should exist: + Assert.IsFalse(DirectoryReader.IndexExists(dir)); + + dir.Dispose(); + } + + private sealed class TOOMRollbackInfoStreamAnonymousClass : InfoStream + { + private readonly AtomicBoolean thrown; + + public TOOMRollbackInfoStreamAnonymousClass(AtomicBoolean thrown) + { + this.thrown = thrown; + } + + public override void Message(string component, string message) + { + if (message.Contains("startFullFlush") && thrown.CompareAndSet(false, true)) + { + throw OutOfMemoryError.Create("fake OOME at " + message); + } + } + + public override bool IsEnabled(string component) + { + return true; + } + + protected override void Dispose(bool disposing) + { + } + } + // LUCENE-1347 private sealed class TestPoint4 : ITestPoint { diff --git a/src/Lucene.Net/Index/DocumentsWriter.cs b/src/Lucene.Net/Index/DocumentsWriter.cs index e464ebc0e9..1cada48a1d 100644 --- a/src/Lucene.Net/Index/DocumentsWriter.cs +++ b/src/Lucene.Net/Index/DocumentsWriter.cs @@ -538,11 +538,14 @@ internal bool UpdateDocuments(IEnumerable> docs, An int dwptNumDocs = dwpt.NumDocsInRAM; try { - int docCount = dwpt.UpdateDocuments(docs, analyzer, delTerm); - numDocsInRAM.AddAndGet(docCount); + dwpt.UpdateDocuments(docs, analyzer, delTerm); } finally { + // We don't know how many documents were actually + // counted as indexed, so we must subtract here to + // accumulate our separate counter: + numDocsInRAM.AddAndGet(dwpt.NumDocsInRAM - dwptNumDocs); if (dwpt.CheckAndResetHasAborted()) { if (dwpt.PendingFilesToDelete.Count > 0) @@ -585,10 +588,13 @@ internal bool UpdateDocument(IEnumerable doc, Analyzer analyzer try { dwpt.UpdateDocument(doc, analyzer, delTerm); - numDocsInRAM.IncrementAndGet(); } finally { + // We don't know whether the document actually + // counted as being indexed, so we must subtract here to + // accumulate our separate counter: + numDocsInRAM.AddAndGet(dwpt.NumDocsInRAM - dwptNumDocs); if (dwpt.CheckAndResetHasAborted()) { if (dwpt.PendingFilesToDelete.Count > 0) @@ -732,6 +738,7 @@ internal void SubtractFlushedNumDocs(int numFlushed) { oldValue = numDocsInRAM; } + if (Debugging.AssertsEnabled) Debugging.Assert(numDocsInRAM >= 0); } // for asserts diff --git a/src/Lucene.Net/Index/IndexWriter.cs b/src/Lucene.Net/Index/IndexWriter.cs index 5f3ab00582..1598d03b06 100644 --- a/src/Lucene.Net/Index/IndexWriter.cs +++ b/src/Lucene.Net/Index/IndexWriter.cs @@ -1188,44 +1188,55 @@ protected virtual void Dispose(bool disposing, bool waitForMerges) [MethodImpl(MethodImplOptions.AggressiveInlining)] // LUCENENET NOTE: this will interfere with stack trace inspection in tests, but that case should be covered by Dispose above which is NoInlining internal void Close(bool waitForMerges) // LUCENENET: made internal for test purposes { + Shutdown(waitForMerges); + } + + /// + /// Gracefully closes (commits, waits for merges), but calls rollback + /// if there's an exc so the IndexWriter is always closed. + /// + // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc) to fix #1284 — + // previously the close path's straight-line cleanup leaked the reader pool, + // deleter, and write.lock when CommitInternal threw an I/O exception. + private void Shutdown(bool waitForMerges) + { + if (pendingCommit != null) + { + throw IllegalStateException.Create("cannot close: prepareCommit was already called with no corresponding call to commit"); + } // Ensure that only one thread actually gets to do the - // closing, and make sure no commit is also in progress: - UninterruptableMonitor.Enter(commitLock); - try + // closing + if (ShouldClose()) { - if (ShouldClose()) + bool success = false; + try { - // If any methods have hit OutOfMemoryError, then abort - // on close, in case the internal state of IndexWriter - // or DocumentsWriter is corrupt - if (hitOOM) + if (infoStream.IsEnabled("IW")) { - RollbackInternal(); + infoStream.Message("IW", "now flush at close"); } - else + Flush(true, true); + FinishMerges(waitForMerges); + CommitInternal(); + RollbackInternal(); // ie close, since we just committed + success = true; + } + finally + { + if (success == false) { - CloseInternal(waitForMerges, true); - if (Debugging.AssertsEnabled) Debugging.Assert(AssertEventQueueAfterClose()); + // Be certain to close the index on any exception + try + { + RollbackInternal(); + } + catch (Exception t) when (t.IsThrowable()) + { + // Suppress so we keep throwing original exception + } } } } - finally - { - UninterruptableMonitor.Exit(commitLock); - } - } - - private bool AssertEventQueueAfterClose() - { - if (eventQueue.IsEmpty) - { - return true; - } - foreach (IEvent e in eventQueue) - { - if (Debugging.AssertsEnabled) Debugging.Assert(e is DocumentsWriter.MergePendingEvent, "{0}", e); - } - return true; } /// @@ -1267,185 +1278,6 @@ private bool ShouldClose() } } - private void CloseInternal(bool waitForMerges, bool doFlush) - { - bool interrupted = false; - try - { - if (pendingCommit != null) - { - throw IllegalStateException.Create("cannot close: prepareCommit was already called with no corresponding call to commit"); - } - - if (infoStream.IsEnabled("IW")) - { - infoStream.Message("IW", "now flush at close waitForMerges=" + waitForMerges); - } - - docWriter.Dispose(); - - try - { - // Only allow a new merge to be triggered if we are - // going to wait for merges: - if (doFlush) - { - Flush(waitForMerges, true); - } - else - { - docWriter.Abort(this); // already closed -- never sync on IW - } - } - finally - { - try - { - // LUCENENET specific - Java calls Thread.interrupted(), which resets and returns the - // initial "interrupted status". .NET has no such method. However, following the logic - // carefully below, we call Thread.CurrentThread.Interrupted() if interrupted is true. - // If the current thread is already in "interrupted status", there is no reason to call - // Thread.CurrentThread.Interrupted() since it is already in that state. - - // clean up merge scheduler in all cases, although flushing may have failed: - //interrupted = ThreadJob.Interrupted(); - - if (waitForMerges) - { - try - { - // Give merge scheduler last chance to run, in case - // any pending merges are waiting: - mergeScheduler.Merge(this, MergeTrigger.CLOSING, false); - } - catch (Util.ThreadInterruptedException) - { - // ignore any interruption, does not matter - interrupted = true; - if (infoStream.IsEnabled("IW")) - { - infoStream.Message("IW", "interrupted while waiting for final merges"); - } - } - } - - UninterruptableMonitor.Enter(this); - try - { - for (; ; ) - { - try - { - FinishMerges(waitForMerges && !interrupted); - break; - } - catch (Util.ThreadInterruptedException) - { - // by setting the interrupted status, the - // next call to finishMerges will pass false, - // so it will not wait - interrupted = true; - if (infoStream.IsEnabled("IW")) - { - infoStream.Message("IW", "interrupted while waiting for merges to finish"); - } - } - } - stopMerges = true; - } - finally - { - UninterruptableMonitor.Exit(this); - } - } - finally - { - // shutdown policy, scheduler and all threads (this call is not interruptible): - IOUtils.DisposeWhileHandlingException(mergePolicy, mergeScheduler); - } - } - - if (infoStream.IsEnabled("IW")) - { - infoStream.Message("IW", "now call final commit()"); - } - - if (doFlush) - { - CommitInternal(); - } - ProcessEvents(false, true); - UninterruptableMonitor.Enter(this); - try - { - // commitInternal calls ReaderPool.commit, which - // writes any pending liveDocs from ReaderPool, so - // it's safe to drop all readers now: - readerPool.DropAll(true); - deleter.Dispose(); - } - finally - { - UninterruptableMonitor.Exit(this); - } - - if (infoStream.IsEnabled("IW")) - { - infoStream.Message("IW", "at close: " + SegString()); - } - - if (writeLock != null) - { - writeLock.Dispose(); // release write lock - writeLock = null; - } - UninterruptableMonitor.Enter(this); - try - { - closed = true; - } - finally - { - UninterruptableMonitor.Exit(this); - } - if (Debugging.AssertsEnabled) - { - // LUCENENET specific - store the number of states so we don't have to call this method twice - int numDeactivatedThreadStates = docWriter.perThreadPool.NumDeactivatedThreadStates(); - Debugging.Assert(numDeactivatedThreadStates == docWriter.perThreadPool.MaxThreadStates, "{0} {1}", numDeactivatedThreadStates, docWriter.perThreadPool.MaxThreadStates); - } - } - catch (Exception oom) when (oom.IsOutOfMemoryError()) - { - HandleOOM(oom, "CloseInternal"); - } - finally - { - UninterruptableMonitor.Enter(this); - try - { - closing = false; - UninterruptableMonitor.PulseAll(this); - if (!closed) - { - if (infoStream.IsEnabled("IW")) - { - infoStream.Message("IW", "hit exception while closing"); - } - } - } - finally - { - UninterruptableMonitor.Exit(this); - } - // finally, restore interrupt status: - if (interrupted) - { - Thread.CurrentThread.Interrupt(); - } - } - } - /// /// Gets the used by this index. public virtual Directory Directory => directory; From 066dd2f9d5083cf9aed662d2cedaf4eb8298c079 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 28 May 2026 13:12:35 -0600 Subject: [PATCH 02/20] Port remaining LUCENE-5871 hunks and refresh close() docs, #1284 Follow-up to 7ec87903b. Audit against upstream 2cfcdcc found four hunks from the same commit that weren't ported and three doc/annotation changes on the close() overloads. This commit closes all seven gaps. Test/test-framework hunks (from 2cfcdcc): - TestIndexWriterCommit.TestPrepareCommitThenClose: new test, pins the Shutdown contract that close() with an outstanding prepareCommit throws IllegalStateException. - RandomIndexWriter.Dispose: add `IndexWriter.IsClosed == false` guard around the 1-in-8 random force-merge. Without it the new Shutdown contract can leave the writer closed when the test's user-visible call threw, and the force-merge in teardown then throws AlreadyClosedException. - TestIndexWriterExceptions: w.Commit() before w.Dispose() in the random- close branch so Shutdown doesn't throw IllegalStateException there. New test file (from LUCENE-5644 at the 4.8.1 release point, with the 2cfcdcc tweaks layered on TestManyThreadsClose): - TestIndexWriterThreadsToSegments: four tests covering segment-flush- per-thread invariants. Uses the 4.8.1-era APIs (NoMergePolicy.NO_COMPOUND_FILES, Lucene46 codec, 4-arg SegmentCommitInfo). IndexWriter doc + annotation changes (from 2cfcdcc): - Dispose(): replace the pre-fix doc text (which described the old "write lock may remain held / call close() again" recovery flow) with the new two-cases bullet list. - Dispose(bool): replace the OOME warning with a cross-reference to Dispose() for exception behavior; expand the merge-starvation note. - Dispose(bool): mark [Obsolete] mirroring upstream's @Deprecated. The message and intent are identical: prefer Commit() + Rollback() to abort merges and then close. LUCENE-5871 and LUCENE-5644 both first appear in 4.10.0 (post-4.8.1), but are in scope because the project tracks branch_4x at 4.8.1. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Index/RandomIndexWriter.cs | 8 +- .../Index/TestIndexWriterCommit.cs | 28 + .../Index/TestIndexWriterExceptions.cs | 6 + .../Index/TestIndexWriterThreadsToSegments.cs | 480 ++++++++++++++++++ src/Lucene.Net/Index/IndexWriter.cs | 92 ++-- 5 files changed, 561 insertions(+), 53 deletions(-) create mode 100644 src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs diff --git a/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs b/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs index 9a03d9c0c0..b6d7006ce9 100644 --- a/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs +++ b/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs @@ -449,7 +449,13 @@ protected virtual void Dispose(bool disposing) { // if someone isn't using getReader() API, we want to be sure to // forceMerge since presumably they might open a reader on the dir. - if (getReaderCalled == false && r.Next(8) == 2) + // LUCENENET: the `IndexWriter.IsClosed == false` guard is ported from upstream + // LUCENE-5871 (commit 2cfcdcc, first released in 4.10.0), pulled in alongside + // the LUCENE-5871 IndexWriter close fix (#1284). Without it the new Shutdown + // contract can leave the writer closed even when the test's user-visible call + // threw, and the random force-merge here would then throw + // AlreadyClosedException during teardown. + if (getReaderCalled == false && r.Next(8) == 2 && IndexWriter.IsClosed == false) { _DoRandomForceMerge(); } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterCommit.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterCommit.cs index 2009c8d4f6..235da95a79 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterCommit.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterCommit.cs @@ -776,5 +776,33 @@ private static void AddDocWithIndex(IndexWriter writer, int index) doc.Add(NewField("id", "" + index, storedTextType)); writer.AddDocument(doc); } + + // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released in + // 4.10.0), pulled in alongside the LUCENE-5871 IndexWriter close fix (#1284). The + // new Shutdown contract throws IllegalStateException when prepareCommit was called + // without a matching commit; this test pins that behavior. + [Test] + public virtual void TestPrepareCommitThenClose() + { + Directory dir = NewDirectory(); + IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); + w.AddDocument(new Document()); + w.PrepareCommit(); + try + { + w.Dispose(); + Assert.Fail("didn't hit exception"); + } + catch (Exception ise) when (ise.IsIllegalStateException()) + { + // expected + } + w.Commit(); + w.Dispose(); + DirectoryReader r = DirectoryReader.Open(dir); + Assert.AreEqual(1, r.MaxDoc); + r.Dispose(); + dir.Dispose(); + } } } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs index be76f3d7eb..74fd3eeb8d 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs @@ -2294,6 +2294,12 @@ public virtual void TestNoLostDeletesOrUpdates() Console.WriteLine(" now close writer"); } doClose = true; + // LUCENENET: w.Commit() before w.Dispose() ported from upstream + // LUCENE-5871 (commit 2cfcdcc, first released in 4.10.0), pulled in + // alongside the LUCENE-5871 IndexWriter close fix (#1284). The new + // Shutdown contract throws IllegalStateException if a prepareCommit + // is outstanding, so we commit first. + w.Commit(); w.Dispose(); w = null; } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs new file mode 100644 index 0000000000..0666fd7b50 --- /dev/null +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -0,0 +1,480 @@ +using J2N.Threading; +using J2N.Threading.Atomic; +using Lucene.Net.Analysis; +using Lucene.Net.Codecs; +using Lucene.Net.Codecs.Lucene46; +using Lucene.Net.Documents; +using Lucene.Net.Index.Extensions; +using Lucene.Net.Store; +using Lucene.Net.Util; +using NUnit.Framework; +using RandomizedTesting.Generators; +using System; +using System.Collections.Generic; +using System.Threading; +using Assert = Lucene.Net.TestFramework.Assert; + +namespace Lucene.Net.Index +{ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + [TestFixture] + public class TestIndexWriterThreadsToSegments : LuceneTestCase + { + // LUCENE-5644: for first segment, two threads each indexed one doc (likely concurrently), but for second segment, each thread indexed the + // doc NOT at the same time, and should have shared the same thread state / segment + [Test] + public virtual void TestSegmentCountOnFlushBasic() + { + Directory dir = NewDirectory(); + IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); + CountdownEvent startingGun = new CountdownEvent(1); + CountdownEvent startDone = new CountdownEvent(2); + CountdownEvent middleGun = new CountdownEvent(1); + CountdownEvent finalGun = new CountdownEvent(1); + ThreadJob[] threads = new ThreadJob[2]; + for (int i = 0; i < threads.Length; i++) + { + int threadID = i; + threads[i] = new SegmentCountOnFlushBasicThread(w, threadID, startingGun, startDone, middleGun, finalGun); + threads[i].Start(); + } + + startingGun.Signal(); + startDone.Wait(); + + IndexReader r = DirectoryReader.Open(w, true); + Assert.AreEqual(2, r.NumDocs); + int numSegments = r.Leaves.Count; + // 1 segment if the threads ran sequentially, else 2: + Assert.IsTrue(numSegments <= 2); + r.Dispose(); + + middleGun.Signal(); + threads[0].Join(); + + finalGun.Signal(); + threads[1].Join(); + + r = DirectoryReader.Open(w, true); + Assert.AreEqual(4, r.NumDocs); + // Both threads should have shared a single thread state since they did not try to index concurrently: + Assert.AreEqual(1 + numSegments, r.Leaves.Count); + r.Dispose(); + + w.Dispose(); + dir.Dispose(); + } + + private sealed class SegmentCountOnFlushBasicThread : ThreadJob + { + private readonly IndexWriter w; + private readonly int threadID; + private readonly CountdownEvent startingGun; + private readonly CountdownEvent startDone; + private readonly CountdownEvent middleGun; + private readonly CountdownEvent finalGun; + + public SegmentCountOnFlushBasicThread(IndexWriter w, int threadID, CountdownEvent startingGun, CountdownEvent startDone, CountdownEvent middleGun, CountdownEvent finalGun) + { + this.w = w; + this.threadID = threadID; + this.startingGun = startingGun; + this.startDone = startDone; + this.middleGun = middleGun; + this.finalGun = finalGun; + } + + public override void Run() + { + try + { + startingGun.Wait(); + Document doc = new Document(); + doc.Add(NewTextField("field", "here is some text", Field.Store.NO)); + w.AddDocument(doc); + startDone.Signal(); + + middleGun.Wait(); + if (threadID == 0) + { + w.AddDocument(doc); + } + else + { + finalGun.Wait(); + w.AddDocument(doc); + } + } + catch (Exception e) when (e.IsException()) + { + throw RuntimeException.Create(e); + } + } + } + + /// + /// Maximum number of simultaneous threads to use for each iteration. + /// + private const int MAX_THREADS_AT_ONCE = 10; + + private sealed class CheckSegmentCount : IDisposable + { + private readonly IndexWriter w; + private readonly AtomicInt32 maxThreadCountPerIter; + private readonly AtomicInt32 indexingCount; + private DirectoryReader r; + + public CheckSegmentCount(IndexWriter w, AtomicInt32 maxThreadCountPerIter, AtomicInt32 indexingCount) + { + this.w = w; + this.maxThreadCountPerIter = maxThreadCountPerIter; + this.indexingCount = indexingCount; + r = DirectoryReader.Open(w, true); + Assert.AreEqual(0, r.Leaves.Count); + SetNextIterThreadCount(); + } + + public void Run() + { + try + { + int oldSegmentCount = r.Leaves.Count; + DirectoryReader r2 = DirectoryReader.OpenIfChanged(r); + Assert.IsNotNull(r2); + r.Dispose(); + r = r2; + int maxThreadStates = w.Config.MaxThreadStates; + int maxExpectedSegments = oldSegmentCount + Math.Min(maxThreadStates, maxThreadCountPerIter.Value); + if (Verbose) + { + Console.WriteLine("TEST: iter done; now verify oldSegCount=" + oldSegmentCount + " newSegCount=" + r2.Leaves.Count + " maxExpected=" + maxExpectedSegments); + } + // NOTE: it won't necessarily be ==, in case some threads were strangely scheduled and never conflicted with one another (should be uncommon...?): + Assert.IsTrue(r.Leaves.Count <= maxExpectedSegments); + SetNextIterThreadCount(); + } + catch (Exception e) when (e.IsException()) + { + throw RuntimeException.Create(e); + } + } + + private void SetNextIterThreadCount() + { + indexingCount.Value = 0; + maxThreadCountPerIter.Value = TestUtil.NextInt32(Random, 1, MAX_THREADS_AT_ONCE); + if (Verbose) + { + Console.WriteLine("TEST: iter set maxThreadCount=" + maxThreadCountPerIter); + } + } + + public void Dispose() + { + r.Dispose(); + r = null; + } + } + + // LUCENE-5644: index docs w/ multiple threads but in between flushes we limit how many threads can index concurrently in the next + // iteration, and then verify that no more segments were flushed than number of threads: + [Test] + public virtual void TestSegmentCountOnFlushRandom() + { + Directory dir = NewFSDirectory(CreateTempDir()); + IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)); + + int maxThreadStates = TestUtil.NextInt32(Random, 1, 12); + + if (Verbose) + { + Console.WriteLine("TEST: maxThreadStates=" + maxThreadStates); + } + + // Never trigger flushes (so we only flush on getReader): + iwc.SetMaxBufferedDocs(100000000); + iwc.SetRAMBufferSizeMB(-1); + iwc.MaxThreadStates = maxThreadStates; + + // Never trigger merges (so we can simplistically count flushed segments): + iwc.SetMergePolicy(NoMergePolicy.NO_COMPOUND_FILES); + + IndexWriter w = new IndexWriter(dir, iwc); + + // How many threads are indexing in the current cycle: + AtomicInt32 indexingCount = new AtomicInt32(); + + // How many threads we will use on each cycle: + AtomicInt32 maxThreadCount = new AtomicInt32(); + + CheckSegmentCount checker = new CheckSegmentCount(w, maxThreadCount, indexingCount); + + // We spin up 10 threads up front, but then in between flushes we limit how many can run on each iteration + const int ITERS = 100; + ThreadJob[] threads = new ThreadJob[MAX_THREADS_AT_ONCE]; + + // We use this to stop all threads once they've indexed their docs in the current iter, and pull a new NRT reader, and verify the + // segment count: + Barrier barrier = new Barrier(MAX_THREADS_AT_ONCE, _ => checker.Run()); + + for (int i = 0; i < threads.Length; i++) + { + threads[i] = new SegmentCountOnFlushRandomThread(w, indexingCount, maxThreadCount, barrier, ITERS); + threads[i].Start(); + } + + foreach (ThreadJob t in threads) + { + t.Join(); + } + + IOUtils.Dispose(checker, w, dir); + } + + private sealed class SegmentCountOnFlushRandomThread : ThreadJob + { + private readonly IndexWriter w; + private readonly AtomicInt32 indexingCount; + private readonly AtomicInt32 maxThreadCount; + private readonly Barrier barrier; + private readonly int iters; + + public SegmentCountOnFlushRandomThread(IndexWriter w, AtomicInt32 indexingCount, AtomicInt32 maxThreadCount, Barrier barrier, int iters) + { + this.w = w; + this.indexingCount = indexingCount; + this.maxThreadCount = maxThreadCount; + this.barrier = barrier; + this.iters = iters; + } + + public override void Run() + { + try + { + for (int iter = 0; iter < iters; iter++) + { + if (indexingCount.IncrementAndGet() <= maxThreadCount.Value) + { + if (Verbose) + { + Console.WriteLine("TEST: " + Thread.CurrentThread.Name + ": do index"); + } + + // We get to index on this cycle: + Document doc = new Document(); + doc.Add(new TextField("field", "here is some text that is a bit longer than normal trivial text", Field.Store.NO)); + for (int j = 0; j < 200; j++) + { + w.AddDocument(doc); + } + } + else + { + // We lose: no indexing for us on this cycle + if (Verbose) + { + Console.WriteLine("TEST: " + Thread.CurrentThread.Name + ": don't index"); + } + } + barrier.SignalAndWait(); + } + } + catch (Exception e) when (e.IsException()) + { + throw RuntimeException.Create(e); + } + } + } + + [Test] + public virtual void TestManyThreadsClose() + { + Directory dir = NewDirectory(); + RandomIndexWriter w = new RandomIndexWriter(Random, dir); + w.DoRandomForceMerge = false; + ThreadJob[] threads = new ThreadJob[TestUtil.NextInt32(Random, 4, 30)]; + CountdownEvent startingGun = new CountdownEvent(1); + for (int i = 0; i < threads.Length; i++) + { + threads[i] = new ManyThreadsCloseThread(w, startingGun); + threads[i].Start(); + } + + startingGun.Signal(); + + Thread.Sleep(100); + // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released in + // 4.10.0), pulled in alongside the LUCENE-5871 IndexWriter close fix (#1284). + // The new Shutdown contract can now throw IllegalStateException if a concurrent + // thread called prepareCommit; the close is still required to retry afterwards. + try + { + w.Dispose(); + } + catch (Exception ise) when (ise.IsIllegalStateException()) + { + // OK but not required + } + foreach (ThreadJob t in threads) + { + t.Join(); + } + w.Dispose(); + dir.Dispose(); + } + + private sealed class ManyThreadsCloseThread : ThreadJob + { + private readonly RandomIndexWriter w; + private readonly CountdownEvent startingGun; + + public ManyThreadsCloseThread(RandomIndexWriter w, CountdownEvent startingGun) + { + this.w = w; + this.startingGun = startingGun; + } + + public override void Run() + { + try + { + startingGun.Wait(); + Document doc = new Document(); + doc.Add(new TextField("field", "here is some text that is a bit longer than normal trivial text", Field.Store.NO)); + // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released + // in 4.10.0), pulled in alongside the LUCENE-5871 IndexWriter close fix + // (#1284). Bounded loop instead of while(true) so threads exit even if + // AlreadyClosedException never fires. + for (int i = 0; i < 10000; i++) + { + w.AddDocument(doc); + } + } + catch (Exception ace) when (ace.IsAlreadyClosedException()) + { + // ok + } + catch (Exception e) when (e.IsException()) + { + throw RuntimeException.Create(e); + } + } + } + + [Test] + public virtual void TestDocsStuckInRAMForever() + { + Directory dir = NewDirectory(); + IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)); + iwc.SetRAMBufferSizeMB(.2); + Codec codec = Codec.ForName("Lucene46"); + iwc.SetCodec(codec); + iwc.SetMergePolicy(NoMergePolicy.NO_COMPOUND_FILES); + IndexWriter w = new IndexWriter(dir, iwc); + CountdownEvent startingGun = new CountdownEvent(1); + ThreadJob[] threads = new ThreadJob[2]; + for (int i = 0; i < threads.Length; i++) + { + int threadID = i; + threads[i] = new DocsStuckInRAMForeverThread(w, threadID, startingGun); + threads[i].Start(); + } + + startingGun.Signal(); + foreach (ThreadJob t in threads) + { + t.Join(); + } + + ISet segSeen = new HashSet(); + int thread0Count = 0; + int thread1Count = 0; + + // At this point the writer should have 2 thread states w/ docs; now we index with only 1 thread until we see all 1000 thread0 & thread1 + // docs flushed. If the writer incorrectly holds onto previously indexed docs forever then this will run forever: + while (thread0Count < 1000 || thread1Count < 1000) + { + Document doc = new Document(); + doc.Add(NewStringField("field", "threadIDmain", Field.Store.NO)); + w.AddDocument(doc); + + foreach (string fileName in dir.ListAll()) + { + if (fileName.EndsWith(".si", StringComparison.Ordinal)) + { + string segName = IndexFileNames.ParseSegmentName(fileName); + if (segSeen.Contains(segName) == false) + { + segSeen.Add(segName); + SegmentInfo si = new Lucene46SegmentInfoFormat().SegmentInfoReader.Read(dir, segName, IOContext.DEFAULT); + si.Codec = codec; + SegmentCommitInfo sci = new SegmentCommitInfo(si, 0, -1, -1); + SegmentReader sr = new SegmentReader(sci, 1, IOContext.DEFAULT); + try + { + thread0Count += sr.DocFreq(new Term("field", "threadID0")); + thread1Count += sr.DocFreq(new Term("field", "threadID1")); + } + finally + { + sr.Dispose(); + } + } + } + } + } + + w.Dispose(); + dir.Dispose(); + } + + private sealed class DocsStuckInRAMForeverThread : ThreadJob + { + private readonly IndexWriter w; + private readonly int threadID; + private readonly CountdownEvent startingGun; + + public DocsStuckInRAMForeverThread(IndexWriter w, int threadID, CountdownEvent startingGun) + { + this.w = w; + this.threadID = threadID; + this.startingGun = startingGun; + } + + public override void Run() + { + try + { + startingGun.Wait(); + for (int j = 0; j < 1000; j++) + { + Document doc = new Document(); + doc.Add(NewStringField("field", "threadID" + threadID, Field.Store.NO)); + w.AddDocument(doc); + } + } + catch (Exception e) when (e.IsException()) + { + throw RuntimeException.Create(e); + } + } + } + } +} diff --git a/src/Lucene.Net/Index/IndexWriter.cs b/src/Lucene.Net/Index/IndexWriter.cs index 1598d03b06..ea78fe9457 100644 --- a/src/Lucene.Net/Index/IndexWriter.cs +++ b/src/Lucene.Net/Index/IndexWriter.cs @@ -1042,55 +1042,32 @@ private void MessageState() /// /// Commits all changes to an index, waits for pending merges - /// to complete, and closes all associated files. - /// - /// This is a "slow graceful shutdown" which may take a long time - /// especially if a big merge is pending: If you only want to close - /// resources use . If you only want to commit - /// pending changes and close resources see . - /// + /// to complete, closes all associated files and releases the + /// write lock. + /// + /// Note that: + /// + /// If you called but failed to call , this + /// method will throw and the + /// will not be closed. + /// If this method throws any other exception, the + /// will be closed, but changes may have been lost. + /// + /// + /// + /// /// Note that this may be a costly /// operation, so, try to re-use a single writer instead of /// closing and opening a new one. See for - /// caveats about write caching done by some IO devices. - /// - /// If an is hit during close, eg due to disk - /// full or some other reason, then both the on-disk index - /// and the internal state of the instance will - /// be consistent. However, the close will not be complete - /// even though part of it (flushing buffered documents) - /// may have succeeded, so the write lock will still be - /// held. - /// - /// If you can correct the underlying cause (eg free up - /// some disk space) then you can call again. - /// Failing that, if you want to force the write lock to be - /// released (dangerous, because you may then lose buffered - /// docs in the instance) then you can do - /// something like this: - /// - /// - /// try - /// { - /// writer.Dispose(); - /// } - /// finally - /// { - /// if (IndexWriter.IsLocked(directory)) - /// { - /// IndexWriter.Unlock(directory); - /// } - /// } - /// - /// - /// after which, you must be certain not to use the writer - /// instance anymore. + /// caveats about write caching done by some IO devices. /// - /// NOTE: if this method hits an - /// you should immediately dispose the writer, again. See - /// for details. + /// NOTE: You must ensure no other threads are still making + /// changes at the same time that this method is invoked. /// /// if there is a low-level IO error + // LUCENENET: doc-comment updated to match upstream LUCENE-5871 (commit 2cfcdcc, first + // released in 4.10.0); the previous text described the pre-fix behavior where the + // write lock could remain held on exception, which no longer applies after #1284. [MethodImpl(MethodImplOptions.NoInlining)] // Stack trace needed intact in TestConcurrentMergeScheduler and TestIndexWriterWithThreads public void Dispose() { @@ -1102,18 +1079,17 @@ public void Dispose() /// Disposes the index with or without waiting for currently /// running merges to finish. This is only meaningful when /// using a that runs merges in background - /// threads. - /// - /// NOTE: If this method hits an - /// you should immediately dispose the writer, again. See - /// for details. + /// threads. See for details on behavior + /// when exceptions are thrown. /// /// NOTE: It is dangerous to always call /// Dispose(false), especially when is not open /// for very long, because this can result in "merge /// starvation" whereby long merges will never have a /// chance to finish. This will cause too many segments in - /// your index over time. + /// your index over time, which leads to all sorts of + /// problems like slow searches, too much RAM and too + /// many file descriptors used by readers, etc. /// /// NOTE: This overload should not be called when implementing a finalizer. /// Instead, call with disposing set to @@ -1124,6 +1100,18 @@ public void Dispose() /// running merges to abort, wait until those merges have /// finished (which should be at most a few seconds), and /// then return. + // LUCENENET: doc-comment updated and [Obsolete] applied to match upstream LUCENE-5871 + // (commit 2cfcdcc, first released in 4.10.0). Upstream marked this overload @Deprecated + // because the new Shutdown contract makes "close without waiting for merges" easy to + // misuse; prefer Commit() followed by Rollback() to abort merges and then close. + // + // The deprecation generates CS0618 warnings on existing in-tree callers. This mirrors + // upstream's posture exactly: branch_4x kept javac [deprecation] warnings at the + // method's call sites and did not migrate them when 2cfcdcc landed. The intent was + // always to remove the overload entirely on the next major release: trunk did exactly + // that in LUCENE-4246 (commit 8559eaf, Lucene 5.0), deleting close(boolean) and + // migrating all the trunk call sites in the same commit. + [Obsolete("To abort merges and then close, call Commit() and then Rollback() instead.")] [MethodImpl(MethodImplOptions.NoInlining)] // Stack trace needed intact in TestConcurrentMergeScheduler and TestIndexWriterWithThreads [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] [SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "This is Lucene's alternate path to Dispose() and we must suppress the finalizer here.")] @@ -1195,9 +1183,9 @@ protected virtual void Dispose(bool disposing, bool waitForMerges) /// Gracefully closes (commits, waits for merges), but calls rollback /// if there's an exc so the IndexWriter is always closed. /// - // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc) to fix #1284 — - // previously the close path's straight-line cleanup leaked the reader pool, - // deleter, and write.lock when CommitInternal threw an I/O exception. + // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released in + // 4.10.0) to fix #1284. Previously the close path's straight-line cleanup leaked + // the reader pool, deleter, and write.lock when CommitInternal threw an I/O exception. private void Shutdown(bool waitForMerges) { if (pendingCommit != null) From 187fbd3300328e78ffde6a5a755ad3690ca32c4a Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 28 May 2026 13:39:42 -0600 Subject: [PATCH 03/20] Rename extracted classes to follow ThreadAnonymousClass convention, #1284 Follow-up to f4049813f. Renames the four extracted Java anonymous Thread classes in TestIndexWriterThreadsToSegments to match the Lucene.NET convention of AnonymousClass with a method-specific suffix when needed: - SegmentCountOnFlushBasicThread -> ThreadAnonymousClassForSegmentCountOnFlushBasic - SegmentCountOnFlushRandomThread -> ThreadAnonymousClassForSegmentCountOnFlushRandom - ManyThreadsCloseThread -> ThreadAnonymousClassForManyThreadsClose - DocsStuckInRAMForeverThread -> ThreadAnonymousClassForDocsStuckInRAMForever CheckSegmentCount is left as-is because it is a named static class in upstream Java, not an anonymous inner class. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Index/TestIndexWriterThreadsToSegments.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs index 0666fd7b50..aa7ef67e60 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -51,7 +51,7 @@ public virtual void TestSegmentCountOnFlushBasic() for (int i = 0; i < threads.Length; i++) { int threadID = i; - threads[i] = new SegmentCountOnFlushBasicThread(w, threadID, startingGun, startDone, middleGun, finalGun); + threads[i] = new ThreadAnonymousClassForSegmentCountOnFlushBasic(w, threadID, startingGun, startDone, middleGun, finalGun); threads[i].Start(); } @@ -81,7 +81,7 @@ public virtual void TestSegmentCountOnFlushBasic() dir.Dispose(); } - private sealed class SegmentCountOnFlushBasicThread : ThreadJob + private sealed class ThreadAnonymousClassForSegmentCountOnFlushBasic : ThreadJob { private readonly IndexWriter w; private readonly int threadID; @@ -90,7 +90,7 @@ private sealed class SegmentCountOnFlushBasicThread : ThreadJob private readonly CountdownEvent middleGun; private readonly CountdownEvent finalGun; - public SegmentCountOnFlushBasicThread(IndexWriter w, int threadID, CountdownEvent startingGun, CountdownEvent startDone, CountdownEvent middleGun, CountdownEvent finalGun) + public ThreadAnonymousClassForSegmentCountOnFlushBasic(IndexWriter w, int threadID, CountdownEvent startingGun, CountdownEvent startDone, CountdownEvent middleGun, CountdownEvent finalGun) { this.w = w; this.threadID = threadID; @@ -235,7 +235,7 @@ public virtual void TestSegmentCountOnFlushRandom() for (int i = 0; i < threads.Length; i++) { - threads[i] = new SegmentCountOnFlushRandomThread(w, indexingCount, maxThreadCount, barrier, ITERS); + threads[i] = new ThreadAnonymousClassForSegmentCountOnFlushRandom(w, indexingCount, maxThreadCount, barrier, ITERS); threads[i].Start(); } @@ -247,7 +247,7 @@ public virtual void TestSegmentCountOnFlushRandom() IOUtils.Dispose(checker, w, dir); } - private sealed class SegmentCountOnFlushRandomThread : ThreadJob + private sealed class ThreadAnonymousClassForSegmentCountOnFlushRandom : ThreadJob { private readonly IndexWriter w; private readonly AtomicInt32 indexingCount; @@ -255,7 +255,7 @@ private sealed class SegmentCountOnFlushRandomThread : ThreadJob private readonly Barrier barrier; private readonly int iters; - public SegmentCountOnFlushRandomThread(IndexWriter w, AtomicInt32 indexingCount, AtomicInt32 maxThreadCount, Barrier barrier, int iters) + public ThreadAnonymousClassForSegmentCountOnFlushRandom(IndexWriter w, AtomicInt32 indexingCount, AtomicInt32 maxThreadCount, Barrier barrier, int iters) { this.w = w; this.indexingCount = indexingCount; @@ -313,7 +313,7 @@ public virtual void TestManyThreadsClose() CountdownEvent startingGun = new CountdownEvent(1); for (int i = 0; i < threads.Length; i++) { - threads[i] = new ManyThreadsCloseThread(w, startingGun); + threads[i] = new ThreadAnonymousClassForManyThreadsClose(w, startingGun); threads[i].Start(); } @@ -340,12 +340,12 @@ public virtual void TestManyThreadsClose() dir.Dispose(); } - private sealed class ManyThreadsCloseThread : ThreadJob + private sealed class ThreadAnonymousClassForManyThreadsClose : ThreadJob { private readonly RandomIndexWriter w; private readonly CountdownEvent startingGun; - public ManyThreadsCloseThread(RandomIndexWriter w, CountdownEvent startingGun) + public ThreadAnonymousClassForManyThreadsClose(RandomIndexWriter w, CountdownEvent startingGun) { this.w = w; this.startingGun = startingGun; @@ -393,7 +393,7 @@ public virtual void TestDocsStuckInRAMForever() for (int i = 0; i < threads.Length; i++) { int threadID = i; - threads[i] = new DocsStuckInRAMForeverThread(w, threadID, startingGun); + threads[i] = new ThreadAnonymousClassForDocsStuckInRAMForever(w, threadID, startingGun); threads[i].Start(); } @@ -445,13 +445,13 @@ public virtual void TestDocsStuckInRAMForever() dir.Dispose(); } - private sealed class DocsStuckInRAMForeverThread : ThreadJob + private sealed class ThreadAnonymousClassForDocsStuckInRAMForever : ThreadJob { private readonly IndexWriter w; private readonly int threadID; private readonly CountdownEvent startingGun; - public DocsStuckInRAMForeverThread(IndexWriter w, int threadID, CountdownEvent startingGun) + public ThreadAnonymousClassForDocsStuckInRAMForever(IndexWriter w, int threadID, CountdownEvent startingGun) { this.w = w; this.threadID = threadID; From c6c9027b37f9c9c6da3cdd1b872658d0a21eddff Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 28 May 2026 13:51:04 -0600 Subject: [PATCH 04/20] Add breadcrumbs for assertEventQueueAfterClose and closeInternal removals, #1284 Upstream 2cfcdcc rewrote close(boolean) to delegate to shutdown(boolean) but kept assertEventQueueAfterClose() and closeInternal(boolean, boolean) as private dead code. The original #1284 port (7ec87903b) dropped both, which matches trunk LUCENE-4246's eventual cleanup (8559eaf, Lucene 5.0) but diverges from the conservative branch_4x state at 2cfcdcc. Adds two LUCENENET breadcrumbs around ShouldClose() so a future porter diffing against 2cfcdcc can find the removal rationale at the position where each method used to live: - Full note before ShouldClose() covering both, since assertEventQueueAfterClose() sat there in upstream. - One-liner after ShouldClose() pointing back to the full note, since closeInternal() sat immediately after. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Lucene.Net/Index/IndexWriter.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Lucene.Net/Index/IndexWriter.cs b/src/Lucene.Net/Index/IndexWriter.cs index ea78fe9457..3ea8474ba9 100644 --- a/src/Lucene.Net/Index/IndexWriter.cs +++ b/src/Lucene.Net/Index/IndexWriter.cs @@ -1227,6 +1227,14 @@ private void Shutdown(bool waitForMerges) } } + // LUCENENET: upstream 2cfcdcc had private assertEventQueueAfterClose() between + // close(boolean) and shouldClose() (and closeInternal(boolean, boolean) immediately + // after shouldClose()). The LUCENE-5871 rewrite of close(boolean) stranded both as + // dead code: shutdown(boolean) replaced their only callers, and neither is referenced + // anywhere else in the file at 2cfcdcc. Trunk LUCENE-4246 (commit 8559eaf, Lucene 5.0) + // removed both alongside the rest of the close(boolean) cleanup; we follow that 5.0 + // end state here rather than carrying the dead branch_4x leftovers. + /// /// Returns true if this thread should attempt to close, or /// false if IndexWriter is now closed; else, waits until @@ -1266,6 +1274,9 @@ private bool ShouldClose() } } + // LUCENENET: upstream 2cfcdcc had private closeInternal(boolean waitForMerges, + // boolean doFlush) here. See the note above ShouldClose() for why it is dropped. + /// /// Gets the used by this index. public virtual Directory Directory => directory; From e983174303870d2b0ae2f6a2095751214239f666 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 28 May 2026 13:56:26 -0600 Subject: [PATCH 05/20] Rename remaining extracted classes to follow AnonymousClass convention, #1284 Follow-up to 08d82e82a. The previous rename pass only covered the four classes in TestIndexWriterThreadsToSegments and missed three classes that the original #1284 port extracted from anonymous inner classes in TestIndexWriter: - RollbackWhileMergeInfoStream -> InfoStreamAnonymousClassForRollbackWhileMergeIsRunning - RollbackWhileMergeScheduler -> ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning - SlowCommittingInfoStream -> InfoStreamAnonymousClassForCloseDuringCommit The ForCloseDuringCommit suffix matches the existing ThreadAnonymousClassForCloseDuringCommit precedent. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 37ab04e5d6..533ce9777a 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -3027,9 +3027,9 @@ public virtual void TestRollbackWhileMergeIsRunning() LogDocMergePolicy mp = new LogDocMergePolicy(); mp.MergeFactor = 2; iwc.SetMergePolicy(mp); - iwc.SetInfoStream(new RollbackWhileMergeInfoStream(closeStarted)); + iwc.SetInfoStream(new InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(closeStarted)); - iwc.SetMergeScheduler(new RollbackWhileMergeScheduler(mergeStarted, closeStarted)); + iwc.SetMergeScheduler(new ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(mergeStarted, closeStarted)); IndexWriter w = new IndexWriter(dir, iwc); w.AddDocument(new Document()); w.Commit(); @@ -3039,11 +3039,11 @@ public virtual void TestRollbackWhileMergeIsRunning() dir.Dispose(); } - private sealed class RollbackWhileMergeInfoStream : InfoStream + private sealed class InfoStreamAnonymousClassForRollbackWhileMergeIsRunning : InfoStream { private readonly CountdownEvent closeStarted; - public RollbackWhileMergeInfoStream(CountdownEvent closeStarted) + public InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(CountdownEvent closeStarted) { this.closeStarted = closeStarted; } @@ -3066,12 +3066,12 @@ protected override void Dispose(bool disposing) } } - private sealed class RollbackWhileMergeScheduler : ConcurrentMergeScheduler + private sealed class ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning : ConcurrentMergeScheduler { private readonly CountdownEvent mergeStarted; private readonly CountdownEvent closeStarted; - public RollbackWhileMergeScheduler(CountdownEvent mergeStarted, CountdownEvent closeStarted) + public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(CountdownEvent mergeStarted, CountdownEvent closeStarted) { this.mergeStarted = mergeStarted; this.closeStarted = closeStarted; @@ -3153,7 +3153,7 @@ public virtual void TestCloseDuringCommit() Directory dir = NewDirectory(); IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, null); // infostream that "takes a long time" to commit - iwc.SetInfoStream(new SlowCommittingInfoStream(startCommit)); + iwc.SetInfoStream(new InfoStreamAnonymousClassForCloseDuringCommit(startCommit)); IndexWriter iw = new IndexWriter(dir, iwc); Document doc = new Document(); new ThreadAnonymousClassForCloseDuringCommit(iw, finishCommit).Start(); @@ -3171,11 +3171,11 @@ public virtual void TestCloseDuringCommit() dir.Dispose(); } - private sealed class SlowCommittingInfoStream : InfoStream + private sealed class InfoStreamAnonymousClassForCloseDuringCommit : InfoStream { private readonly CountdownEvent startCommit; - public SlowCommittingInfoStream(CountdownEvent startCommit) + public InfoStreamAnonymousClassForCloseDuringCommit(CountdownEvent startCommit) { this.startCommit = startCommit; } From 31844c5278d00cddb04fb0cdb0ab15267e00dae0 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 28 May 2026 15:50:21 -0600 Subject: [PATCH 06/20] Add Java-style CountDownLatch port and tests, #1284 Adds a Lucene.Net.Support.Threading.CountDownLatch class as a 1:1 port of java.util.concurrent.CountDownLatch (adapted from Apache Harmony), along with a JSR166-style CountDownLatchTest port. Why this exists alongside System.Threading.CountdownEvent: - CountdownEvent.Signal() throws InvalidOperationException if called after the count reaches zero. Java's CountDownLatch.countDown() is idempotent at zero. Test code ported from upstream Lucene Java routinely calls countDown() past zero (e.g. one thread per loop iteration when only the first N matter); the C# port has to either wrap each call in a CurrentCount > 0 guard or eat sporadic exceptions during teardown. - This bug was hit by TestConcurrentMergeScheduler.TestMaxMergeCount on CI after the LUCENE-5871 / #1284 port made Shutdown(false) forward-flush merges more aggressively, pushing more merges through DoMerge than maxMergeCount and tripping the Signal() overshoot. The new CountDownLatch wraps a ManualResetEventSlim + Interlocked int. CountDown is safe past zero (early return + idempotent Set). Count clamps at zero to match Java's getCount() semantics. Await(TimeSpan) returns a bool matching await(long, TimeUnit). Thread interrupt is NOT honored (consistent with the rest of Lucene.NET's threading; see the ReentrantLockTest precedent), and the two interrupt-dependent tests in CountDownLatchTest are [Ignore]'d with a LUCENENET note. CountDownLatchTest extends the existing JSR166TestCase. Anonymous Java inner classes are extracted as ThreadAnonymousClassFor per the established naming convention. 7 of 9 upstream tests pass; 2 are skipped per the interrupt-semantics note above. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Support/Threading/CountDownLatchTest.cs | 344 ++++++++++++++++++ .../Support/Threading/CountDownLatch.cs | 205 +++++++++++ 2 files changed, 549 insertions(+) create mode 100644 src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs create mode 100644 src/Lucene.Net/Support/Threading/CountDownLatch.cs diff --git a/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs b/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs new file mode 100644 index 0000000000..eae7309b9b --- /dev/null +++ b/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs @@ -0,0 +1,344 @@ +// Some code adapted from Apache Harmony: https://github.com/apache/harmony/blob/02970cb7227a335edd2c8457ebdde0195a735733/classlib/modules/concurrent/src/test/java/CountDownLatchTest.java + +#region Copyright 2010 by Apache Harmony, Licensed under the Apache License, Version 2.0 +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +using J2N.Threading; +using Lucene.Net.Support.Threading; +using NUnit.Framework; +using System; +using System.Threading; +using Assert = Lucene.Net.TestFramework.Assert; + +namespace Lucene.Net.Support.Threading +{ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + [TestFixture] + public class CountDownLatchTest : JSR166TestCase + { + //public static void main(String[] args) + //{ + // junit.textui.TestRunner.run(suite()); + //} + //public static Test suite() + //{ + // return new TestSuite(CountDownLatchTest.class); + //} + + /** + * negative constructor argument throws IAE + */ + [Test] + public void TestConstructor() + { + try + { + _ = new CountDownLatch(-1); + shouldThrow(); + } + catch (ArgumentOutOfRangeException) + { + // success + } + } + + /** + * getCount returns initial count and decreases after countDown + */ + [Test] + public void TestGetCount() + { + CountDownLatch l = new CountDownLatch(2); + assertEquals(2, l.Count); + l.CountDown(); + assertEquals(1, l.Count); + } + + /** + * countDown decrements count when positive and has no effect when zero + */ + [Test] + public void TestCountDown() + { + CountDownLatch l = new CountDownLatch(1); + assertEquals(1, l.Count); + l.CountDown(); + assertEquals(0, l.Count); + l.CountDown(); + assertEquals(0, l.Count); + } + + /** + * await returns after countDown to zero, but not before + */ + [Test] + public void TestAwait() + { + CountDownLatch l = new CountDownLatch(2); + + ThreadJob t = new ThreadAnonymousClassForTestAwait(this, l); + t.Start(); + try + { + assertEquals(l.Count, 2); + Thread.Sleep(SHORT_DELAY_MS); + l.CountDown(); + assertEquals(l.Count, 1); + l.CountDown(); + assertEquals(l.Count, 0); + t.Join(); + } + catch (Exception e) when (e.IsInterruptedException()) + { + unexpectedException(); + } + } + + private sealed class ThreadAnonymousClassForTestAwait : ThreadJob + { + private readonly CountDownLatchTest outerInstance; + private readonly CountDownLatch l; + + public ThreadAnonymousClassForTestAwait(CountDownLatchTest outerInstance, CountDownLatch l) + { + this.outerInstance = outerInstance; + this.l = l; + } + + public override void Run() + { + outerInstance.threadAssertTrue(l.Count > 0); + l.Await(); + outerInstance.threadAssertTrue(l.Count == 0); + } + } + + /** + * timed await returns after countDown to zero + */ + [Test] + public void TestTimedAwait() + { + CountDownLatch l = new CountDownLatch(2); + + ThreadJob t = new ThreadAnonymousClassForTestTimedAwait(this, l); + t.Start(); + try + { + assertEquals(l.Count, 2); + Thread.Sleep(SHORT_DELAY_MS); + l.CountDown(); + assertEquals(l.Count, 1); + l.CountDown(); + assertEquals(l.Count, 0); + t.Join(); + } + catch (Exception e) when (e.IsInterruptedException()) + { + unexpectedException(); + } + } + + private sealed class ThreadAnonymousClassForTestTimedAwait : ThreadJob + { + private readonly CountDownLatchTest outerInstance; + private readonly CountDownLatch l; + + public ThreadAnonymousClassForTestTimedAwait(CountDownLatchTest outerInstance, CountDownLatch l) + { + this.outerInstance = outerInstance; + this.l = l; + } + + public override void Run() + { + outerInstance.threadAssertTrue(l.Count > 0); + outerInstance.threadAssertTrue(l.Await(TimeSpan.FromMilliseconds(SMALL_DELAY_MS))); + } + } + + /** + * await throws IE if interrupted before counted down + */ + [Test] + [Ignore("LUCENENET: CountDownLatch.Await() does not honor Thread.Interrupt() because Lucene.NET does not support Java-style thread interrupts. See the LUCENENET note on CountDownLatch.Await() for details.")] + public void TestAwait_InterruptedException() + { + CountDownLatch l = new CountDownLatch(1); + ThreadJob t = new ThreadAnonymousClassForTestAwaitInterruptedException(this, l); + t.Start(); + try + { + assertEquals(l.Count, 1); + t.Interrupt(); + t.Join(); + } + catch (Exception e) when (e.IsInterruptedException()) + { + unexpectedException(); + } + } + + private sealed class ThreadAnonymousClassForTestAwaitInterruptedException : ThreadJob + { + private readonly CountDownLatchTest outerInstance; + private readonly CountDownLatch l; + + public ThreadAnonymousClassForTestAwaitInterruptedException(CountDownLatchTest outerInstance, CountDownLatch l) + { + this.outerInstance = outerInstance; + this.l = l; + } + + public override void Run() + { + try + { + outerInstance.threadAssertTrue(l.Count > 0); + l.Await(); + outerInstance.threadShouldThrow(); + } + catch (Exception e) when (e.IsInterruptedException()) + { + // success + } + } + } + + /** + * timed await throws IE if interrupted before counted down + */ + [Test] + [Ignore("LUCENENET: CountDownLatch.Await(TimeSpan) does not honor Thread.Interrupt() because Lucene.NET does not support Java-style thread interrupts. See the LUCENENET note on CountDownLatch.Await() for details.")] + public void TestTimedAwait_InterruptedException() + { + CountDownLatch l = new CountDownLatch(1); + ThreadJob t = new ThreadAnonymousClassForTestTimedAwaitInterruptedException(this, l); + t.Start(); + try + { + Thread.Sleep(SHORT_DELAY_MS); + assertEquals(l.Count, 1); + t.Interrupt(); + t.Join(); + } + catch (Exception e) when (e.IsInterruptedException()) + { + unexpectedException(); + } + } + + private sealed class ThreadAnonymousClassForTestTimedAwaitInterruptedException : ThreadJob + { + private readonly CountDownLatchTest outerInstance; + private readonly CountDownLatch l; + + public ThreadAnonymousClassForTestTimedAwaitInterruptedException(CountDownLatchTest outerInstance, CountDownLatch l) + { + this.outerInstance = outerInstance; + this.l = l; + } + + public override void Run() + { + try + { + outerInstance.threadAssertTrue(l.Count > 0); + l.Await(TimeSpan.FromMilliseconds(MEDIUM_DELAY_MS)); + outerInstance.threadShouldThrow(); + } + catch (Exception e) when (e.IsInterruptedException()) + { + // success + } + } + } + + /** + * timed await times out if not counted down before timeout + */ + [Test] + public void TestAwaitTimeout() + { + CountDownLatch l = new CountDownLatch(1); + ThreadJob t = new ThreadAnonymousClassForTestAwaitTimeout(this, l); + t.Start(); + try + { + assertEquals(l.Count, 1); + t.Join(); + } + catch (Exception e) when (e.IsInterruptedException()) + { + unexpectedException(); + } + } + + private sealed class ThreadAnonymousClassForTestAwaitTimeout : ThreadJob + { + private readonly CountDownLatchTest outerInstance; + private readonly CountDownLatch l; + + public ThreadAnonymousClassForTestAwaitTimeout(CountDownLatchTest outerInstance, CountDownLatch l) + { + this.outerInstance = outerInstance; + this.l = l; + } + + public override void Run() + { + outerInstance.threadAssertTrue(l.Count > 0); + outerInstance.threadAssertFalse(l.Await(TimeSpan.FromMilliseconds(SHORT_DELAY_MS))); + outerInstance.threadAssertTrue(l.Count > 0); + } + } + + /** + * toString indicates current count + */ + [Test] + public void TestToString() + { + CountDownLatch s = new CountDownLatch(2); + string us = s.ToString(); + assertTrue(us.IndexOf("Count = 2", StringComparison.Ordinal) >= 0); + s.CountDown(); + string s1 = s.ToString(); + assertTrue(s1.IndexOf("Count = 1", StringComparison.Ordinal) >= 0); + s.CountDown(); + string s2 = s.ToString(); + assertTrue(s2.IndexOf("Count = 0", StringComparison.Ordinal) >= 0); + } + } +} diff --git a/src/Lucene.Net/Support/Threading/CountDownLatch.cs b/src/Lucene.Net/Support/Threading/CountDownLatch.cs new file mode 100644 index 0000000000..ed7b2368b4 --- /dev/null +++ b/src/Lucene.Net/Support/Threading/CountDownLatch.cs @@ -0,0 +1,205 @@ +// Some code adapted from Apache Harmony: https://github.com/apache/harmony/blob/02970cb7227a335edd2c8457ebdde0195a735733/classlib/modules/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java + +#region Copyright 2010 by Apache Harmony, Licensed under the Apache License, Version 2.0 +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +// Other aspects adapted from .NET's CountdownEvent: https://github.com/dotnet/runtime/blob/38496302e54e1b6fb11a998b297492f3fdfbfd0c/src/libraries/System.Threading/src/System/Threading/CountdownEvent.cs + +using System; +using System.Threading; + +namespace Lucene.Net.Support.Threading +{ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /// + /// A synchronization aid that allows one or more threads to wait until + /// a set of operations being performed in other threads completes. + /// + /// A is initialized with a given count. + /// The methods block until the current count reaches + /// zero due to invocations of the method, after which + /// all waiting threads are released and any subsequent invocations of + /// return immediately. This is a one-shot phenomenon: + /// the count cannot be reset. + /// + /// A is a versatile synchronization tool + /// and can be used for a number of purposes. A + /// initialized with a count of one serves as a simple on/off latch, or gate: + /// all threads invoking wait at the gate until it is + /// opened by a thread invoking . A + /// initialized to N can be used to make + /// one thread wait until N threads have completed some action, or + /// some action has been completed N times. + /// + /// A useful property of a is that it + /// doesn't require that threads calling wait for + /// the count to reach zero before proceeding, it simply prevents any + /// thread from proceeding past an until all + /// threads could pass. + /// + /// LUCENENET specific: This type is similar to , + /// but unlike , + /// can be called any number of times after the count reaches zero without + /// throwing. This matches Java's CountDownLatch.countDown() semantics. + /// + internal class CountDownLatch + { + // LUCENENET: the current count and ManualResetEventSlim for signaling, instead of Java's Sync class + private volatile int _currentCount; + private readonly ManualResetEventSlim _event; + + /// + /// Constructs a initialized with the given . + /// + /// The number of times must be invoked + /// before threads can pass through + /// if is negative + public CountDownLatch(int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "count < 0"); + } + + _currentCount = count; + _event = new ManualResetEventSlim(count == 0); + } + + /// + /// Causes the current thread to wait until the latch has counted down to + /// zero. + /// + /// If the current count is zero then this method returns immediately. + /// + /// If the current count is greater than zero then the current + /// thread becomes disabled for thread scheduling purposes and lies + /// dormant until the count reaches zero due to invocations of the + /// method. + /// + /// LUCENENET specific: Unlike Java's await(), which throws + /// InterruptedException if the thread is interrupted while + /// waiting, this overload blocks unconditionally until the count + /// reaches zero. Callers that need cancellation/interrupt support + /// should use the timeout overload or a higher-level coordination + /// primitive. + /// + public void Await() + { + _event.Wait(Timeout.Infinite); + } + + /// + /// Causes the current thread to wait until the latch has counted down to + /// zero, or the specified waiting time elapses. + /// + /// If the current count is zero then this method returns immediately + /// with the value true. + /// + /// If the current count is greater than zero then the current + /// thread becomes disabled for thread scheduling purposes and lies + /// dormant until one of two things happen: + /// + /// The count reaches zero due to invocations of the + /// method; or + /// The specified waiting time elapses. + /// + /// + /// If the count reaches zero then the method returns with the + /// value true. + /// + /// If the specified waiting time elapses then the value false + /// is returned. If the time is less than or equal to zero, the method + /// will not wait at all. + /// + /// The maximum time to wait. + /// true if the count reached zero, or false + /// if the waiting time elapsed before the count reached zero. + public bool Await(TimeSpan timeout) + { + return _event.Wait(timeout); + } + + /// + /// Decrements the count of the latch, releasing all waiting threads if + /// the count reaches zero. + /// + /// If the current count is greater than zero then it is decremented. + /// If the new count is zero then all waiting threads are re-enabled for + /// thread scheduling purposes. + /// + /// If the current count equals zero then nothing happens. + /// + public void CountDown() + { + if (_currentCount <= 0) + { + // Try to avoid unnecessary decrementing of the count below zero, + // but a couple concurrent races below zero are fine + return; + } + + int newCount = Interlocked.Decrement(ref _currentCount); + if (newCount <= 0) + { + _event.Set(); + } + } + + /// + /// Returns the current count. + /// + /// This property is typically used for debugging and testing purposes. + /// + /// + /// In Java, getCount() returns long, but internally it's + /// always an int. Here we retain the Java return type. There is no need + /// to make the internal count 64-bit. + /// + /// The returned value is clamped at zero. The internal counter may + /// briefly drop below zero under concurrent + /// calls that race past the early-return guard, but that detail is + /// hidden here to match Java's getCount() semantics, which never + /// returns a negative value. + /// + public long Count => Math.Max(0, _currentCount); + + /// + /// Returns a string identifying this latch, as well as its state. + /// The state, in brackets, includes the String "Count =" + /// followed by the current count. + /// + /// a string identifying this latch, as well as its state + public override string ToString() => $"{base.ToString()}[Count = {_currentCount}]"; + } +} From 6c4ad933fd3224b8fd1be4268834091bd422b803 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 28 May 2026 16:01:33 -0600 Subject: [PATCH 07/20] Migrate test CountdownEvent usage to Java-style CountDownLatch, #1284 Replaces System.Threading.CountdownEvent with the new Lucene.Net.Support.Threading.CountDownLatch across 22 test files where the upstream Java equivalent uses java.util.concurrent.CountDownLatch. Mechanical changes: - type rename: CountdownEvent -> CountDownLatch - .Signal() -> .CountDown() - .Wait() / .Wait(TimeSpan) -> .Await() / .Await(TimeSpan) - .CurrentCount -> .Count - .IsSet -> .Count == 0 - added using Lucene.Net.Support.Threading where needed Workarounds for CountdownEvent.Signal()'s "throws past zero" behavior that are now redundant (CountDown is idempotent at zero, matching Java): - TestConcurrentMergeScheduler.TestMaxMergeCount: removed the CurrentCount > 0 guard. This was the trigger for the CI failure on Windows .NET Framework and Ubuntu .NET 8 after the LUCENE-5871 port made Shutdown(false) flush more aggressively. - TestIndexWriterWithThreads: removed the same pre-existing guard at iwConstructed. - TestDocumentsWriterStallControl: removed if (!updateJoin.IsSet) guard around CountDown(); upstream calls countDown() unconditionally. - TestControlledRealTimeReopenThread: replaced two signal.Reset(Count - 1)/latch.Reset(Count - 1) hacks with plain CountDown(), matching upstream signal.countDown()/latch.countDown(). Other adjustments: - BaseDocValuesFormatTestCase: dropped `using` keyword on two latches. CountdownEvent was IDisposable; CountDownLatch isn't (Java's isn't either), so the latches now go out of scope without explicit cleanup. - TestControlledRealTimeReopenThread.LatchedIndexWriter: ctor changed from public to internal so CountDownLatch (internal) is reachable from a public class member. Files touched: 22 test files plus 2 in TestFramework. All ported sites now read the same as the upstream Java; no LUCENENET notes were added because the code is now equivalent. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Analysis/BaseTokenStreamTestCase.cs | 11 ++-- .../Index/BaseDocValuesFormatTestCase.cs | 21 +++---- .../Index/TestBagOfPositions.cs | 10 ++-- .../Index/TestBagOfPostings.cs | 10 ++-- .../Index/TestBinaryDocValuesUpdates.cs | 11 ++-- .../Index/TestConcurrentMergeScheduler.cs | 13 +++-- .../Index/TestDocValuesIndexing.cs | 11 ++-- .../Index/TestDocValuesWithThreads.cs | 11 ++-- .../Index/TestDocumentsWriterDeleteQueue.cs | 10 ++-- .../Index/TestDocumentsWriterStallControl.cs | 43 +++++++-------- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 41 +++++++------- .../Index/TestIndexWriterDelete.cs | 19 ++++--- .../Index/TestIndexWriterNRTIsCurrent.cs | 16 +++--- .../Index/TestIndexWriterThreadsToSegments.cs | 55 ++++++++++--------- .../Index/TestIndexWriterWithThreads.cs | 19 +++---- .../Index/TestMixedDocValuesUpdates.cs | 11 ++-- .../Index/TestNumericDocValuesUpdates.cs | 11 ++-- .../Search/TestAutomatonQuery.cs | 11 ++-- .../TestControlledRealTimeReopenThread.cs | 24 ++++---- .../Search/TestLiveFieldValues.cs | 11 ++-- .../Search/TestSameScoresWithThreads.cs | 11 ++-- .../Search/TestSearcherManager.cs | 18 +++--- 22 files changed, 203 insertions(+), 195 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index 5bb64c285e..4ce6d420c5 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -17,6 +17,7 @@ using Attribute = Lucene.Net.Util.Attribute; using Directory = Lucene.Net.Store.Directory; using JCG = J2N.Collections.Generic; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Analysis { @@ -629,7 +630,7 @@ internal class AnalysisThread : ThreadJob internal readonly bool simple; internal readonly bool offsetsAreCorrect; internal readonly RandomIndexWriter iw; - private readonly CountdownEvent latch; + private readonly CountDownLatch latch; // NOTE: not volatile because we don't want the tests to // add memory barriers (ie alter how threads @@ -637,7 +638,7 @@ internal class AnalysisThread : ThreadJob public bool Failed { get; set; } public Exception FirstException { get; set; } = null; - internal AnalysisThread(long seed, CountdownEvent latch, Analyzer a, int iterations, int maxWordLength, + internal AnalysisThread(long seed, CountDownLatch latch, Analyzer a, int iterations, int maxWordLength, bool useCharFilter, bool simple, bool offsetsAreCorrect, RandomIndexWriter iw) { this.seed = seed; @@ -656,7 +657,7 @@ public override void Run() bool success = false; try { - if (latch != null) latch.Wait(); + if (latch != null) latch.Await(); // see the part in checkRandomData where it replays the same text again // to verify reproducability/reuse: hopefully this would catch thread hazards. CheckRandomData(new J2N.Randomizer(seed), a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw); @@ -710,7 +711,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in // now test with multiple threads: note we do the EXACT same thing we did before in each thread, // so this should only really fail from another thread if its an actual thread problem int numThreads = TestUtil.NextInt32(random, 2, 4); - var startingGun = new CountdownEvent(1); + var startingGun = new CountDownLatch(1); var threads = new AnalysisThread[numThreads]; for (int i = 0; i < threads.Length; i++) { @@ -722,7 +723,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in thread.Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (var t in threads) { try diff --git a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs index 79b4097fcc..e4aca4de40 100644 --- a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs @@ -18,6 +18,7 @@ using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; using Test = NUnit.Framework.TestAttribute; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -3243,13 +3244,13 @@ public virtual void TestThreads() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - using CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass(ir, startingGun); threads[i].Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob t in threads) { t.Join(); @@ -3259,9 +3260,9 @@ public virtual void TestThreads() private sealed class ThreadAnonymousClass : ThreadJob { private readonly DirectoryReader ir; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; - public ThreadAnonymousClass(DirectoryReader ir, CountdownEvent startingGun) + public ThreadAnonymousClass(DirectoryReader ir, CountDownLatch startingGun) { this.ir = ir; this.startingGun = startingGun; @@ -3271,7 +3272,7 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); BytesRef scratch = new BytesRef(); // LUCENENET: Moved outside of the loop for performance foreach (AtomicReaderContext context in ir.Leaves) { @@ -3379,13 +3380,13 @@ public virtual void TestThreads2() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - using CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass2(ir, startingGun); threads[i].Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob t in threads) { t.Join(); @@ -3395,9 +3396,9 @@ public virtual void TestThreads2() private sealed class ThreadAnonymousClass2 : ThreadJob { private readonly DirectoryReader ir; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; - public ThreadAnonymousClass2(DirectoryReader ir, CountdownEvent startingGun) + public ThreadAnonymousClass2(DirectoryReader ir, CountDownLatch startingGun) { this.ir = ir; this.startingGun = startingGun; @@ -3407,7 +3408,7 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); foreach (AtomicReaderContext context in ir.Leaves) { AtomicReader r = context.AtomicReader; diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs index 6c7abfa04a..aad88c4cbd 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs @@ -117,7 +117,7 @@ public virtual void Test() // else just positions ThreadJob[] threads = new ThreadJob[threadCount]; - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); for (int threadID = 0; threadID < threadCount; threadID++) { @@ -128,7 +128,7 @@ public virtual void Test() threads[threadID] = new ThreadAnonymousClass(maxTermsPerDoc, postings, iw, startingGun, threadRandom, document, field); threads[threadID].Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob t in threads) { t.Join(); @@ -160,12 +160,12 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly int maxTermsPerDoc; private readonly ConcurrentQueue postings; private readonly RandomIndexWriter iw; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; private readonly Random threadRandom; private readonly Document document; private readonly Field field; - public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountdownEvent startingGun, Random threadRandom, Document document, Field field) + public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountDownLatch startingGun, Random threadRandom, Document document, Field field) { this.maxTermsPerDoc = maxTermsPerDoc; this.postings = postings; @@ -180,7 +180,7 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); while (!postings.IsEmpty) { StringBuilder text = new StringBuilder(); diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs b/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs index a3fef2598a..f2720f901f 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs @@ -96,14 +96,14 @@ public virtual void Test() } ThreadJob[] threads = new ThreadJob[threadCount]; - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); for (int threadID = 0; threadID < threadCount; threadID++) { threads[threadID] = new ThreadAnonymousClass(maxTermsPerDoc, postings, iw, startingGun); threads[threadID].Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob t in threads) { t.Join(); @@ -141,9 +141,9 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly int maxTermsPerDoc; private readonly ConcurrentQueue postings; private readonly RandomIndexWriter iw; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; - public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountdownEvent startingGun) + public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountDownLatch startingGun) { this.maxTermsPerDoc = maxTermsPerDoc; this.postings = postings; @@ -158,7 +158,7 @@ public override void Run() Document document = new Document(); Field field = NewTextField("field", "", Field.Store.NO); document.Add(field); - startingGun.Wait(); + startingGun.Await(); while (!postings.IsEmpty) { StringBuilder text = new StringBuilder(); diff --git a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs index 91683b48ac..d4ee105c02 100644 --- a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs @@ -18,6 +18,7 @@ using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -1200,7 +1201,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountdownEvent done = new CountdownEvent(numThreads); + CountDownLatch done = new CountDownLatch(numThreads); AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens @@ -1216,7 +1217,7 @@ public virtual void TestStressMultiThreading() { t.Start(); } - done.Wait(); + done.Await(); writer.Dispose(); DirectoryReader reader = DirectoryReader.Open(dir); @@ -1253,12 +1254,12 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter writer; private readonly int numDocs; - private readonly CountdownEvent done; + private readonly CountDownLatch done; private readonly AtomicInt32 numUpdates; private readonly string f; private readonly string cf; - public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountdownEvent done, AtomicInt32 numUpdates, string f, string cf) + public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountDownLatch done, AtomicInt32 numUpdates, string f, string cf) : base(str) { this.writer = writer; @@ -1365,7 +1366,7 @@ public override void Run() } } } - done.Signal(); + done.CountDown(); } } } diff --git a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs index fb8150dab4..2afbbd752f 100644 --- a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs +++ b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs @@ -3,6 +3,7 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Store; +using Lucene.Net.Support.Threading; using Lucene.Net.Util; using NUnit.Framework; using RandomizedTesting.Generators; @@ -286,7 +287,7 @@ public virtual void TestMaxMergeCount() int maxMergeCount = TestUtil.NextInt32(Random, 1, 5); int maxMergeThreads = TestUtil.NextInt32(Random, 1, maxMergeCount); - CountdownEvent enoughMergesWaiting = new CountdownEvent(maxMergeCount); + CountDownLatch enoughMergesWaiting = new CountDownLatch(maxMergeCount); AtomicInt32 runningMergeCount = new AtomicInt32(0); AtomicBoolean failed = new AtomicBoolean(); @@ -308,7 +309,7 @@ public virtual void TestMaxMergeCount() IndexWriter w = new IndexWriter(dir, iwc); Document doc = new Document(); doc.Add(NewField("field", "field", TextField.TYPE_NOT_STORED)); - while (enoughMergesWaiting.CurrentCount != 0 && !failed) + while (enoughMergesWaiting.Count != 0 && !failed) { for (int i = 0; i < 10; i++) { @@ -322,11 +323,11 @@ public virtual void TestMaxMergeCount() private sealed class ConcurrentMergeSchedulerAnonymousClass : ConcurrentMergeScheduler { private readonly int maxMergeCount; - private readonly CountdownEvent enoughMergesWaiting; + private readonly CountDownLatch enoughMergesWaiting; private readonly AtomicInt32 runningMergeCount; private readonly AtomicBoolean failed; - public ConcurrentMergeSchedulerAnonymousClass(int maxMergeCount, CountdownEvent enoughMergesWaiting, AtomicInt32 runningMergeCount, AtomicBoolean failed) + public ConcurrentMergeSchedulerAnonymousClass(int maxMergeCount, CountDownLatch enoughMergesWaiting, AtomicInt32 runningMergeCount, AtomicBoolean failed) { this.maxMergeCount = maxMergeCount; this.enoughMergesWaiting = enoughMergesWaiting; @@ -344,14 +345,14 @@ protected internal override void DoMerge(MergePolicy.OneMerge merge) try { Assert.IsTrue(count <= maxMergeCount, "count=" + count + " vs maxMergeCount=" + maxMergeCount); - enoughMergesWaiting.Signal(); + enoughMergesWaiting.CountDown(); // Stall this merge until we see exactly // maxMergeCount merges waiting while (true) { // wait for 10 milliseconds - if (enoughMergesWaiting.Wait(new TimeSpan(0, 0, 0, 0, 10)) || failed) + if (enoughMergesWaiting.Await(new TimeSpan(0, 0, 0, 0, 10)) || failed) { break; } diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs index 817bb7dd01..9e4f2a4aa5 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs @@ -7,6 +7,7 @@ using System; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -513,7 +514,7 @@ public virtual void TestMixedTypesDifferentThreads() Directory dir = NewDirectory(); IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); AtomicBoolean hitExc = new AtomicBoolean(); ThreadJob[] threads = new ThreadJob[3]; for (int i = 0; i < 3; i++) @@ -538,7 +539,7 @@ public virtual void TestMixedTypesDifferentThreads() threads[i].Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob t in threads) { @@ -552,11 +553,11 @@ public virtual void TestMixedTypesDifferentThreads() private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter w; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; private readonly AtomicBoolean hitExc; private readonly Document doc; - public ThreadAnonymousClass(IndexWriter w, CountdownEvent startingGun, AtomicBoolean hitExc, Document doc) + public ThreadAnonymousClass(IndexWriter w, CountDownLatch startingGun, AtomicBoolean hitExc, Document doc) { this.w = w; this.startingGun = startingGun; @@ -568,7 +569,7 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); w.AddDocument(doc); } catch (Exception iae) when (iae.IsIllegalArgumentException()) diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs index 0e4c8f7ccf..7312de27ee 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs @@ -9,6 +9,7 @@ using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; using RandomizedTesting.Generators; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -77,7 +78,7 @@ public virtual void Test() int numThreads = TestUtil.NextInt32(Random, 2, 5); IList threads = new JCG.List(); - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); for (int t = 0; t < numThreads; t++) { Random threadRandom = new J2N.Randomizer(Random.NextInt64()); @@ -86,7 +87,7 @@ public virtual void Test() threads.Add(thread); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob thread in threads) { @@ -104,10 +105,10 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly IList sorted; private readonly int numDocs; private readonly AtomicReader ar; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; private readonly Random threadRandom; - public ThreadAnonymousClass(IList numbers, IList binary, IList sorted, int numDocs, AtomicReader ar, CountdownEvent startingGun, Random threadRandom) + public ThreadAnonymousClass(IList numbers, IList binary, IList sorted, int numDocs, AtomicReader ar, CountDownLatch startingGun, Random threadRandom) { this.numbers = numbers; this.binary = binary; @@ -127,7 +128,7 @@ public override void Run() //BinaryDocValues bdv = ar.GetBinaryDocValues("bytes"); BinaryDocValues bdv = FieldCache.DEFAULT.GetTerms(ar, "bytes", false); SortedDocValues sdv = FieldCache.DEFAULT.GetTermsIndex(ar, "sorted"); - startingGun.Wait(); + startingGun.Await(); int iters = AtLeast(1000); BytesRef scratch = new BytesRef(); BytesRef scratch2 = new BytesRef(); diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs index e6fdc176fb..91ab4ef245 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs @@ -258,7 +258,7 @@ public virtual void TestStressDeleteQueue() ids[i] = Random.Next(); uniqueValues.Add(new Term("id", ids[i].ToString())); } - CountdownEvent latch = new CountdownEvent(1); + CountDownLatch latch = new CountDownLatch(1); AtomicInt32 index = new AtomicInt32(0); int numThreads = 2 + Random.Next(5); UpdateThread[] threads = new UpdateThread[numThreads]; @@ -267,7 +267,7 @@ public virtual void TestStressDeleteQueue() threads[i] = new UpdateThread(queue, index, ids, latch); threads[i].Start(); } - latch.Signal(); + latch.CountDown(); for (int i = 0; i < threads.Length; i++) { threads[i].Join(); @@ -301,9 +301,9 @@ private class UpdateThread : ThreadJob internal readonly int[] ids; internal readonly DeleteSlice slice; internal readonly BufferedUpdates deletes; - internal readonly CountdownEvent latch; + internal readonly CountDownLatch latch; - protected internal UpdateThread(DocumentsWriterDeleteQueue queue, AtomicInt32 index, int[] ids, CountdownEvent latch) + protected internal UpdateThread(DocumentsWriterDeleteQueue queue, AtomicInt32 index, int[] ids, CountDownLatch latch) { this.queue = queue; this.index = index; @@ -317,7 +317,7 @@ public override void Run() { try { - latch.Wait(); + latch.Await(); } catch (Exception ie) when (ie.IsInterruptedException()) { diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs index ee363b2ad0..cd870bc6c3 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -153,7 +154,7 @@ public virtual void TestAccquireReleaseRace() { if (checkPoint) { - Assert.IsTrue(sync.updateJoin.Wait(TimeSpan.FromSeconds(10)), "timed out waiting for update threads - deadlock?"); + Assert.IsTrue(sync.updateJoin.Await(TimeSpan.FromSeconds(10)), "timed out waiting for update threads - deadlock?"); if (exceptions.Count > 0) { foreach (Exception throwable in exceptions) @@ -169,11 +170,11 @@ public virtual void TestAccquireReleaseRace() } checkPoint.Value = false; - sync.waiter.Signal(); - sync.leftCheckpoint.Wait(); + sync.waiter.CountDown(); + sync.leftCheckpoint.Await(); } Assert.IsFalse(checkPoint); - Assert.AreEqual(0, sync.waiter.CurrentCount); + Assert.AreEqual(0, sync.waiter.Count); if (checkPointProbability >= Random.NextSingle()) { sync.Reset(numStallers + numReleasers, numStallers + numReleasers + numWaiters); @@ -186,12 +187,12 @@ public virtual void TestAccquireReleaseRace() checkPoint.Value = true; } - Assert.IsTrue(sync.updateJoin.Wait(TimeSpan.FromSeconds(10))); + Assert.IsTrue(sync.updateJoin.Await(TimeSpan.FromSeconds(10))); AssertState(numReleasers, numStallers, numWaiters, threads, ctrl); checkPoint.Value = false; stop.Value = true; - sync.waiter.Signal(); - sync.leftCheckpoint.Wait(); + sync.waiter.CountDown(); + sync.leftCheckpoint.Await(); for (int i = 0; i < threads.Length; i++) { @@ -272,7 +273,7 @@ public override void Run() } catch (Exception e) when (e.IsInterruptedException()) { - Console.WriteLine("[Waiter] got interrupted - wait count: " + sync.waiter.CurrentCount); + Console.WriteLine("[Waiter] got interrupted - wait count: " + sync.waiter.Count); throw new Util.ThreadInterruptedException(e); } } @@ -319,14 +320,14 @@ public override void Run() } if (checkPoint) { - sync.updateJoin.Signal(); + sync.updateJoin.CountDown(); try { Assert.IsTrue(sync.Await()); } catch (Exception e) when (e.IsInterruptedException()) { - Console.WriteLine("[Updater] got interrupted - wait count: " + sync.waiter.CurrentCount); + Console.WriteLine("[Updater] got interrupted - wait count: " + sync.waiter.Count); throw new Util.ThreadInterruptedException(e); } // LUCENENET: Not sure why this catch block was added, but I suspect it was for debugging purposes. Commented it rather than removing it because @@ -337,7 +338,7 @@ public override void Run() // throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) //} - sync.leftCheckpoint.Signal(); + sync.leftCheckpoint.CountDown(); } if (Random.NextBoolean()) { @@ -351,11 +352,7 @@ public override void Run() exceptions.Add(e); } - // LUCENENET specific - possible InvalidOperationException here if Signal() is called more than what is required to decrement to zero - if (!sync.updateJoin.IsSet) - { - sync.updateJoin.Signal(); - } + sync.updateJoin.CountDown(); } } @@ -446,9 +443,9 @@ public static void AwaitState(ThreadState state, params ThreadJob[] threads) public sealed class Synchronizer { - internal volatile CountdownEvent waiter; - internal volatile CountdownEvent updateJoin; - internal volatile CountdownEvent leftCheckpoint; + internal volatile CountDownLatch waiter; + internal volatile CountDownLatch updateJoin; + internal volatile CountDownLatch leftCheckpoint; public Synchronizer(int numUpdater, int numThreads) { @@ -457,14 +454,14 @@ public Synchronizer(int numUpdater, int numThreads) public void Reset(int numUpdaters, int numThreads) { - this.waiter = new CountdownEvent(1); - this.updateJoin = new CountdownEvent(numUpdaters); - this.leftCheckpoint = new CountdownEvent(numUpdaters); + this.waiter = new CountDownLatch(1); + this.updateJoin = new CountDownLatch(numUpdaters); + this.leftCheckpoint = new CountDownLatch(numUpdaters); } public bool Await() { - return waiter.Wait(TimeSpan.FromSeconds(10)); + return waiter.Await(TimeSpan.FromSeconds(10)); } } } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 533ce9777a..3349b2c587 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -20,6 +20,7 @@ using System.Text; using System.Threading; using JCG = J2N.Collections.Generic; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -3020,8 +3021,8 @@ public virtual void TestRollbackWhileMergeIsRunning() { Directory dir = NewDirectory(); - CountdownEvent mergeStarted = new CountdownEvent(1); - CountdownEvent closeStarted = new CountdownEvent(1); + CountDownLatch mergeStarted = new CountDownLatch(1); + CountDownLatch closeStarted = new CountDownLatch(1); IndexWriterConfig iwc = NewIndexWriterConfig(Random, TEST_VERSION_CURRENT, new MockAnalyzer(Random)); LogDocMergePolicy mp = new LogDocMergePolicy(); @@ -3041,9 +3042,9 @@ public virtual void TestRollbackWhileMergeIsRunning() private sealed class InfoStreamAnonymousClassForRollbackWhileMergeIsRunning : InfoStream { - private readonly CountdownEvent closeStarted; + private readonly CountDownLatch closeStarted; - public InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(CountdownEvent closeStarted) + public InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(CountDownLatch closeStarted) { this.closeStarted = closeStarted; } @@ -3057,7 +3058,7 @@ public override void Message(string component, string message) { if (message.Equals("rollback", StringComparison.Ordinal)) { - closeStarted.Signal(); + closeStarted.CountDown(); } } @@ -3068,10 +3069,10 @@ protected override void Dispose(bool disposing) private sealed class ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning : ConcurrentMergeScheduler { - private readonly CountdownEvent mergeStarted; - private readonly CountdownEvent closeStarted; + private readonly CountDownLatch mergeStarted; + private readonly CountDownLatch closeStarted; - public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(CountdownEvent mergeStarted, CountdownEvent closeStarted) + public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(CountDownLatch mergeStarted, CountDownLatch closeStarted) { this.mergeStarted = mergeStarted; this.closeStarted = closeStarted; @@ -3079,10 +3080,10 @@ public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(Coun protected internal override void DoMerge(MergePolicy.OneMerge merge) { - mergeStarted.Signal(); + mergeStarted.CountDown(); try { - closeStarted.Wait(); + closeStarted.Await(); } catch (Exception ie) when (ie.IsInterruptedException()) { @@ -3147,8 +3148,8 @@ public virtual void TestClosingNRTReaderDoesNotCorruptYourIndex() [Test] public virtual void TestCloseDuringCommit() { - CountdownEvent startCommit = new CountdownEvent(1); - CountdownEvent finishCommit = new CountdownEvent(1); + CountDownLatch startCommit = new CountDownLatch(1); + CountDownLatch finishCommit = new CountDownLatch(1); Directory dir = NewDirectory(); IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, null); @@ -3157,7 +3158,7 @@ public virtual void TestCloseDuringCommit() IndexWriter iw = new IndexWriter(dir, iwc); Document doc = new Document(); new ThreadAnonymousClassForCloseDuringCommit(iw, finishCommit).Start(); - startCommit.Wait(); + startCommit.Await(); try { iw.Dispose(); @@ -3166,16 +3167,16 @@ public virtual void TestCloseDuringCommit() { // OK, but not required (depends on thread scheduling) } - finishCommit.Wait(); + finishCommit.Await(); iw.Dispose(); dir.Dispose(); } private sealed class InfoStreamAnonymousClassForCloseDuringCommit : InfoStream { - private readonly CountdownEvent startCommit; + private readonly CountDownLatch startCommit; - public InfoStreamAnonymousClassForCloseDuringCommit(CountdownEvent startCommit) + public InfoStreamAnonymousClassForCloseDuringCommit(CountDownLatch startCommit) { this.startCommit = startCommit; } @@ -3184,7 +3185,7 @@ public override void Message(string component, string message) { if (message.Equals("finishStartCommit", StringComparison.Ordinal)) { - startCommit.Signal(); + startCommit.CountDown(); try { Thread.Sleep(10); @@ -3209,9 +3210,9 @@ protected override void Dispose(bool disposing) private sealed class ThreadAnonymousClassForCloseDuringCommit : ThreadJob { private readonly IndexWriter iw; - private readonly CountdownEvent finishCommit; + private readonly CountDownLatch finishCommit; - public ThreadAnonymousClassForCloseDuringCommit(IndexWriter iw, CountdownEvent finishCommit) + public ThreadAnonymousClassForCloseDuringCommit(IndexWriter iw, CountDownLatch finishCommit) { this.iw = iw; this.finishCommit = finishCommit; @@ -3222,7 +3223,7 @@ public override void Run() try { iw.Commit(); - finishCommit.Signal(); + finishCommit.CountDown(); } catch (IOException ioe) { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs index 1527d4d9f4..52907111ae 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs @@ -18,6 +18,7 @@ using JCG = J2N.Collections.Generic; // ReSharper disable once RedundantUsingDirective - keep until we have an analyzer to look out for accidental NUnit asserts using Assert = Lucene.Net.TestFramework.Assert; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -355,17 +356,17 @@ public virtual void TestDeleteAllNoDeadLock() RandomIndexWriter modifier = new RandomIndexWriter(Random, dir); int numThreads = AtLeast(2); ThreadJob[] threads = new ThreadJob[numThreads]; - CountdownEvent latch = new CountdownEvent(1); - CountdownEvent doneLatch = new CountdownEvent(numThreads); + CountDownLatch latch = new CountDownLatch(1); + CountDownLatch doneLatch = new CountDownLatch(numThreads); for (int i = 0; i < numThreads; i++) { int offset = i; threads[i] = new ThreadAnonymousClass(modifier, latch, doneLatch, offset); threads[i].Start(); } - latch.Signal(); + latch.CountDown(); //Wait for 1 millisecond - while (!doneLatch.Wait(TimeSpan.FromMilliseconds(1))) + while (!doneLatch.Await(TimeSpan.FromMilliseconds(1))) { modifier.DeleteAll(); if (Verbose) @@ -393,11 +394,11 @@ public virtual void TestDeleteAllNoDeadLock() private sealed class ThreadAnonymousClass : ThreadJob { private readonly RandomIndexWriter modifier; - private readonly CountdownEvent latch; - private readonly CountdownEvent doneLatch; + private readonly CountDownLatch latch; + private readonly CountDownLatch doneLatch; private readonly int offset; - public ThreadAnonymousClass(RandomIndexWriter modifier, CountdownEvent latch, CountdownEvent doneLatch, int offset) + public ThreadAnonymousClass(RandomIndexWriter modifier, CountDownLatch latch, CountDownLatch doneLatch, int offset) { this.modifier = modifier; this.latch = latch; @@ -411,7 +412,7 @@ public override void Run() const int value = 100; try { - latch.Wait(); + latch.Await(); for (int j = 0; j < 1000; j++) { Document doc = new Document(); @@ -435,7 +436,7 @@ public override void Run() } finally { - doneLatch.Signal(); + doneLatch.CountDown(); if (Verbose) { Console.WriteLine("\tThread[" + offset + "]: done indexing"); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs index 3640d41e39..f5e594ad76 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs @@ -52,7 +52,7 @@ public virtual void TestIsCurrentWithThreads() IndexWriter writer = new IndexWriter(dir, conf); ReaderHolder holder = new ReaderHolder(); ReaderThread[] threads = new ReaderThread[AtLeast(3)]; - CountdownEvent latch = new CountdownEvent(1); + CountDownLatch latch = new CountDownLatch(1); WriterThread writerThread = new WriterThread(holder, writer, AtLeast(500), Random, latch); for (int i = 0; i < threads.Length; i++) { @@ -87,10 +87,10 @@ public class WriterThread : ThreadJob internal readonly IndexWriter writer; internal readonly int numOps; internal bool countdown = true; - internal readonly CountdownEvent latch; + internal readonly CountDownLatch latch; internal Exception failed; - internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, Random random, CountdownEvent latch) + internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, Random random, CountDownLatch latch) : base() { this.holder = holder; @@ -133,7 +133,7 @@ public override void Run() if (countdown) { countdown = false; - latch.Signal(); + latch.CountDown(); } } if (random.NextBoolean()) @@ -161,7 +161,7 @@ public override void Run() holder.reader = null; if (countdown) { - latch.Signal(); + latch.CountDown(); } if (currentReader != null) { @@ -184,10 +184,10 @@ public override void Run() public sealed class ReaderThread : ThreadJob { internal readonly ReaderHolder holder; - internal readonly CountdownEvent latch; + internal readonly CountDownLatch latch; internal Exception failed; - internal ReaderThread(ReaderHolder holder, CountdownEvent latch) + internal ReaderThread(ReaderHolder holder, CountDownLatch latch) : base() { this.holder = holder; @@ -198,7 +198,7 @@ public override void Run() { try { - latch.Wait(); + latch.Await(); } catch (Exception e) when (e.IsInterruptedException()) { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs index aa7ef67e60..d0551718a7 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -43,10 +44,10 @@ public virtual void TestSegmentCountOnFlushBasic() { Directory dir = NewDirectory(); IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - CountdownEvent startingGun = new CountdownEvent(1); - CountdownEvent startDone = new CountdownEvent(2); - CountdownEvent middleGun = new CountdownEvent(1); - CountdownEvent finalGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); + CountDownLatch startDone = new CountDownLatch(2); + CountDownLatch middleGun = new CountDownLatch(1); + CountDownLatch finalGun = new CountDownLatch(1); ThreadJob[] threads = new ThreadJob[2]; for (int i = 0; i < threads.Length; i++) { @@ -55,8 +56,8 @@ public virtual void TestSegmentCountOnFlushBasic() threads[i].Start(); } - startingGun.Signal(); - startDone.Wait(); + startingGun.CountDown(); + startDone.Await(); IndexReader r = DirectoryReader.Open(w, true); Assert.AreEqual(2, r.NumDocs); @@ -65,10 +66,10 @@ public virtual void TestSegmentCountOnFlushBasic() Assert.IsTrue(numSegments <= 2); r.Dispose(); - middleGun.Signal(); + middleGun.CountDown(); threads[0].Join(); - finalGun.Signal(); + finalGun.CountDown(); threads[1].Join(); r = DirectoryReader.Open(w, true); @@ -85,12 +86,12 @@ private sealed class ThreadAnonymousClassForSegmentCountOnFlushBasic : ThreadJob { private readonly IndexWriter w; private readonly int threadID; - private readonly CountdownEvent startingGun; - private readonly CountdownEvent startDone; - private readonly CountdownEvent middleGun; - private readonly CountdownEvent finalGun; + private readonly CountDownLatch startingGun; + private readonly CountDownLatch startDone; + private readonly CountDownLatch middleGun; + private readonly CountDownLatch finalGun; - public ThreadAnonymousClassForSegmentCountOnFlushBasic(IndexWriter w, int threadID, CountdownEvent startingGun, CountdownEvent startDone, CountdownEvent middleGun, CountdownEvent finalGun) + public ThreadAnonymousClassForSegmentCountOnFlushBasic(IndexWriter w, int threadID, CountDownLatch startingGun, CountDownLatch startDone, CountDownLatch middleGun, CountDownLatch finalGun) { this.w = w; this.threadID = threadID; @@ -104,20 +105,20 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); Document doc = new Document(); doc.Add(NewTextField("field", "here is some text", Field.Store.NO)); w.AddDocument(doc); - startDone.Signal(); + startDone.CountDown(); - middleGun.Wait(); + middleGun.Await(); if (threadID == 0) { w.AddDocument(doc); } else { - finalGun.Wait(); + finalGun.Await(); w.AddDocument(doc); } } @@ -310,14 +311,14 @@ public virtual void TestManyThreadsClose() RandomIndexWriter w = new RandomIndexWriter(Random, dir); w.DoRandomForceMerge = false; ThreadJob[] threads = new ThreadJob[TestUtil.NextInt32(Random, 4, 30)]; - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClassForManyThreadsClose(w, startingGun); threads[i].Start(); } - startingGun.Signal(); + startingGun.CountDown(); Thread.Sleep(100); // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released in @@ -343,9 +344,9 @@ public virtual void TestManyThreadsClose() private sealed class ThreadAnonymousClassForManyThreadsClose : ThreadJob { private readonly RandomIndexWriter w; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; - public ThreadAnonymousClassForManyThreadsClose(RandomIndexWriter w, CountdownEvent startingGun) + public ThreadAnonymousClassForManyThreadsClose(RandomIndexWriter w, CountDownLatch startingGun) { this.w = w; this.startingGun = startingGun; @@ -355,7 +356,7 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); Document doc = new Document(); doc.Add(new TextField("field", "here is some text that is a bit longer than normal trivial text", Field.Store.NO)); // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released @@ -388,7 +389,7 @@ public virtual void TestDocsStuckInRAMForever() iwc.SetCodec(codec); iwc.SetMergePolicy(NoMergePolicy.NO_COMPOUND_FILES); IndexWriter w = new IndexWriter(dir, iwc); - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); ThreadJob[] threads = new ThreadJob[2]; for (int i = 0; i < threads.Length; i++) { @@ -397,7 +398,7 @@ public virtual void TestDocsStuckInRAMForever() threads[i].Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob t in threads) { t.Join(); @@ -449,9 +450,9 @@ private sealed class ThreadAnonymousClassForDocsStuckInRAMForever : ThreadJob { private readonly IndexWriter w; private readonly int threadID; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; - public ThreadAnonymousClassForDocsStuckInRAMForever(IndexWriter w, int threadID, CountdownEvent startingGun) + public ThreadAnonymousClassForDocsStuckInRAMForever(IndexWriter w, int threadID, CountDownLatch startingGun) { this.w = w; this.threadID = threadID; @@ -462,7 +463,7 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); for (int j = 0; j < 1000; j++) { Document doc = new Document(); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs index e406ebdb8f..858698f9f9 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs @@ -551,14 +551,14 @@ public virtual void TestIOExceptionDuringWriteSegmentWithThreadsOnlyOnce() public virtual void TestOpenTwoIndexWritersOnDifferentThreads() { Directory dir = NewDirectory(); - CountdownEvent oneIWConstructed = new CountdownEvent(1); + CountDownLatch oneIWConstructed = new CountDownLatch(1); DelayedIndexAndCloseRunnable thread1 = new DelayedIndexAndCloseRunnable(dir, oneIWConstructed); DelayedIndexAndCloseRunnable thread2 = new DelayedIndexAndCloseRunnable(dir, oneIWConstructed); thread1.Start(); thread2.Start(); - oneIWConstructed.Wait(); + oneIWConstructed.Await(); thread1.StartIndexing(); thread2.StartIndexing(); @@ -592,10 +592,10 @@ internal class DelayedIndexAndCloseRunnable : ThreadJob private readonly Directory dir; internal bool failed = false; internal Exception failure = null; - private readonly CountdownEvent startIndexing = new CountdownEvent(1); - private CountdownEvent iwConstructed; + private readonly CountDownLatch startIndexing = new CountDownLatch(1); + private CountDownLatch iwConstructed; - public DelayedIndexAndCloseRunnable(Directory dir, CountdownEvent iwConstructed) + public DelayedIndexAndCloseRunnable(Directory dir, CountDownLatch iwConstructed) { this.dir = dir; this.iwConstructed = iwConstructed; @@ -603,7 +603,7 @@ public DelayedIndexAndCloseRunnable(Directory dir, CountdownEvent iwConstructed) public virtual void StartIndexing() { - this.startIndexing.Signal(); + this.startIndexing.CountDown(); } public override void Run() @@ -614,11 +614,8 @@ public override void Run() Field field = NewTextField("field", "testData", Field.Store.YES); doc.Add(field); using IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - if (iwConstructed.CurrentCount > 0) - { - iwConstructed.Signal(); - } - startIndexing.Wait(); + iwConstructed.CountDown(); + startIndexing.Await(); writer.AddDocument(doc); } catch (Exception e) when (e.IsThrowable()) diff --git a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs index 17eef90fc7..b47301344c 100644 --- a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs @@ -10,6 +10,7 @@ using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -268,7 +269,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountdownEvent done = new CountdownEvent(numThreads); + CountDownLatch done = new CountDownLatch(numThreads); AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens @@ -284,7 +285,7 @@ public virtual void TestStressMultiThreading() { t.Start(); } - done.Wait(); + done.Await(); writer.Dispose(); DirectoryReader reader = DirectoryReader.Open(dir); @@ -326,12 +327,12 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter writer; private readonly int numDocs; - private readonly CountdownEvent done; + private readonly CountDownLatch done; private readonly AtomicInt32 numUpdates; private readonly string f; private readonly string cf; - public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountdownEvent done, AtomicInt32 numUpdates, string f, string cf) + public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountDownLatch done, AtomicInt32 numUpdates, string f, string cf) : base(str) { this.writer = writer; @@ -440,7 +441,7 @@ public override void Run() } } } - done.Signal(); + done.CountDown(); } } } diff --git a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs index ee4cb364f1..e1d0fe82da 100644 --- a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs @@ -10,6 +10,7 @@ using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -1139,7 +1140,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountdownEvent done = new CountdownEvent(numThreads); + CountDownLatch done = new CountDownLatch(numThreads); AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens @@ -1155,7 +1156,7 @@ public virtual void TestStressMultiThreading() { t.Start(); } - done.Wait(); + done.Await(); writer.Dispose(); DirectoryReader reader = DirectoryReader.Open(dir); @@ -1191,12 +1192,12 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter writer; private readonly int numDocs; - private readonly CountdownEvent done; + private readonly CountDownLatch done; private readonly AtomicInt32 numUpdates; private readonly string f; private readonly string cf; - public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountdownEvent done, AtomicInt32 numUpdates, string f, string cf) + public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountDownLatch done, AtomicInt32 numUpdates, string f, string cf) : base(str) { this.writer = writer; @@ -1303,7 +1304,7 @@ public override void Run() } } } - done.Signal(); + done.CountDown(); } } } diff --git a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs index 4dfbf16be0..4ac304d831 100644 --- a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs +++ b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Search { @@ -230,7 +231,7 @@ public virtual void TestHashCodeWithThreads() { queries[i] = new AutomatonQuery(new Term("bogus", "bogus"), AutomatonTestUtil.RandomAutomaton(Random)); } - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); int numThreads = TestUtil.NextInt32(Random, 2, 5); ThreadJob[] threads = new ThreadJob[numThreads]; for (int threadID = 0; threadID < numThreads; threadID++) @@ -239,7 +240,7 @@ public virtual void TestHashCodeWithThreads() threads[threadID] = thread; thread.Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob thread in threads) { thread.Join(); @@ -249,9 +250,9 @@ public virtual void TestHashCodeWithThreads() private sealed class ThreadAnonymousClass : ThreadJob { private readonly AutomatonQuery[] queries; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; - public ThreadAnonymousClass(AutomatonQuery[] queries, CountdownEvent startingGun) + public ThreadAnonymousClass(AutomatonQuery[] queries, CountDownLatch startingGun) { this.queries = queries; this.startingGun = startingGun; @@ -259,7 +260,7 @@ public ThreadAnonymousClass(AutomatonQuery[] queries, CountdownEvent startingGun public override void Run() { - startingGun.Wait(); + startingGun.Await(); for (int i = 0; i < queries.Length; i++) { _ = queries[i].GetHashCode(); // LUCENENET: using discard variable to prevent warning about ignoring return value diff --git a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs index 4cd4f816d6..b07c1f999e 100644 --- a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs +++ b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs @@ -396,8 +396,8 @@ public virtual void TestThreadStarvationNoDeleteNRTReader() IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)); conf.SetMergePolicy(Random.NextBoolean() ? NoMergePolicy.COMPOUND_FILES : NoMergePolicy.NO_COMPOUND_FILES); Directory d = NewDirectory(); - CountdownEvent latch = new CountdownEvent(1); - CountdownEvent signal = new CountdownEvent(1); + CountDownLatch latch = new CountDownLatch(1); + CountDownLatch signal = new CountDownLatch(1); LatchedIndexWriter _writer = new LatchedIndexWriter(d, conf, latch, signal); TrackingIndexWriter writer = new TrackingIndexWriter(_writer); @@ -450,12 +450,12 @@ public virtual void TestThreadStarvationNoDeleteNRTReader() private sealed class ThreadAnonymousClass : ThreadJob { - private readonly CountdownEvent latch; - private readonly CountdownEvent signal; + private readonly CountDownLatch latch; + private readonly CountDownLatch signal; private readonly TrackingIndexWriter writer; private readonly SearcherManager manager; - public ThreadAnonymousClass(CountdownEvent latch, CountdownEvent signal, TrackingIndexWriter writer, SearcherManager manager) + public ThreadAnonymousClass(CountDownLatch latch, CountDownLatch signal, TrackingIndexWriter writer, SearcherManager manager) { this.latch = latch; this.signal = signal; @@ -467,7 +467,7 @@ public override void Run() { try { - signal.Wait(); + signal.Await(); manager.MaybeRefresh(); writer.DeleteDocuments(new TermQuery(new Term("foo", "barista"))); manager.MaybeRefresh(); // kick off another reopen so we inc. the internal gen @@ -478,7 +478,7 @@ public override void Run() } finally { - latch.Reset(latch.CurrentCount == 0 ? 0 : latch.CurrentCount - 1); // let the add below finish + latch.CountDown(); // let the add below finish } } } @@ -513,11 +513,11 @@ public override void Run() public class LatchedIndexWriter : IndexWriter { - internal CountdownEvent latch; + internal CountDownLatch latch; internal bool waitAfterUpdate = false; - internal CountdownEvent signal; + internal CountDownLatch signal; - public LatchedIndexWriter(Directory d, IndexWriterConfig conf, CountdownEvent latch, CountdownEvent signal) + internal LatchedIndexWriter(Directory d, IndexWriterConfig conf, CountDownLatch latch, CountDownLatch signal) : base(d, conf) { this.latch = latch; @@ -532,8 +532,8 @@ public override void UpdateDocument(Term term, IEnumerable doc, { if (waitAfterUpdate) { - signal.Reset(signal.CurrentCount == 0 ? 0 : signal.CurrentCount - 1); - latch.Wait(); + signal.CountDown(); + latch.Await(); } } catch (Exception ie) when (ie.IsInterruptedException()) diff --git a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs index c6a46fd48f..d0287ac32f 100644 --- a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs +++ b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using System.Threading; +using Lucene.Net.Support.Threading; using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; @@ -68,7 +69,7 @@ public virtual void Test() Console.WriteLine(numThreads + " threads"); } - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); IList threads = new JCG.List(); int iters = AtLeast(1000); @@ -87,7 +88,7 @@ public virtual void Test() thread.Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob thread in threads) { @@ -140,7 +141,7 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly SearcherManager mgr; private readonly int? missing; private readonly LiveFieldValues rt; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; private readonly int iters; private readonly int idCount; private readonly double reopenChance; @@ -150,7 +151,7 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly int threadID; private readonly Random threadRandom; - public ThreadAnonymousClass(IndexWriter w, SearcherManager mgr, int? missing, LiveFieldValues rt, CountdownEvent startingGun, int iters, int idCount, double reopenChance, double deleteChance, double addChance, int t, int threadID, Random threadRandom) + public ThreadAnonymousClass(IndexWriter w, SearcherManager mgr, int? missing, LiveFieldValues rt, CountDownLatch startingGun, int iters, int idCount, double reopenChance, double deleteChance, double addChance, int t, int threadID, Random threadRandom) { this.w = w; this.mgr = mgr; @@ -174,7 +175,7 @@ public override void Run() IDictionary values = new JCG.Dictionary(); IList allIDs = new SynchronizedList(); - startingGun.Wait(); + startingGun.Await(); for (int iter = 0; iter < iters; iter++) { // Add/update a document diff --git a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs index 08475ee562..b0de6eeaa9 100644 --- a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs +++ b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs @@ -6,6 +6,7 @@ using System.Threading; using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; +using Lucene.Net.Support.Threading; namespace Lucene.Net.Search { @@ -91,7 +92,7 @@ public virtual void Test() if (answers.Count > 0) { - CountdownEvent startingGun = new CountdownEvent(1); + CountDownLatch startingGun = new CountDownLatch(1); int numThreads = TestUtil.NextInt32(Random, 2, 5); ThreadJob[] threads = new ThreadJob[numThreads]; for (int threadID = 0; threadID < numThreads; threadID++) @@ -100,7 +101,7 @@ public virtual void Test() threads[threadID] = thread; thread.Start(); } - startingGun.Signal(); + startingGun.CountDown(); foreach (ThreadJob thread in threads) { thread.Join(); @@ -114,9 +115,9 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexSearcher s; private readonly IDictionary answers; - private readonly CountdownEvent startingGun; + private readonly CountDownLatch startingGun; - public ThreadAnonymousClass(IndexSearcher s, IDictionary answers, CountdownEvent startingGun) + public ThreadAnonymousClass(IndexSearcher s, IDictionary answers, CountDownLatch startingGun) { this.s = s; this.answers = answers; @@ -127,7 +128,7 @@ public override void Run() { try { - startingGun.Wait(); + startingGun.Await(); for (int i = 0; i < 20; i++) { IList> shuffled = new JCG.List>(answers); diff --git a/src/Lucene.Net.Tests/Search/TestSearcherManager.cs b/src/Lucene.Net.Tests/Search/TestSearcherManager.cs index f2b7abefe5..44b61fae3d 100644 --- a/src/Lucene.Net.Tests/Search/TestSearcherManager.cs +++ b/src/Lucene.Net.Tests/Search/TestSearcherManager.cs @@ -272,8 +272,8 @@ public virtual void TestIntermediateClose() IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetMergeScheduler(new ConcurrentMergeScheduler())); writer.AddDocument(new Document()); writer.Commit(); - CountdownEvent awaitEnterWarm = new CountdownEvent(1); - CountdownEvent awaitClose = new CountdownEvent(1); + CountDownLatch awaitEnterWarm = new CountDownLatch(1); + CountDownLatch awaitClose = new CountDownLatch(1); AtomicBoolean triedReopen = new AtomicBoolean(false); //TaskScheduler es = Random().NextBoolean() ? null : Executors.newCachedThreadPool(new NamedThreadFactory("testIntermediateClose")); TaskScheduler es = Random.NextBoolean() ? null : TaskScheduler.Default; @@ -302,13 +302,13 @@ public virtual void TestIntermediateClose() { Console.WriteLine("THREAD started"); } - awaitEnterWarm.Wait(); + awaitEnterWarm.Await(); if (Verbose) { Console.WriteLine("NOW call close"); } searcherManager.Dispose(); - awaitClose.Signal(); + awaitClose.CountDown(); thread.Join(); try { @@ -333,12 +333,12 @@ public virtual void TestIntermediateClose() private sealed class SearcherFactoryAnonymousClass2 : SearcherFactory { - private readonly CountdownEvent awaitEnterWarm; - private readonly CountdownEvent awaitClose; + private readonly CountDownLatch awaitEnterWarm; + private readonly CountDownLatch awaitClose; private readonly AtomicBoolean triedReopen; private readonly TaskScheduler es; - public SearcherFactoryAnonymousClass2(CountdownEvent awaitEnterWarm, CountdownEvent awaitClose, AtomicBoolean triedReopen, TaskScheduler es) + public SearcherFactoryAnonymousClass2(CountDownLatch awaitEnterWarm, CountDownLatch awaitClose, AtomicBoolean triedReopen, TaskScheduler es) { this.awaitEnterWarm = awaitEnterWarm; this.awaitClose = awaitClose; @@ -352,8 +352,8 @@ public override IndexSearcher NewSearcher(IndexReader r) { if (triedReopen) { - awaitEnterWarm.Signal(); - awaitClose.Wait(); + awaitEnterWarm.CountDown(); + awaitClose.Await(); } } catch (Exception e) when (e.IsInterruptedException()) From 10abb862e8cdf4988d95026d1e2e89d9a013e4d6 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Fri, 29 May 2026 08:41:42 -0600 Subject: [PATCH 08/20] Address PR feedback --- .../ByTask/Tasks/CloseIndexTask.cs | 2 + .../Analysis/BaseTokenStreamTestCase.cs | 4 +- .../Index/BaseDocValuesFormatTestCase.cs | 6 +- .../ThreadedIndexingAndSearchingTestCase.cs | 2 + src/Lucene.Net.Tests/Index/TestAddIndexes.cs | 2 + .../Index/TestBackwardsCompatibility.cs | 6 + .../Index/TestBackwardsCompatibility3x.cs | 6 + .../Index/TestBagOfPositions.cs | 2 +- .../Index/TestBinaryDocValuesUpdates.cs | 2 +- .../Index/TestConcurrentMergeScheduler.cs | 4 + src/Lucene.Net.Tests/Index/TestCrash.cs | 2 + .../Index/TestDocValuesIndexing.cs | 2 +- .../Index/TestDocValuesWithThreads.cs | 6 +- .../Index/TestDocumentsWriterStallControl.cs | 2 +- .../Index/TestFlushByRamOrCountsPolicy.cs | 2 + src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 6 +- .../Index/TestIndexWriterMerging.cs | 2 + .../Index/TestIndexWriterOnDiskFull.cs | 4 + .../Index/TestIndexWriterReader.cs | 2 + .../Index/TestIndexWriterThreadsToSegments.cs | 2 +- .../Index/TestIndexWriterWithThreads.cs | 10 ++ .../Index/TestMixedDocValuesUpdates.cs | 2 +- .../Index/TestNumericDocValuesUpdates.cs | 2 +- .../Search/TestAutomatonQuery.cs | 2 +- .../TestControlledRealTimeReopenThread.cs | 4 +- .../Search/TestSameScoresWithThreads.cs | 4 +- .../Support/Threading/CountDownLatchTest.cs | 57 ++++----- src/Lucene.Net/Index/IndexWriter.cs | 116 +++++++++--------- .../Support/Threading/CountDownLatch.cs | 76 +++++++----- 29 files changed, 198 insertions(+), 141 deletions(-) diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs index 72e3f101f2..a3677dab51 100644 --- a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs +++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs @@ -47,7 +47,9 @@ public override int DoLogic() { infoStream.Dispose(); } +#pragma warning disable 612, 618 iw.Dispose(doWait); +#pragma warning restore 612, 618 RunData.IndexWriter = null; } return 1; diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index 4ce6d420c5..1163251f7f 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -5,6 +5,7 @@ using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Support; +using Lucene.Net.Support.Threading; using Lucene.Net.Util; using RandomizedTesting.Generators; using System; @@ -17,7 +18,6 @@ using Attribute = Lucene.Net.Util.Attribute; using Directory = Lucene.Net.Store.Directory; using JCG = J2N.Collections.Generic; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Analysis { @@ -711,7 +711,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in // now test with multiple threads: note we do the EXACT same thing we did before in each thread, // so this should only really fail from another thread if its an actual thread problem int numThreads = TestUtil.NextInt32(random, 2, 4); - var startingGun = new CountDownLatch(1); + using var startingGun = new CountDownLatch(1); var threads = new AnalysisThread[numThreads]; for (int i = 0; i < threads.Length; i++) { diff --git a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs index e4aca4de40..1da122feb7 100644 --- a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs @@ -8,6 +8,7 @@ using Lucene.Net.Search; using Lucene.Net.Store; using Lucene.Net.Support; +using Lucene.Net.Support.Threading; using Lucene.Net.Util; using RandomizedTesting.Generators; using System; @@ -18,7 +19,6 @@ using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; using Test = NUnit.Framework.TestAttribute; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -3244,7 +3244,7 @@ public virtual void TestThreads() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - CountDownLatch startingGun = new CountDownLatch(1); + using CountDownLatch startingGun = new CountDownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass(ir, startingGun); @@ -3380,7 +3380,7 @@ public virtual void TestThreads2() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - CountDownLatch startingGun = new CountDownLatch(1); + using CountDownLatch startingGun = new CountDownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass2(ir, startingGun); diff --git a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs index 28b0e4b119..cf99b3b78d 100644 --- a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs @@ -772,7 +772,9 @@ public virtual void RunTest(string testName) assertEquals("index=" + m_writer.SegString() + " addCount=" + m_addCount + " delCount=" + m_delCount, m_addCount - m_delCount, m_writer.NumDocs); DoClose(); +#pragma warning disable 612, 618 m_writer.Dispose(false); +#pragma warning restore 612, 618 // Cannot shutdown until after writer is closed because // writer has merged segment warmer that uses IS to run diff --git a/src/Lucene.Net.Tests/Index/TestAddIndexes.cs b/src/Lucene.Net.Tests/Index/TestAddIndexes.cs index 19028c99a1..d79d81bed7 100644 --- a/src/Lucene.Net.Tests/Index/TestAddIndexes.cs +++ b/src/Lucene.Net.Tests/Index/TestAddIndexes.cs @@ -804,7 +804,9 @@ internal virtual void JoinThreads() internal virtual void Close(bool doWait) { didClose = true; +#pragma warning disable 612, 618 writer2.Dispose(doWait); +#pragma warning restore 612, 618 } internal virtual void CloseDir() diff --git a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs index ada3a788db..9ddee48986 100644 --- a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs +++ b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs @@ -313,7 +313,9 @@ public virtual void TestUnsupportedOldIndexes() // above, so close without waiting for merges. if (writer != null) { +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 } writer = null; } @@ -1050,7 +1052,9 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() { AddDoc(w, id++); } +#pragma warning disable 612, 618 w.Dispose(false); +#pragma warning restore 612, 618 } // add dummy segments (which are all in current @@ -1061,7 +1065,9 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() .SetMergePolicy(mp_); IndexWriter iw = new IndexWriter(dir, iwc_); iw.AddIndexes(ramDir); +#pragma warning disable 612, 618 iw.Dispose(false); +#pragma warning restore 612, 618 // determine count of segments in modified index int origSegCount = GetNumberOfSegments(dir); diff --git a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs index 413bfa91e2..214c37e3d0 100644 --- a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs +++ b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs @@ -244,7 +244,9 @@ public virtual void TestUnsupportedOldIndexes() // above, so close without waiting for merges. if (writer != null) { +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 } writer = null; } @@ -955,7 +957,9 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() { AddDoc(w, id++); } +#pragma warning disable 612, 618 w.Dispose(false); +#pragma warning restore 612, 618 } // add dummy segments (which are all in current @@ -965,7 +969,9 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() .SetMergePolicy(mp_); IndexWriter w_ = new IndexWriter(dir, iwc_); w_.AddIndexes(ramDir); +#pragma warning disable 612, 618 w_.Dispose(false); +#pragma warning restore 612, 618 // determine count of segments in modified index int origSegCount = GetNumberOfSegments(dir); diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs index aad88c4cbd..5b211d9c1d 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs @@ -10,8 +10,8 @@ using System.Globalization; using System.Text; using System.Threading; -using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; +using JCG = J2N.Collections.Generic; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs index d4ee105c02..c88c08ff6a 100644 --- a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs @@ -10,6 +10,7 @@ using Lucene.Net.Search; using Lucene.Net.Store; using Lucene.Net.Support; +using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; @@ -18,7 +19,6 @@ using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs index 2afbbd752f..958f05a25c 100644 --- a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs +++ b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs @@ -261,7 +261,9 @@ public virtual void TestNoWaitClose() writer.AddDocument(doc); writer.Commit(); +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 IndexReader reader = DirectoryReader.Open(directory); Assert.AreEqual((1 + iter) * 182, reader.NumDocs); @@ -316,7 +318,9 @@ public virtual void TestMaxMergeCount() w.AddDocument(doc); } } +#pragma warning disable 612, 618 w.Dispose(false); +#pragma warning restore 612, 618 dir.Dispose(); } diff --git a/src/Lucene.Net.Tests/Index/TestCrash.cs b/src/Lucene.Net.Tests/Index/TestCrash.cs index 55cb6cb789..4efcd9e74a 100644 --- a/src/Lucene.Net.Tests/Index/TestCrash.cs +++ b/src/Lucene.Net.Tests/Index/TestCrash.cs @@ -210,7 +210,9 @@ public virtual void TestCrashAfterCloseNoWait() IndexWriter writer = InitIndex(Random, false); MockDirectoryWrapper dir = (MockDirectoryWrapper)writer.Directory; +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 dir.Crash(); diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs index 9e4f2a4aa5..36af357d2d 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs @@ -3,11 +3,11 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Search; +using Lucene.Net.Support.Threading; using NUnit.Framework; using System; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs index 7312de27ee..7c72c077e3 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs @@ -2,14 +2,14 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Search; +using Lucene.Net.Support.Threading; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.Threading; -using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; -using RandomizedTesting.Generators; -using Lucene.Net.Support.Threading; +using JCG = J2N.Collections.Generic; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs index cd870bc6c3..abe3614032 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs @@ -1,12 +1,12 @@ using J2N.Threading; using J2N.Threading.Atomic; +using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs b/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs index df98295767..a462f57ead 100644 --- a/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs +++ b/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs @@ -314,7 +314,9 @@ public virtual void TestStallControl() Assert.IsTrue(docsWriter.flushControl.stallControl.WasStalled); } AssertActiveBytesAfter(flushControl); +#pragma warning disable 612, 618 writer.Dispose(true); +#pragma warning restore 612, 618 dir.Dispose(); } } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 3349b2c587..549e972028 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -10,6 +10,7 @@ using Lucene.Net.Index.Extensions; using Lucene.Net.Search; using Lucene.Net.Support; +using Lucene.Net.Support.Threading; using Lucene.Net.Util; using NUnit.Framework; using RandomizedTesting.Generators; @@ -20,7 +21,6 @@ using System.Text; using System.Threading; using JCG = J2N.Collections.Generic; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { @@ -3225,7 +3225,7 @@ public override void Run() iw.Commit(); finishCommit.CountDown(); } - catch (IOException ioe) + catch (Exception ioe) when (ioe.IsIOException()) { throw RuntimeException.Create(ioe); } @@ -3254,7 +3254,7 @@ public virtual void TestDisposeDoesNotLeakWriteLockOnCommitFailure() writer.Dispose(); Assert.Fail("expected IOException from Dispose"); } - catch (IOException expected) when (expected.Message != null && expected.Message.Contains("on purpose")) + catch (Exception expected) when (expected.IsIOException() && expected.Message != null && expected.Message.Contains("on purpose")) { // expected } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs index 4bf92322cc..7f81d04358 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs @@ -433,7 +433,9 @@ public virtual void TestNoWaitClose() t1.Start(); +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 t1.Join(); // Make sure reader can read diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs index 8584cfca20..ba7e316352 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs @@ -664,7 +664,9 @@ public virtual void TestImmediateDiskFull() } try { +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 Assert.Fail("did not hit disk full"); } catch (Exception ioe) when (ioe.IsIOException()) @@ -674,7 +676,9 @@ public virtual void TestImmediateDiskFull() // Make sure once disk space is avail again, we can // cleanly close: dir.MaxSizeInBytes = 0; +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 dir.Dispose(); } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs index 1e11288c91..e5b689dd43 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs @@ -515,7 +515,9 @@ internal virtual void Close(bool doWait) { mainWriter.WaitForMerges(); } +#pragma warning disable 612, 618 mainWriter.Dispose(doWait); +#pragma warning restore 612, 618 } internal virtual void CloseDir() diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs index d0551718a7..4f0ab5d55f 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -6,6 +6,7 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Store; +using Lucene.Net.Support.Threading; using Lucene.Net.Util; using NUnit.Framework; using RandomizedTesting.Generators; @@ -13,7 +14,6 @@ using System.Collections.Generic; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs index 858698f9f9..d1af9e2ab2 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs @@ -197,7 +197,9 @@ public virtual void TestImmediateDiskFullWithThreads() // Make sure once disk space is avail again, we can // cleanly close: dir.MaxSizeInBytes = 0; +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 dir.Dispose(); } } @@ -267,7 +269,9 @@ public virtual void TestCloseWithThreads() { Console.WriteLine("\nTEST: now close"); } +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 // Make sure threads that are adding docs are not hung: for (int i = 0; i < NUM_THREADS; i++) @@ -343,13 +347,17 @@ public virtual void TestMultipleThreadsFailure(Failure failure) bool success = false; try { +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 success = true; } catch (Exception ioe) when (ioe.IsIOException()) { failure.ClearDoFail(); +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 } if (Verbose) { @@ -408,7 +416,9 @@ public virtual void TestSingleThreadFailure(Failure failure) } failure.ClearDoFail(); writer.AddDocument(doc); +#pragma warning disable 612, 618 writer.Dispose(false); +#pragma warning restore 612, 618 dir.Dispose(); } diff --git a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs index b47301344c..c1cdaeac69 100644 --- a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs @@ -3,6 +3,7 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Support; +using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; @@ -10,7 +11,6 @@ using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs index e1d0fe82da..d93f969e1c 100644 --- a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs @@ -3,6 +3,7 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Support; +using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; @@ -10,7 +11,6 @@ using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs index 4ac304d831..3ebb4ccadf 100644 --- a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs +++ b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs @@ -1,10 +1,10 @@ using J2N.Threading; using Lucene.Net.Documents; +using Lucene.Net.Support.Threading; using NUnit.Framework; using System; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Search { diff --git a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs index b07c1f999e..f8a329c8ed 100644 --- a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs +++ b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs @@ -1,6 +1,7 @@ using J2N.Threading; using J2N.Threading.Atomic; using Lucene.Net.Analysis.Standard; +using Lucene.Net.Attributes; using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Store; @@ -15,9 +16,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; -using Lucene.Net.Attributes; +using JCG = J2N.Collections.Generic; namespace Lucene.Net.Search { diff --git a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs index b0de6eeaa9..796afb2af5 100644 --- a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs +++ b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs @@ -1,12 +1,12 @@ using J2N.Collections.Generic.Extensions; using J2N.Threading; +using Lucene.Net.Support.Threading; using NUnit.Framework; using System; using System.Collections.Generic; using System.Threading; -using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; -using Lucene.Net.Support.Threading; +using JCG = J2N.Collections.Generic; namespace Lucene.Net.Search { diff --git a/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs b/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs index eae7309b9b..a0dc5b74df 100644 --- a/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs +++ b/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs @@ -1,23 +1,5 @@ // Some code adapted from Apache Harmony: https://github.com/apache/harmony/blob/02970cb7227a335edd2c8457ebdde0195a735733/classlib/modules/concurrent/src/test/java/CountDownLatchTest.java -#region Copyright 2010 by Apache Harmony, Licensed under the Apache License, Version 2.0 -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#endregion - using J2N.Threading; using Lucene.Net.Support.Threading; using NUnit.Framework; @@ -138,9 +120,16 @@ public ThreadAnonymousClassForTestAwait(CountDownLatchTest outerInstance, CountD public override void Run() { - outerInstance.threadAssertTrue(l.Count > 0); - l.Await(); - outerInstance.threadAssertTrue(l.Count == 0); + try + { + outerInstance.threadAssertTrue(l.Count > 0); + l.Await(); + outerInstance.threadAssertTrue(l.Count == 0); + } + catch (Exception e) when (e.IsInterruptedException()) + { + outerInstance.threadUnexpectedException(); + } } } @@ -183,8 +172,15 @@ public ThreadAnonymousClassForTestTimedAwait(CountDownLatchTest outerInstance, C public override void Run() { - outerInstance.threadAssertTrue(l.Count > 0); - outerInstance.threadAssertTrue(l.Await(TimeSpan.FromMilliseconds(SMALL_DELAY_MS))); + try + { + outerInstance.threadAssertTrue(l.Count > 0); + outerInstance.threadAssertTrue(l.Await(TimeSpan.FromMilliseconds(SMALL_DELAY_MS))); + } + catch (Exception e) when (e.IsInterruptedException()) + { + outerInstance.threadUnexpectedException(); + } } } @@ -192,7 +188,6 @@ public override void Run() * await throws IE if interrupted before counted down */ [Test] - [Ignore("LUCENENET: CountDownLatch.Await() does not honor Thread.Interrupt() because Lucene.NET does not support Java-style thread interrupts. See the LUCENENET note on CountDownLatch.Await() for details.")] public void TestAwait_InterruptedException() { CountDownLatch l = new CountDownLatch(1); @@ -240,7 +235,6 @@ public override void Run() * timed await throws IE if interrupted before counted down */ [Test] - [Ignore("LUCENENET: CountDownLatch.Await(TimeSpan) does not honor Thread.Interrupt() because Lucene.NET does not support Java-style thread interrupts. See the LUCENENET note on CountDownLatch.Await() for details.")] public void TestTimedAwait_InterruptedException() { CountDownLatch l = new CountDownLatch(1); @@ -318,9 +312,16 @@ public ThreadAnonymousClassForTestAwaitTimeout(CountDownLatchTest outerInstance, public override void Run() { - outerInstance.threadAssertTrue(l.Count > 0); - outerInstance.threadAssertFalse(l.Await(TimeSpan.FromMilliseconds(SHORT_DELAY_MS))); - outerInstance.threadAssertTrue(l.Count > 0); + try + { + outerInstance.threadAssertTrue(l.Count > 0); + outerInstance.threadAssertFalse(l.Await(TimeSpan.FromMilliseconds(SHORT_DELAY_MS))); + outerInstance.threadAssertTrue(l.Count > 0); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + outerInstance.threadUnexpectedException(); + } } } diff --git a/src/Lucene.Net/Index/IndexWriter.cs b/src/Lucene.Net/Index/IndexWriter.cs index 3ea8474ba9..b6f9c3fd12 100644 --- a/src/Lucene.Net/Index/IndexWriter.cs +++ b/src/Lucene.Net/Index/IndexWriter.cs @@ -1040,29 +1040,75 @@ private void MessageState() } } + /// + /// Gracefully closes (commits, waits for merges), but calls rollback + /// if there's an exc so the IndexWriter is always closed. + /// + // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released in + // 4.10.0) to fix #1284. Previously the close path's straight-line cleanup leaked + // the reader pool, deleter, and write.lock when CommitInternal threw an I/O exception. + private void Shutdown(bool waitForMerges) + { + if (pendingCommit != null) + { + throw IllegalStateException.Create("cannot close: prepareCommit was already called with no corresponding call to commit"); + } + // Ensure that only one thread actually gets to do the + // closing + if (ShouldClose()) + { + bool success = false; + try + { + if (infoStream.IsEnabled("IW")) + { + infoStream.Message("IW", "now flush at close"); + } + Flush(true, true); + FinishMerges(waitForMerges); + CommitInternal(); + RollbackInternal(); // ie close, since we just committed + success = true; + } + finally + { + if (success == false) + { + // Be certain to close the index on any exception + try + { + RollbackInternal(); + } + catch (Exception t) when (t.IsThrowable()) + { + // Suppress so we keep throwing original exception + } + } + } + } + } + /// /// Commits all changes to an index, waits for pending merges /// to complete, closes all associated files and releases the /// write lock. - /// - /// Note that: + /// + /// Note that: /// /// If you called but failed to call , this /// method will throw and the - /// will not be closed. + /// will not be disposed. /// If this method throws any other exception, the - /// will be closed, but changes may have been lost. + /// will be disposed, but changes may have been lost. /// - /// - /// - /// + /// /// Note that this may be a costly /// operation, so, try to re-use a single writer instead of /// closing and opening a new one. See for - /// caveats about write caching done by some IO devices. - /// - /// NOTE: You must ensure no other threads are still making - /// changes at the same time that this method is invoked. + /// caveats about write caching done by some IO devices. + /// + /// NOTE: You must ensure no other threads are still making + /// changes at the same time that this method is invoked. /// /// if there is a low-level IO error // LUCENENET: doc-comment updated to match upstream LUCENE-5871 (commit 2cfcdcc, first @@ -1179,54 +1225,6 @@ protected virtual void Dispose(bool disposing, bool waitForMerges) Shutdown(waitForMerges); } - /// - /// Gracefully closes (commits, waits for merges), but calls rollback - /// if there's an exc so the IndexWriter is always closed. - /// - // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released in - // 4.10.0) to fix #1284. Previously the close path's straight-line cleanup leaked - // the reader pool, deleter, and write.lock when CommitInternal threw an I/O exception. - private void Shutdown(bool waitForMerges) - { - if (pendingCommit != null) - { - throw IllegalStateException.Create("cannot close: prepareCommit was already called with no corresponding call to commit"); - } - // Ensure that only one thread actually gets to do the - // closing - if (ShouldClose()) - { - bool success = false; - try - { - if (infoStream.IsEnabled("IW")) - { - infoStream.Message("IW", "now flush at close"); - } - Flush(true, true); - FinishMerges(waitForMerges); - CommitInternal(); - RollbackInternal(); // ie close, since we just committed - success = true; - } - finally - { - if (success == false) - { - // Be certain to close the index on any exception - try - { - RollbackInternal(); - } - catch (Exception t) when (t.IsThrowable()) - { - // Suppress so we keep throwing original exception - } - } - } - } - } - // LUCENENET: upstream 2cfcdcc had private assertEventQueueAfterClose() between // close(boolean) and shouldClose() (and closeInternal(boolean, boolean) immediately // after shouldClose()). The LUCENE-5871 rewrite of close(boolean) stranded both as diff --git a/src/Lucene.Net/Support/Threading/CountDownLatch.cs b/src/Lucene.Net/Support/Threading/CountDownLatch.cs index ed7b2368b4..b159d06f3b 100644 --- a/src/Lucene.Net/Support/Threading/CountDownLatch.cs +++ b/src/Lucene.Net/Support/Threading/CountDownLatch.cs @@ -1,23 +1,4 @@ // Some code adapted from Apache Harmony: https://github.com/apache/harmony/blob/02970cb7227a335edd2c8457ebdde0195a735733/classlib/modules/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java - -#region Copyright 2010 by Apache Harmony, Licensed under the Apache License, Version 2.0 -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#endregion - // Other aspects adapted from .NET's CountdownEvent: https://github.com/dotnet/runtime/blob/38496302e54e1b6fb11a998b297492f3fdfbfd0c/src/libraries/System.Threading/src/System/Threading/CountdownEvent.cs using System; @@ -73,7 +54,14 @@ namespace Lucene.Net.Support.Threading /// can be called any number of times after the count reaches zero without /// throwing. This matches Java's CountDownLatch.countDown() semantics. /// - internal class CountDownLatch + /// + /// LUCENENET specific: Unlike Java's java.util.concurrent.CountDownLatch, + /// which is not AutoCloseable, this type implements + /// because it holds an unmanaged synchronization handle that may be lazily allocated + /// the first time a waiter blocks. Callers should dispose instances when they are no + /// longer needed, typically via a using statement. + /// + internal class CountDownLatch : IDisposable { // LUCENENET: the current count and ManualResetEventSlim for signaling, instead of Java's Sync class private volatile int _currentCount; @@ -98,22 +86,25 @@ public CountDownLatch(int count) /// /// Causes the current thread to wait until the latch has counted down to - /// zero. + /// zero, unless the thread is interrupted. /// /// If the current count is zero then this method returns immediately. /// /// If the current count is greater than zero then the current /// thread becomes disabled for thread scheduling purposes and lies - /// dormant until the count reaches zero due to invocations of the - /// method. + /// dormant until one of two things happen: + /// + /// The count reaches zero due to invocations of the + /// method; or + /// Some other thread interrupts the current thread. + /// /// - /// LUCENENET specific: Unlike Java's await(), which throws - /// InterruptedException if the thread is interrupted while - /// waiting, this overload blocks unconditionally until the count - /// reaches zero. Callers that need cancellation/interrupt support - /// should use the timeout overload or a higher-level coordination - /// primitive. + /// If the current thread is interrupted while waiting, a + /// is thrown by the underlying + /// . /// + /// if the current thread is + /// interrupted while waiting. public void Await() { _event.Wait(Timeout.Infinite); @@ -121,23 +112,29 @@ public void Await() /// /// Causes the current thread to wait until the latch has counted down to - /// zero, or the specified waiting time elapses. + /// zero, unless the thread is interrupted, or the specified waiting time + /// elapses. /// /// If the current count is zero then this method returns immediately /// with the value true. /// /// If the current count is greater than zero then the current /// thread becomes disabled for thread scheduling purposes and lies - /// dormant until one of two things happen: + /// dormant until one of three things happen: /// /// The count reaches zero due to invocations of the /// method; or + /// Some other thread interrupts the current thread; or /// The specified waiting time elapses. /// /// /// If the count reaches zero then the method returns with the /// value true. /// + /// If the current thread is interrupted while waiting, a + /// is thrown by the underlying + /// . + /// /// If the specified waiting time elapses then the value false /// is returned. If the time is less than or equal to zero, the method /// will not wait at all. @@ -145,6 +142,8 @@ public void Await() /// The maximum time to wait. /// true if the count reached zero, or false /// if the waiting time elapsed before the count reached zero. + /// if the current thread is + /// interrupted while waiting. public bool Await(TimeSpan timeout) { return _event.Wait(timeout); @@ -201,5 +200,20 @@ public void CountDown() /// /// a string identifying this latch, as well as its state public override string ToString() => $"{base.ToString()}[Count = {_currentCount}]"; + + /// + /// Releases the unmanaged synchronization resources held by this latch. + /// + /// + /// LUCENENET specific: Java's CountDownLatch is not AutoCloseable + /// and does not need to be closed. This .NET port may hold an unmanaged + /// synchronization handle that is lazily allocated the first time a waiter + /// blocks, so it must be disposed when no longer needed. + /// + public void Dispose() + { + _event.Dispose(); + GC.SuppressFinalize(this); + } } } From f0f0167879285815adef517cd52d321681bb9ad0 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Fri, 29 May 2026 10:54:54 -0600 Subject: [PATCH 09/20] Rename CountDownLatch to CountdownLatch with Wait/Signal methods, #1284 Align the latch's naming with .NET's CountdownEvent: rename the type CountDownLatch -> CountdownLatch, Await()/Await(TimeSpan) -> Wait()/Wait(TimeSpan), and CountDown() -> Signal(). Upstream-Java references in comments are left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Analysis/BaseTokenStreamTestCase.cs | 10 +- .../Index/BaseDocValuesFormatTestCase.cs | 20 ++-- .../Index/TestBagOfPositions.cs | 10 +- .../Index/TestBagOfPostings.cs | 10 +- .../Index/TestBinaryDocValuesUpdates.cs | 10 +- .../Index/TestConcurrentMergeScheduler.cs | 10 +- .../Index/TestDocValuesIndexing.cs | 10 +- .../Index/TestDocValuesWithThreads.cs | 10 +- .../Index/TestDocumentsWriterDeleteQueue.cs | 10 +- .../Index/TestDocumentsWriterStallControl.cs | 32 ++--- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 40 +++---- .../Index/TestIndexWriterDelete.cs | 18 +-- .../Index/TestIndexWriterNRTIsCurrent.cs | 16 +-- .../Index/TestIndexWriterThreadsToSegments.cs | 54 ++++----- .../Index/TestIndexWriterWithThreads.cs | 16 +-- .../Index/TestMixedDocValuesUpdates.cs | 10 +- .../Index/TestNumericDocValuesUpdates.cs | 10 +- .../Search/TestAutomatonQuery.cs | 10 +- .../TestControlledRealTimeReopenThread.cs | 24 ++-- .../Search/TestLiveFieldValues.cs | 10 +- .../Search/TestSameScoresWithThreads.cs | 10 +- .../Search/TestSearcherManager.cs | 18 +-- ...DownLatchTest.cs => CountdownLatchTest.cs} | 110 +++++++++--------- .../Util/TestMaxFailuresRule.cs | 4 +- .../{CountDownLatch.cs => CountdownLatch.cs} | 48 ++++---- 25 files changed, 265 insertions(+), 265 deletions(-) rename src/Lucene.Net.Tests/Support/Threading/{CountDownLatchTest.cs => CountdownLatchTest.cs} (65%) rename src/Lucene.Net/Support/Threading/{CountDownLatch.cs => CountdownLatch.cs} (86%) diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index 1163251f7f..b55cebc24d 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -630,7 +630,7 @@ internal class AnalysisThread : ThreadJob internal readonly bool simple; internal readonly bool offsetsAreCorrect; internal readonly RandomIndexWriter iw; - private readonly CountDownLatch latch; + private readonly CountdownLatch latch; // NOTE: not volatile because we don't want the tests to // add memory barriers (ie alter how threads @@ -638,7 +638,7 @@ internal class AnalysisThread : ThreadJob public bool Failed { get; set; } public Exception FirstException { get; set; } = null; - internal AnalysisThread(long seed, CountDownLatch latch, Analyzer a, int iterations, int maxWordLength, + internal AnalysisThread(long seed, CountdownLatch latch, Analyzer a, int iterations, int maxWordLength, bool useCharFilter, bool simple, bool offsetsAreCorrect, RandomIndexWriter iw) { this.seed = seed; @@ -657,7 +657,7 @@ public override void Run() bool success = false; try { - if (latch != null) latch.Await(); + if (latch != null) latch.Wait(); // see the part in checkRandomData where it replays the same text again // to verify reproducability/reuse: hopefully this would catch thread hazards. CheckRandomData(new J2N.Randomizer(seed), a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw); @@ -711,7 +711,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in // now test with multiple threads: note we do the EXACT same thing we did before in each thread, // so this should only really fail from another thread if its an actual thread problem int numThreads = TestUtil.NextInt32(random, 2, 4); - using var startingGun = new CountDownLatch(1); + using var startingGun = new CountdownLatch(1); var threads = new AnalysisThread[numThreads]; for (int i = 0; i < threads.Length; i++) { @@ -723,7 +723,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in thread.Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (var t in threads) { try diff --git a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs index 1da122feb7..22198db310 100644 --- a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs @@ -3244,13 +3244,13 @@ public virtual void TestThreads() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - using CountDownLatch startingGun = new CountDownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass(ir, startingGun); threads[i].Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob t in threads) { t.Join(); @@ -3260,9 +3260,9 @@ public virtual void TestThreads() private sealed class ThreadAnonymousClass : ThreadJob { private readonly DirectoryReader ir; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; - public ThreadAnonymousClass(DirectoryReader ir, CountDownLatch startingGun) + public ThreadAnonymousClass(DirectoryReader ir, CountdownLatch startingGun) { this.ir = ir; this.startingGun = startingGun; @@ -3272,7 +3272,7 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); BytesRef scratch = new BytesRef(); // LUCENENET: Moved outside of the loop for performance foreach (AtomicReaderContext context in ir.Leaves) { @@ -3380,13 +3380,13 @@ public virtual void TestThreads2() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - using CountDownLatch startingGun = new CountDownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass2(ir, startingGun); threads[i].Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob t in threads) { t.Join(); @@ -3396,9 +3396,9 @@ public virtual void TestThreads2() private sealed class ThreadAnonymousClass2 : ThreadJob { private readonly DirectoryReader ir; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; - public ThreadAnonymousClass2(DirectoryReader ir, CountDownLatch startingGun) + public ThreadAnonymousClass2(DirectoryReader ir, CountdownLatch startingGun) { this.ir = ir; this.startingGun = startingGun; @@ -3408,7 +3408,7 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); foreach (AtomicReaderContext context in ir.Leaves) { AtomicReader r = context.AtomicReader; diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs index 5b211d9c1d..feb9d39441 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs @@ -117,7 +117,7 @@ public virtual void Test() // else just positions ThreadJob[] threads = new ThreadJob[threadCount]; - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); for (int threadID = 0; threadID < threadCount; threadID++) { @@ -128,7 +128,7 @@ public virtual void Test() threads[threadID] = new ThreadAnonymousClass(maxTermsPerDoc, postings, iw, startingGun, threadRandom, document, field); threads[threadID].Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob t in threads) { t.Join(); @@ -160,12 +160,12 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly int maxTermsPerDoc; private readonly ConcurrentQueue postings; private readonly RandomIndexWriter iw; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; private readonly Random threadRandom; private readonly Document document; private readonly Field field; - public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountDownLatch startingGun, Random threadRandom, Document document, Field field) + public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountdownLatch startingGun, Random threadRandom, Document document, Field field) { this.maxTermsPerDoc = maxTermsPerDoc; this.postings = postings; @@ -180,7 +180,7 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); while (!postings.IsEmpty) { StringBuilder text = new StringBuilder(); diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs b/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs index f2720f901f..bee8e28357 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs @@ -96,14 +96,14 @@ public virtual void Test() } ThreadJob[] threads = new ThreadJob[threadCount]; - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); for (int threadID = 0; threadID < threadCount; threadID++) { threads[threadID] = new ThreadAnonymousClass(maxTermsPerDoc, postings, iw, startingGun); threads[threadID].Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob t in threads) { t.Join(); @@ -141,9 +141,9 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly int maxTermsPerDoc; private readonly ConcurrentQueue postings; private readonly RandomIndexWriter iw; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; - public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountDownLatch startingGun) + public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue postings, RandomIndexWriter iw, CountdownLatch startingGun) { this.maxTermsPerDoc = maxTermsPerDoc; this.postings = postings; @@ -158,7 +158,7 @@ public override void Run() Document document = new Document(); Field field = NewTextField("field", "", Field.Store.NO); document.Add(field); - startingGun.Await(); + startingGun.Wait(); while (!postings.IsEmpty) { StringBuilder text = new StringBuilder(); diff --git a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs index c88c08ff6a..e9483f169e 100644 --- a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs @@ -1201,7 +1201,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountDownLatch done = new CountDownLatch(numThreads); + CountdownLatch done = new CountdownLatch(numThreads); AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens @@ -1217,7 +1217,7 @@ public virtual void TestStressMultiThreading() { t.Start(); } - done.Await(); + done.Wait(); writer.Dispose(); DirectoryReader reader = DirectoryReader.Open(dir); @@ -1254,12 +1254,12 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter writer; private readonly int numDocs; - private readonly CountDownLatch done; + private readonly CountdownLatch done; private readonly AtomicInt32 numUpdates; private readonly string f; private readonly string cf; - public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountDownLatch done, AtomicInt32 numUpdates, string f, string cf) + public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountdownLatch done, AtomicInt32 numUpdates, string f, string cf) : base(str) { this.writer = writer; @@ -1366,7 +1366,7 @@ public override void Run() } } } - done.CountDown(); + done.Signal(); } } } diff --git a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs index 958f05a25c..b3e43486d9 100644 --- a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs +++ b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs @@ -289,7 +289,7 @@ public virtual void TestMaxMergeCount() int maxMergeCount = TestUtil.NextInt32(Random, 1, 5); int maxMergeThreads = TestUtil.NextInt32(Random, 1, maxMergeCount); - CountDownLatch enoughMergesWaiting = new CountDownLatch(maxMergeCount); + CountdownLatch enoughMergesWaiting = new CountdownLatch(maxMergeCount); AtomicInt32 runningMergeCount = new AtomicInt32(0); AtomicBoolean failed = new AtomicBoolean(); @@ -327,11 +327,11 @@ public virtual void TestMaxMergeCount() private sealed class ConcurrentMergeSchedulerAnonymousClass : ConcurrentMergeScheduler { private readonly int maxMergeCount; - private readonly CountDownLatch enoughMergesWaiting; + private readonly CountdownLatch enoughMergesWaiting; private readonly AtomicInt32 runningMergeCount; private readonly AtomicBoolean failed; - public ConcurrentMergeSchedulerAnonymousClass(int maxMergeCount, CountDownLatch enoughMergesWaiting, AtomicInt32 runningMergeCount, AtomicBoolean failed) + public ConcurrentMergeSchedulerAnonymousClass(int maxMergeCount, CountdownLatch enoughMergesWaiting, AtomicInt32 runningMergeCount, AtomicBoolean failed) { this.maxMergeCount = maxMergeCount; this.enoughMergesWaiting = enoughMergesWaiting; @@ -349,14 +349,14 @@ protected internal override void DoMerge(MergePolicy.OneMerge merge) try { Assert.IsTrue(count <= maxMergeCount, "count=" + count + " vs maxMergeCount=" + maxMergeCount); - enoughMergesWaiting.CountDown(); + enoughMergesWaiting.Signal(); // Stall this merge until we see exactly // maxMergeCount merges waiting while (true) { // wait for 10 milliseconds - if (enoughMergesWaiting.Await(new TimeSpan(0, 0, 0, 0, 10)) || failed) + if (enoughMergesWaiting.Wait(new TimeSpan(0, 0, 0, 0, 10)) || failed) { break; } diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs index 36af357d2d..43db8d7458 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs @@ -514,7 +514,7 @@ public virtual void TestMixedTypesDifferentThreads() Directory dir = NewDirectory(); IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); AtomicBoolean hitExc = new AtomicBoolean(); ThreadJob[] threads = new ThreadJob[3]; for (int i = 0; i < 3; i++) @@ -539,7 +539,7 @@ public virtual void TestMixedTypesDifferentThreads() threads[i].Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob t in threads) { @@ -553,11 +553,11 @@ public virtual void TestMixedTypesDifferentThreads() private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter w; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; private readonly AtomicBoolean hitExc; private readonly Document doc; - public ThreadAnonymousClass(IndexWriter w, CountDownLatch startingGun, AtomicBoolean hitExc, Document doc) + public ThreadAnonymousClass(IndexWriter w, CountdownLatch startingGun, AtomicBoolean hitExc, Document doc) { this.w = w; this.startingGun = startingGun; @@ -569,7 +569,7 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); w.AddDocument(doc); } catch (Exception iae) when (iae.IsIllegalArgumentException()) diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs index 7c72c077e3..aa02f6d7fa 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs @@ -78,7 +78,7 @@ public virtual void Test() int numThreads = TestUtil.NextInt32(Random, 2, 5); IList threads = new JCG.List(); - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); for (int t = 0; t < numThreads; t++) { Random threadRandom = new J2N.Randomizer(Random.NextInt64()); @@ -87,7 +87,7 @@ public virtual void Test() threads.Add(thread); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob thread in threads) { @@ -105,10 +105,10 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly IList sorted; private readonly int numDocs; private readonly AtomicReader ar; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; private readonly Random threadRandom; - public ThreadAnonymousClass(IList numbers, IList binary, IList sorted, int numDocs, AtomicReader ar, CountDownLatch startingGun, Random threadRandom) + public ThreadAnonymousClass(IList numbers, IList binary, IList sorted, int numDocs, AtomicReader ar, CountdownLatch startingGun, Random threadRandom) { this.numbers = numbers; this.binary = binary; @@ -128,7 +128,7 @@ public override void Run() //BinaryDocValues bdv = ar.GetBinaryDocValues("bytes"); BinaryDocValues bdv = FieldCache.DEFAULT.GetTerms(ar, "bytes", false); SortedDocValues sdv = FieldCache.DEFAULT.GetTermsIndex(ar, "sorted"); - startingGun.Await(); + startingGun.Wait(); int iters = AtLeast(1000); BytesRef scratch = new BytesRef(); BytesRef scratch2 = new BytesRef(); diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs index 91ab4ef245..372e7d733b 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs @@ -258,7 +258,7 @@ public virtual void TestStressDeleteQueue() ids[i] = Random.Next(); uniqueValues.Add(new Term("id", ids[i].ToString())); } - CountDownLatch latch = new CountDownLatch(1); + CountdownLatch latch = new CountdownLatch(1); AtomicInt32 index = new AtomicInt32(0); int numThreads = 2 + Random.Next(5); UpdateThread[] threads = new UpdateThread[numThreads]; @@ -267,7 +267,7 @@ public virtual void TestStressDeleteQueue() threads[i] = new UpdateThread(queue, index, ids, latch); threads[i].Start(); } - latch.CountDown(); + latch.Signal(); for (int i = 0; i < threads.Length; i++) { threads[i].Join(); @@ -301,9 +301,9 @@ private class UpdateThread : ThreadJob internal readonly int[] ids; internal readonly DeleteSlice slice; internal readonly BufferedUpdates deletes; - internal readonly CountDownLatch latch; + internal readonly CountdownLatch latch; - protected internal UpdateThread(DocumentsWriterDeleteQueue queue, AtomicInt32 index, int[] ids, CountDownLatch latch) + protected internal UpdateThread(DocumentsWriterDeleteQueue queue, AtomicInt32 index, int[] ids, CountdownLatch latch) { this.queue = queue; this.index = index; @@ -317,7 +317,7 @@ public override void Run() { try { - latch.Await(); + latch.Wait(); } catch (Exception ie) when (ie.IsInterruptedException()) { diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs index abe3614032..f3fad0d4ae 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs @@ -154,7 +154,7 @@ public virtual void TestAccquireReleaseRace() { if (checkPoint) { - Assert.IsTrue(sync.updateJoin.Await(TimeSpan.FromSeconds(10)), "timed out waiting for update threads - deadlock?"); + Assert.IsTrue(sync.updateJoin.Wait(TimeSpan.FromSeconds(10)), "timed out waiting for update threads - deadlock?"); if (exceptions.Count > 0) { foreach (Exception throwable in exceptions) @@ -170,8 +170,8 @@ public virtual void TestAccquireReleaseRace() } checkPoint.Value = false; - sync.waiter.CountDown(); - sync.leftCheckpoint.Await(); + sync.waiter.Signal(); + sync.leftCheckpoint.Wait(); } Assert.IsFalse(checkPoint); Assert.AreEqual(0, sync.waiter.Count); @@ -187,12 +187,12 @@ public virtual void TestAccquireReleaseRace() checkPoint.Value = true; } - Assert.IsTrue(sync.updateJoin.Await(TimeSpan.FromSeconds(10))); + Assert.IsTrue(sync.updateJoin.Wait(TimeSpan.FromSeconds(10))); AssertState(numReleasers, numStallers, numWaiters, threads, ctrl); checkPoint.Value = false; stop.Value = true; - sync.waiter.CountDown(); - sync.leftCheckpoint.Await(); + sync.waiter.Signal(); + sync.leftCheckpoint.Wait(); for (int i = 0; i < threads.Length; i++) { @@ -320,7 +320,7 @@ public override void Run() } if (checkPoint) { - sync.updateJoin.CountDown(); + sync.updateJoin.Signal(); try { Assert.IsTrue(sync.Await()); @@ -338,7 +338,7 @@ public override void Run() // throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) //} - sync.leftCheckpoint.CountDown(); + sync.leftCheckpoint.Signal(); } if (Random.NextBoolean()) { @@ -352,7 +352,7 @@ public override void Run() exceptions.Add(e); } - sync.updateJoin.CountDown(); + sync.updateJoin.Signal(); } } @@ -443,9 +443,9 @@ public static void AwaitState(ThreadState state, params ThreadJob[] threads) public sealed class Synchronizer { - internal volatile CountDownLatch waiter; - internal volatile CountDownLatch updateJoin; - internal volatile CountDownLatch leftCheckpoint; + internal volatile CountdownLatch waiter; + internal volatile CountdownLatch updateJoin; + internal volatile CountdownLatch leftCheckpoint; public Synchronizer(int numUpdater, int numThreads) { @@ -454,14 +454,14 @@ public Synchronizer(int numUpdater, int numThreads) public void Reset(int numUpdaters, int numThreads) { - this.waiter = new CountDownLatch(1); - this.updateJoin = new CountDownLatch(numUpdaters); - this.leftCheckpoint = new CountDownLatch(numUpdaters); + this.waiter = new CountdownLatch(1); + this.updateJoin = new CountdownLatch(numUpdaters); + this.leftCheckpoint = new CountdownLatch(numUpdaters); } public bool Await() { - return waiter.Await(TimeSpan.FromSeconds(10)); + return waiter.Wait(TimeSpan.FromSeconds(10)); } } } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 549e972028..9e5bcaed72 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -3021,8 +3021,8 @@ public virtual void TestRollbackWhileMergeIsRunning() { Directory dir = NewDirectory(); - CountDownLatch mergeStarted = new CountDownLatch(1); - CountDownLatch closeStarted = new CountDownLatch(1); + CountdownLatch mergeStarted = new CountdownLatch(1); + CountdownLatch closeStarted = new CountdownLatch(1); IndexWriterConfig iwc = NewIndexWriterConfig(Random, TEST_VERSION_CURRENT, new MockAnalyzer(Random)); LogDocMergePolicy mp = new LogDocMergePolicy(); @@ -3042,9 +3042,9 @@ public virtual void TestRollbackWhileMergeIsRunning() private sealed class InfoStreamAnonymousClassForRollbackWhileMergeIsRunning : InfoStream { - private readonly CountDownLatch closeStarted; + private readonly CountdownLatch closeStarted; - public InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(CountDownLatch closeStarted) + public InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(CountdownLatch closeStarted) { this.closeStarted = closeStarted; } @@ -3058,7 +3058,7 @@ public override void Message(string component, string message) { if (message.Equals("rollback", StringComparison.Ordinal)) { - closeStarted.CountDown(); + closeStarted.Signal(); } } @@ -3069,10 +3069,10 @@ protected override void Dispose(bool disposing) private sealed class ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning : ConcurrentMergeScheduler { - private readonly CountDownLatch mergeStarted; - private readonly CountDownLatch closeStarted; + private readonly CountdownLatch mergeStarted; + private readonly CountdownLatch closeStarted; - public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(CountDownLatch mergeStarted, CountDownLatch closeStarted) + public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(CountdownLatch mergeStarted, CountdownLatch closeStarted) { this.mergeStarted = mergeStarted; this.closeStarted = closeStarted; @@ -3080,10 +3080,10 @@ public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(Coun protected internal override void DoMerge(MergePolicy.OneMerge merge) { - mergeStarted.CountDown(); + mergeStarted.Signal(); try { - closeStarted.Await(); + closeStarted.Wait(); } catch (Exception ie) when (ie.IsInterruptedException()) { @@ -3148,8 +3148,8 @@ public virtual void TestClosingNRTReaderDoesNotCorruptYourIndex() [Test] public virtual void TestCloseDuringCommit() { - CountDownLatch startCommit = new CountDownLatch(1); - CountDownLatch finishCommit = new CountDownLatch(1); + CountdownLatch startCommit = new CountdownLatch(1); + CountdownLatch finishCommit = new CountdownLatch(1); Directory dir = NewDirectory(); IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, null); @@ -3158,7 +3158,7 @@ public virtual void TestCloseDuringCommit() IndexWriter iw = new IndexWriter(dir, iwc); Document doc = new Document(); new ThreadAnonymousClassForCloseDuringCommit(iw, finishCommit).Start(); - startCommit.Await(); + startCommit.Wait(); try { iw.Dispose(); @@ -3167,16 +3167,16 @@ public virtual void TestCloseDuringCommit() { // OK, but not required (depends on thread scheduling) } - finishCommit.Await(); + finishCommit.Wait(); iw.Dispose(); dir.Dispose(); } private sealed class InfoStreamAnonymousClassForCloseDuringCommit : InfoStream { - private readonly CountDownLatch startCommit; + private readonly CountdownLatch startCommit; - public InfoStreamAnonymousClassForCloseDuringCommit(CountDownLatch startCommit) + public InfoStreamAnonymousClassForCloseDuringCommit(CountdownLatch startCommit) { this.startCommit = startCommit; } @@ -3185,7 +3185,7 @@ public override void Message(string component, string message) { if (message.Equals("finishStartCommit", StringComparison.Ordinal)) { - startCommit.CountDown(); + startCommit.Signal(); try { Thread.Sleep(10); @@ -3210,9 +3210,9 @@ protected override void Dispose(bool disposing) private sealed class ThreadAnonymousClassForCloseDuringCommit : ThreadJob { private readonly IndexWriter iw; - private readonly CountDownLatch finishCommit; + private readonly CountdownLatch finishCommit; - public ThreadAnonymousClassForCloseDuringCommit(IndexWriter iw, CountDownLatch finishCommit) + public ThreadAnonymousClassForCloseDuringCommit(IndexWriter iw, CountdownLatch finishCommit) { this.iw = iw; this.finishCommit = finishCommit; @@ -3223,7 +3223,7 @@ public override void Run() try { iw.Commit(); - finishCommit.CountDown(); + finishCommit.Signal(); } catch (Exception ioe) when (ioe.IsIOException()) { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs index 52907111ae..2699a419db 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs @@ -356,17 +356,17 @@ public virtual void TestDeleteAllNoDeadLock() RandomIndexWriter modifier = new RandomIndexWriter(Random, dir); int numThreads = AtLeast(2); ThreadJob[] threads = new ThreadJob[numThreads]; - CountDownLatch latch = new CountDownLatch(1); - CountDownLatch doneLatch = new CountDownLatch(numThreads); + CountdownLatch latch = new CountdownLatch(1); + CountdownLatch doneLatch = new CountdownLatch(numThreads); for (int i = 0; i < numThreads; i++) { int offset = i; threads[i] = new ThreadAnonymousClass(modifier, latch, doneLatch, offset); threads[i].Start(); } - latch.CountDown(); + latch.Signal(); //Wait for 1 millisecond - while (!doneLatch.Await(TimeSpan.FromMilliseconds(1))) + while (!doneLatch.Wait(TimeSpan.FromMilliseconds(1))) { modifier.DeleteAll(); if (Verbose) @@ -394,11 +394,11 @@ public virtual void TestDeleteAllNoDeadLock() private sealed class ThreadAnonymousClass : ThreadJob { private readonly RandomIndexWriter modifier; - private readonly CountDownLatch latch; - private readonly CountDownLatch doneLatch; + private readonly CountdownLatch latch; + private readonly CountdownLatch doneLatch; private readonly int offset; - public ThreadAnonymousClass(RandomIndexWriter modifier, CountDownLatch latch, CountDownLatch doneLatch, int offset) + public ThreadAnonymousClass(RandomIndexWriter modifier, CountdownLatch latch, CountdownLatch doneLatch, int offset) { this.modifier = modifier; this.latch = latch; @@ -412,7 +412,7 @@ public override void Run() const int value = 100; try { - latch.Await(); + latch.Wait(); for (int j = 0; j < 1000; j++) { Document doc = new Document(); @@ -436,7 +436,7 @@ public override void Run() } finally { - doneLatch.CountDown(); + doneLatch.Signal(); if (Verbose) { Console.WriteLine("\tThread[" + offset + "]: done indexing"); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs index f5e594ad76..e94b4940a5 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs @@ -52,7 +52,7 @@ public virtual void TestIsCurrentWithThreads() IndexWriter writer = new IndexWriter(dir, conf); ReaderHolder holder = new ReaderHolder(); ReaderThread[] threads = new ReaderThread[AtLeast(3)]; - CountDownLatch latch = new CountDownLatch(1); + CountdownLatch latch = new CountdownLatch(1); WriterThread writerThread = new WriterThread(holder, writer, AtLeast(500), Random, latch); for (int i = 0; i < threads.Length; i++) { @@ -87,10 +87,10 @@ public class WriterThread : ThreadJob internal readonly IndexWriter writer; internal readonly int numOps; internal bool countdown = true; - internal readonly CountDownLatch latch; + internal readonly CountdownLatch latch; internal Exception failed; - internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, Random random, CountDownLatch latch) + internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, Random random, CountdownLatch latch) : base() { this.holder = holder; @@ -133,7 +133,7 @@ public override void Run() if (countdown) { countdown = false; - latch.CountDown(); + latch.Signal(); } } if (random.NextBoolean()) @@ -161,7 +161,7 @@ public override void Run() holder.reader = null; if (countdown) { - latch.CountDown(); + latch.Signal(); } if (currentReader != null) { @@ -184,10 +184,10 @@ public override void Run() public sealed class ReaderThread : ThreadJob { internal readonly ReaderHolder holder; - internal readonly CountDownLatch latch; + internal readonly CountdownLatch latch; internal Exception failed; - internal ReaderThread(ReaderHolder holder, CountDownLatch latch) + internal ReaderThread(ReaderHolder holder, CountdownLatch latch) : base() { this.holder = holder; @@ -198,7 +198,7 @@ public override void Run() { try { - latch.Await(); + latch.Wait(); } catch (Exception e) when (e.IsInterruptedException()) { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs index 4f0ab5d55f..9d434b8960 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -44,10 +44,10 @@ public virtual void TestSegmentCountOnFlushBasic() { Directory dir = NewDirectory(); IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - CountDownLatch startingGun = new CountDownLatch(1); - CountDownLatch startDone = new CountDownLatch(2); - CountDownLatch middleGun = new CountDownLatch(1); - CountDownLatch finalGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); + CountdownLatch startDone = new CountdownLatch(2); + CountdownLatch middleGun = new CountdownLatch(1); + CountdownLatch finalGun = new CountdownLatch(1); ThreadJob[] threads = new ThreadJob[2]; for (int i = 0; i < threads.Length; i++) { @@ -56,8 +56,8 @@ public virtual void TestSegmentCountOnFlushBasic() threads[i].Start(); } - startingGun.CountDown(); - startDone.Await(); + startingGun.Signal(); + startDone.Wait(); IndexReader r = DirectoryReader.Open(w, true); Assert.AreEqual(2, r.NumDocs); @@ -66,10 +66,10 @@ public virtual void TestSegmentCountOnFlushBasic() Assert.IsTrue(numSegments <= 2); r.Dispose(); - middleGun.CountDown(); + middleGun.Signal(); threads[0].Join(); - finalGun.CountDown(); + finalGun.Signal(); threads[1].Join(); r = DirectoryReader.Open(w, true); @@ -86,12 +86,12 @@ private sealed class ThreadAnonymousClassForSegmentCountOnFlushBasic : ThreadJob { private readonly IndexWriter w; private readonly int threadID; - private readonly CountDownLatch startingGun; - private readonly CountDownLatch startDone; - private readonly CountDownLatch middleGun; - private readonly CountDownLatch finalGun; + private readonly CountdownLatch startingGun; + private readonly CountdownLatch startDone; + private readonly CountdownLatch middleGun; + private readonly CountdownLatch finalGun; - public ThreadAnonymousClassForSegmentCountOnFlushBasic(IndexWriter w, int threadID, CountDownLatch startingGun, CountDownLatch startDone, CountDownLatch middleGun, CountDownLatch finalGun) + public ThreadAnonymousClassForSegmentCountOnFlushBasic(IndexWriter w, int threadID, CountdownLatch startingGun, CountdownLatch startDone, CountdownLatch middleGun, CountdownLatch finalGun) { this.w = w; this.threadID = threadID; @@ -105,20 +105,20 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); Document doc = new Document(); doc.Add(NewTextField("field", "here is some text", Field.Store.NO)); w.AddDocument(doc); - startDone.CountDown(); + startDone.Signal(); - middleGun.Await(); + middleGun.Wait(); if (threadID == 0) { w.AddDocument(doc); } else { - finalGun.Await(); + finalGun.Wait(); w.AddDocument(doc); } } @@ -311,14 +311,14 @@ public virtual void TestManyThreadsClose() RandomIndexWriter w = new RandomIndexWriter(Random, dir); w.DoRandomForceMerge = false; ThreadJob[] threads = new ThreadJob[TestUtil.NextInt32(Random, 4, 30)]; - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClassForManyThreadsClose(w, startingGun); threads[i].Start(); } - startingGun.CountDown(); + startingGun.Signal(); Thread.Sleep(100); // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released in @@ -344,9 +344,9 @@ public virtual void TestManyThreadsClose() private sealed class ThreadAnonymousClassForManyThreadsClose : ThreadJob { private readonly RandomIndexWriter w; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; - public ThreadAnonymousClassForManyThreadsClose(RandomIndexWriter w, CountDownLatch startingGun) + public ThreadAnonymousClassForManyThreadsClose(RandomIndexWriter w, CountdownLatch startingGun) { this.w = w; this.startingGun = startingGun; @@ -356,7 +356,7 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); Document doc = new Document(); doc.Add(new TextField("field", "here is some text that is a bit longer than normal trivial text", Field.Store.NO)); // LUCENENET: ported from upstream LUCENE-5871 (commit 2cfcdcc, first released @@ -389,7 +389,7 @@ public virtual void TestDocsStuckInRAMForever() iwc.SetCodec(codec); iwc.SetMergePolicy(NoMergePolicy.NO_COMPOUND_FILES); IndexWriter w = new IndexWriter(dir, iwc); - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); ThreadJob[] threads = new ThreadJob[2]; for (int i = 0; i < threads.Length; i++) { @@ -398,7 +398,7 @@ public virtual void TestDocsStuckInRAMForever() threads[i].Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob t in threads) { t.Join(); @@ -450,9 +450,9 @@ private sealed class ThreadAnonymousClassForDocsStuckInRAMForever : ThreadJob { private readonly IndexWriter w; private readonly int threadID; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; - public ThreadAnonymousClassForDocsStuckInRAMForever(IndexWriter w, int threadID, CountDownLatch startingGun) + public ThreadAnonymousClassForDocsStuckInRAMForever(IndexWriter w, int threadID, CountdownLatch startingGun) { this.w = w; this.threadID = threadID; @@ -463,7 +463,7 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); for (int j = 0; j < 1000; j++) { Document doc = new Document(); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs index d1af9e2ab2..bfed359471 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs @@ -561,14 +561,14 @@ public virtual void TestIOExceptionDuringWriteSegmentWithThreadsOnlyOnce() public virtual void TestOpenTwoIndexWritersOnDifferentThreads() { Directory dir = NewDirectory(); - CountDownLatch oneIWConstructed = new CountDownLatch(1); + CountdownLatch oneIWConstructed = new CountdownLatch(1); DelayedIndexAndCloseRunnable thread1 = new DelayedIndexAndCloseRunnable(dir, oneIWConstructed); DelayedIndexAndCloseRunnable thread2 = new DelayedIndexAndCloseRunnable(dir, oneIWConstructed); thread1.Start(); thread2.Start(); - oneIWConstructed.Await(); + oneIWConstructed.Wait(); thread1.StartIndexing(); thread2.StartIndexing(); @@ -602,10 +602,10 @@ internal class DelayedIndexAndCloseRunnable : ThreadJob private readonly Directory dir; internal bool failed = false; internal Exception failure = null; - private readonly CountDownLatch startIndexing = new CountDownLatch(1); - private CountDownLatch iwConstructed; + private readonly CountdownLatch startIndexing = new CountdownLatch(1); + private CountdownLatch iwConstructed; - public DelayedIndexAndCloseRunnable(Directory dir, CountDownLatch iwConstructed) + public DelayedIndexAndCloseRunnable(Directory dir, CountdownLatch iwConstructed) { this.dir = dir; this.iwConstructed = iwConstructed; @@ -613,7 +613,7 @@ public DelayedIndexAndCloseRunnable(Directory dir, CountDownLatch iwConstructed) public virtual void StartIndexing() { - this.startIndexing.CountDown(); + this.startIndexing.Signal(); } public override void Run() @@ -624,8 +624,8 @@ public override void Run() Field field = NewTextField("field", "testData", Field.Store.YES); doc.Add(field); using IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - iwConstructed.CountDown(); - startIndexing.Await(); + iwConstructed.Signal(); + startIndexing.Wait(); writer.AddDocument(doc); } catch (Exception e) when (e.IsThrowable()) diff --git a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs index c1cdaeac69..f702a92035 100644 --- a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs @@ -269,7 +269,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountDownLatch done = new CountDownLatch(numThreads); + CountdownLatch done = new CountdownLatch(numThreads); AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens @@ -285,7 +285,7 @@ public virtual void TestStressMultiThreading() { t.Start(); } - done.Await(); + done.Wait(); writer.Dispose(); DirectoryReader reader = DirectoryReader.Open(dir); @@ -327,12 +327,12 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter writer; private readonly int numDocs; - private readonly CountDownLatch done; + private readonly CountdownLatch done; private readonly AtomicInt32 numUpdates; private readonly string f; private readonly string cf; - public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountDownLatch done, AtomicInt32 numUpdates, string f, string cf) + public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountdownLatch done, AtomicInt32 numUpdates, string f, string cf) : base(str) { this.writer = writer; @@ -441,7 +441,7 @@ public override void Run() } } } - done.CountDown(); + done.Signal(); } } } diff --git a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs index d93f969e1c..d52d3b6d34 100644 --- a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs @@ -1140,7 +1140,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountDownLatch done = new CountDownLatch(numThreads); + CountdownLatch done = new CountdownLatch(numThreads); AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens @@ -1156,7 +1156,7 @@ public virtual void TestStressMultiThreading() { t.Start(); } - done.Await(); + done.Wait(); writer.Dispose(); DirectoryReader reader = DirectoryReader.Open(dir); @@ -1192,12 +1192,12 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexWriter writer; private readonly int numDocs; - private readonly CountDownLatch done; + private readonly CountdownLatch done; private readonly AtomicInt32 numUpdates; private readonly string f; private readonly string cf; - public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountDownLatch done, AtomicInt32 numUpdates, string f, string cf) + public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountdownLatch done, AtomicInt32 numUpdates, string f, string cf) : base(str) { this.writer = writer; @@ -1304,7 +1304,7 @@ public override void Run() } } } - done.CountDown(); + done.Signal(); } } } diff --git a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs index 3ebb4ccadf..8160ef303b 100644 --- a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs +++ b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs @@ -231,7 +231,7 @@ public virtual void TestHashCodeWithThreads() { queries[i] = new AutomatonQuery(new Term("bogus", "bogus"), AutomatonTestUtil.RandomAutomaton(Random)); } - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); int numThreads = TestUtil.NextInt32(Random, 2, 5); ThreadJob[] threads = new ThreadJob[numThreads]; for (int threadID = 0; threadID < numThreads; threadID++) @@ -240,7 +240,7 @@ public virtual void TestHashCodeWithThreads() threads[threadID] = thread; thread.Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob thread in threads) { thread.Join(); @@ -250,9 +250,9 @@ public virtual void TestHashCodeWithThreads() private sealed class ThreadAnonymousClass : ThreadJob { private readonly AutomatonQuery[] queries; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; - public ThreadAnonymousClass(AutomatonQuery[] queries, CountDownLatch startingGun) + public ThreadAnonymousClass(AutomatonQuery[] queries, CountdownLatch startingGun) { this.queries = queries; this.startingGun = startingGun; @@ -260,7 +260,7 @@ public ThreadAnonymousClass(AutomatonQuery[] queries, CountDownLatch startingGun public override void Run() { - startingGun.Await(); + startingGun.Wait(); for (int i = 0; i < queries.Length; i++) { _ = queries[i].GetHashCode(); // LUCENENET: using discard variable to prevent warning about ignoring return value diff --git a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs index f8a329c8ed..e9e258a31e 100644 --- a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs +++ b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs @@ -396,8 +396,8 @@ public virtual void TestThreadStarvationNoDeleteNRTReader() IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)); conf.SetMergePolicy(Random.NextBoolean() ? NoMergePolicy.COMPOUND_FILES : NoMergePolicy.NO_COMPOUND_FILES); Directory d = NewDirectory(); - CountDownLatch latch = new CountDownLatch(1); - CountDownLatch signal = new CountDownLatch(1); + CountdownLatch latch = new CountdownLatch(1); + CountdownLatch signal = new CountdownLatch(1); LatchedIndexWriter _writer = new LatchedIndexWriter(d, conf, latch, signal); TrackingIndexWriter writer = new TrackingIndexWriter(_writer); @@ -450,12 +450,12 @@ public virtual void TestThreadStarvationNoDeleteNRTReader() private sealed class ThreadAnonymousClass : ThreadJob { - private readonly CountDownLatch latch; - private readonly CountDownLatch signal; + private readonly CountdownLatch latch; + private readonly CountdownLatch signal; private readonly TrackingIndexWriter writer; private readonly SearcherManager manager; - public ThreadAnonymousClass(CountDownLatch latch, CountDownLatch signal, TrackingIndexWriter writer, SearcherManager manager) + public ThreadAnonymousClass(CountdownLatch latch, CountdownLatch signal, TrackingIndexWriter writer, SearcherManager manager) { this.latch = latch; this.signal = signal; @@ -467,7 +467,7 @@ public override void Run() { try { - signal.Await(); + signal.Wait(); manager.MaybeRefresh(); writer.DeleteDocuments(new TermQuery(new Term("foo", "barista"))); manager.MaybeRefresh(); // kick off another reopen so we inc. the internal gen @@ -478,7 +478,7 @@ public override void Run() } finally { - latch.CountDown(); // let the add below finish + latch.Signal(); // let the add below finish } } } @@ -513,11 +513,11 @@ public override void Run() public class LatchedIndexWriter : IndexWriter { - internal CountDownLatch latch; + internal CountdownLatch latch; internal bool waitAfterUpdate = false; - internal CountDownLatch signal; + internal CountdownLatch signal; - internal LatchedIndexWriter(Directory d, IndexWriterConfig conf, CountDownLatch latch, CountDownLatch signal) + internal LatchedIndexWriter(Directory d, IndexWriterConfig conf, CountdownLatch latch, CountdownLatch signal) : base(d, conf) { this.latch = latch; @@ -532,8 +532,8 @@ public override void UpdateDocument(Term term, IEnumerable doc, { if (waitAfterUpdate) { - signal.CountDown(); - latch.Await(); + signal.Signal(); + latch.Wait(); } } catch (Exception ie) when (ie.IsInterruptedException()) diff --git a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs index d0287ac32f..b83507fa52 100644 --- a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs +++ b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs @@ -69,7 +69,7 @@ public virtual void Test() Console.WriteLine(numThreads + " threads"); } - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); IList threads = new JCG.List(); int iters = AtLeast(1000); @@ -88,7 +88,7 @@ public virtual void Test() thread.Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob thread in threads) { @@ -141,7 +141,7 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly SearcherManager mgr; private readonly int? missing; private readonly LiveFieldValues rt; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; private readonly int iters; private readonly int idCount; private readonly double reopenChance; @@ -151,7 +151,7 @@ private sealed class ThreadAnonymousClass : ThreadJob private readonly int threadID; private readonly Random threadRandom; - public ThreadAnonymousClass(IndexWriter w, SearcherManager mgr, int? missing, LiveFieldValues rt, CountDownLatch startingGun, int iters, int idCount, double reopenChance, double deleteChance, double addChance, int t, int threadID, Random threadRandom) + public ThreadAnonymousClass(IndexWriter w, SearcherManager mgr, int? missing, LiveFieldValues rt, CountdownLatch startingGun, int iters, int idCount, double reopenChance, double deleteChance, double addChance, int t, int threadID, Random threadRandom) { this.w = w; this.mgr = mgr; @@ -175,7 +175,7 @@ public override void Run() IDictionary values = new JCG.Dictionary(); IList allIDs = new SynchronizedList(); - startingGun.Await(); + startingGun.Wait(); for (int iter = 0; iter < iters; iter++) { // Add/update a document diff --git a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs index 796afb2af5..c98c0fdab1 100644 --- a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs +++ b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs @@ -92,7 +92,7 @@ public virtual void Test() if (answers.Count > 0) { - CountDownLatch startingGun = new CountDownLatch(1); + CountdownLatch startingGun = new CountdownLatch(1); int numThreads = TestUtil.NextInt32(Random, 2, 5); ThreadJob[] threads = new ThreadJob[numThreads]; for (int threadID = 0; threadID < numThreads; threadID++) @@ -101,7 +101,7 @@ public virtual void Test() threads[threadID] = thread; thread.Start(); } - startingGun.CountDown(); + startingGun.Signal(); foreach (ThreadJob thread in threads) { thread.Join(); @@ -115,9 +115,9 @@ private sealed class ThreadAnonymousClass : ThreadJob { private readonly IndexSearcher s; private readonly IDictionary answers; - private readonly CountDownLatch startingGun; + private readonly CountdownLatch startingGun; - public ThreadAnonymousClass(IndexSearcher s, IDictionary answers, CountDownLatch startingGun) + public ThreadAnonymousClass(IndexSearcher s, IDictionary answers, CountdownLatch startingGun) { this.s = s; this.answers = answers; @@ -128,7 +128,7 @@ public override void Run() { try { - startingGun.Await(); + startingGun.Wait(); for (int i = 0; i < 20; i++) { IList> shuffled = new JCG.List>(answers); diff --git a/src/Lucene.Net.Tests/Search/TestSearcherManager.cs b/src/Lucene.Net.Tests/Search/TestSearcherManager.cs index 44b61fae3d..31888bc726 100644 --- a/src/Lucene.Net.Tests/Search/TestSearcherManager.cs +++ b/src/Lucene.Net.Tests/Search/TestSearcherManager.cs @@ -272,8 +272,8 @@ public virtual void TestIntermediateClose() IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetMergeScheduler(new ConcurrentMergeScheduler())); writer.AddDocument(new Document()); writer.Commit(); - CountDownLatch awaitEnterWarm = new CountDownLatch(1); - CountDownLatch awaitClose = new CountDownLatch(1); + CountdownLatch awaitEnterWarm = new CountdownLatch(1); + CountdownLatch awaitClose = new CountdownLatch(1); AtomicBoolean triedReopen = new AtomicBoolean(false); //TaskScheduler es = Random().NextBoolean() ? null : Executors.newCachedThreadPool(new NamedThreadFactory("testIntermediateClose")); TaskScheduler es = Random.NextBoolean() ? null : TaskScheduler.Default; @@ -302,13 +302,13 @@ public virtual void TestIntermediateClose() { Console.WriteLine("THREAD started"); } - awaitEnterWarm.Await(); + awaitEnterWarm.Wait(); if (Verbose) { Console.WriteLine("NOW call close"); } searcherManager.Dispose(); - awaitClose.CountDown(); + awaitClose.Signal(); thread.Join(); try { @@ -333,12 +333,12 @@ public virtual void TestIntermediateClose() private sealed class SearcherFactoryAnonymousClass2 : SearcherFactory { - private readonly CountDownLatch awaitEnterWarm; - private readonly CountDownLatch awaitClose; + private readonly CountdownLatch awaitEnterWarm; + private readonly CountdownLatch awaitClose; private readonly AtomicBoolean triedReopen; private readonly TaskScheduler es; - public SearcherFactoryAnonymousClass2(CountDownLatch awaitEnterWarm, CountDownLatch awaitClose, AtomicBoolean triedReopen, TaskScheduler es) + public SearcherFactoryAnonymousClass2(CountdownLatch awaitEnterWarm, CountdownLatch awaitClose, AtomicBoolean triedReopen, TaskScheduler es) { this.awaitEnterWarm = awaitEnterWarm; this.awaitClose = awaitClose; @@ -352,8 +352,8 @@ public override IndexSearcher NewSearcher(IndexReader r) { if (triedReopen) { - awaitEnterWarm.CountDown(); - awaitClose.Await(); + awaitEnterWarm.Signal(); + awaitClose.Wait(); } } catch (Exception e) when (e.IsInterruptedException()) diff --git a/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs b/src/Lucene.Net.Tests/Support/Threading/CountdownLatchTest.cs similarity index 65% rename from src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs rename to src/Lucene.Net.Tests/Support/Threading/CountdownLatchTest.cs index a0dc5b74df..54af9d8fa4 100644 --- a/src/Lucene.Net.Tests/Support/Threading/CountDownLatchTest.cs +++ b/src/Lucene.Net.Tests/Support/Threading/CountdownLatchTest.cs @@ -27,7 +27,7 @@ namespace Lucene.Net.Support.Threading */ [TestFixture] - public class CountDownLatchTest : JSR166TestCase + public class CountdownLatchTest : JSR166TestCase { //public static void main(String[] args) //{ @@ -46,7 +46,7 @@ public void TestConstructor() { try { - _ = new CountDownLatch(-1); + _ = new CountdownLatch(-1); shouldThrow(); } catch (ArgumentOutOfRangeException) @@ -61,9 +61,9 @@ public void TestConstructor() [Test] public void TestGetCount() { - CountDownLatch l = new CountDownLatch(2); + CountdownLatch l = new CountdownLatch(2); assertEquals(2, l.Count); - l.CountDown(); + l.Signal(); assertEquals(1, l.Count); } @@ -71,13 +71,13 @@ public void TestGetCount() * countDown decrements count when positive and has no effect when zero */ [Test] - public void TestCountDown() + public void TestSignal() // LUCENENET: renamed from TestCountDown (Signal() was CountDown()) { - CountDownLatch l = new CountDownLatch(1); + CountdownLatch l = new CountdownLatch(1); assertEquals(1, l.Count); - l.CountDown(); + l.Signal(); assertEquals(0, l.Count); - l.CountDown(); + l.Signal(); assertEquals(0, l.Count); } @@ -85,19 +85,19 @@ public void TestCountDown() * await returns after countDown to zero, but not before */ [Test] - public void TestAwait() + public void TestWait() // LUCENENET: renamed from TestAwait (Wait() was Await()) { - CountDownLatch l = new CountDownLatch(2); + CountdownLatch l = new CountdownLatch(2); - ThreadJob t = new ThreadAnonymousClassForTestAwait(this, l); + ThreadJob t = new ThreadAnonymousClassForTestWait(this, l); t.Start(); try { assertEquals(l.Count, 2); Thread.Sleep(SHORT_DELAY_MS); - l.CountDown(); + l.Signal(); assertEquals(l.Count, 1); - l.CountDown(); + l.Signal(); assertEquals(l.Count, 0); t.Join(); } @@ -107,12 +107,12 @@ public void TestAwait() } } - private sealed class ThreadAnonymousClassForTestAwait : ThreadJob + private sealed class ThreadAnonymousClassForTestWait : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestAwait { - private readonly CountDownLatchTest outerInstance; - private readonly CountDownLatch l; + private readonly CountdownLatchTest outerInstance; + private readonly CountdownLatch l; - public ThreadAnonymousClassForTestAwait(CountDownLatchTest outerInstance, CountDownLatch l) + public ThreadAnonymousClassForTestWait(CountdownLatchTest outerInstance, CountdownLatch l) { this.outerInstance = outerInstance; this.l = l; @@ -123,7 +123,7 @@ public override void Run() try { outerInstance.threadAssertTrue(l.Count > 0); - l.Await(); + l.Wait(); outerInstance.threadAssertTrue(l.Count == 0); } catch (Exception e) when (e.IsInterruptedException()) @@ -137,19 +137,19 @@ public override void Run() * timed await returns after countDown to zero */ [Test] - public void TestTimedAwait() + public void TestTimedWait() // LUCENENET: renamed from TestTimedAwait (Wait() was Await()) { - CountDownLatch l = new CountDownLatch(2); + CountdownLatch l = new CountdownLatch(2); - ThreadJob t = new ThreadAnonymousClassForTestTimedAwait(this, l); + ThreadJob t = new ThreadAnonymousClassForTestTimedWait(this, l); t.Start(); try { assertEquals(l.Count, 2); Thread.Sleep(SHORT_DELAY_MS); - l.CountDown(); + l.Signal(); assertEquals(l.Count, 1); - l.CountDown(); + l.Signal(); assertEquals(l.Count, 0); t.Join(); } @@ -159,12 +159,12 @@ public void TestTimedAwait() } } - private sealed class ThreadAnonymousClassForTestTimedAwait : ThreadJob + private sealed class ThreadAnonymousClassForTestTimedWait : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestTimedAwait { - private readonly CountDownLatchTest outerInstance; - private readonly CountDownLatch l; + private readonly CountdownLatchTest outerInstance; + private readonly CountdownLatch l; - public ThreadAnonymousClassForTestTimedAwait(CountDownLatchTest outerInstance, CountDownLatch l) + public ThreadAnonymousClassForTestTimedWait(CountdownLatchTest outerInstance, CountdownLatch l) { this.outerInstance = outerInstance; this.l = l; @@ -175,7 +175,7 @@ public override void Run() try { outerInstance.threadAssertTrue(l.Count > 0); - outerInstance.threadAssertTrue(l.Await(TimeSpan.FromMilliseconds(SMALL_DELAY_MS))); + outerInstance.threadAssertTrue(l.Wait(TimeSpan.FromMilliseconds(SMALL_DELAY_MS))); } catch (Exception e) when (e.IsInterruptedException()) { @@ -188,10 +188,10 @@ public override void Run() * await throws IE if interrupted before counted down */ [Test] - public void TestAwait_InterruptedException() + public void TestWait_InterruptedException() // LUCENENET: renamed from TestAwait_InterruptedException (Wait() was Await()) { - CountDownLatch l = new CountDownLatch(1); - ThreadJob t = new ThreadAnonymousClassForTestAwaitInterruptedException(this, l); + CountdownLatch l = new CountdownLatch(1); + ThreadJob t = new ThreadAnonymousClassForTestWaitInterruptedException(this, l); t.Start(); try { @@ -205,12 +205,12 @@ public void TestAwait_InterruptedException() } } - private sealed class ThreadAnonymousClassForTestAwaitInterruptedException : ThreadJob + private sealed class ThreadAnonymousClassForTestWaitInterruptedException : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestAwaitInterruptedException { - private readonly CountDownLatchTest outerInstance; - private readonly CountDownLatch l; + private readonly CountdownLatchTest outerInstance; + private readonly CountdownLatch l; - public ThreadAnonymousClassForTestAwaitInterruptedException(CountDownLatchTest outerInstance, CountDownLatch l) + public ThreadAnonymousClassForTestWaitInterruptedException(CountdownLatchTest outerInstance, CountdownLatch l) { this.outerInstance = outerInstance; this.l = l; @@ -221,7 +221,7 @@ public override void Run() try { outerInstance.threadAssertTrue(l.Count > 0); - l.Await(); + l.Wait(); outerInstance.threadShouldThrow(); } catch (Exception e) when (e.IsInterruptedException()) @@ -235,10 +235,10 @@ public override void Run() * timed await throws IE if interrupted before counted down */ [Test] - public void TestTimedAwait_InterruptedException() + public void TestTimedWait_InterruptedException() // LUCENENET: renamed from TestTimedAwait_InterruptedException (Wait() was Await()) { - CountDownLatch l = new CountDownLatch(1); - ThreadJob t = new ThreadAnonymousClassForTestTimedAwaitInterruptedException(this, l); + CountdownLatch l = new CountdownLatch(1); + ThreadJob t = new ThreadAnonymousClassForTestTimedWaitInterruptedException(this, l); t.Start(); try { @@ -253,12 +253,12 @@ public void TestTimedAwait_InterruptedException() } } - private sealed class ThreadAnonymousClassForTestTimedAwaitInterruptedException : ThreadJob + private sealed class ThreadAnonymousClassForTestTimedWaitInterruptedException : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestTimedAwaitInterruptedException { - private readonly CountDownLatchTest outerInstance; - private readonly CountDownLatch l; + private readonly CountdownLatchTest outerInstance; + private readonly CountdownLatch l; - public ThreadAnonymousClassForTestTimedAwaitInterruptedException(CountDownLatchTest outerInstance, CountDownLatch l) + public ThreadAnonymousClassForTestTimedWaitInterruptedException(CountdownLatchTest outerInstance, CountdownLatch l) { this.outerInstance = outerInstance; this.l = l; @@ -269,7 +269,7 @@ public override void Run() try { outerInstance.threadAssertTrue(l.Count > 0); - l.Await(TimeSpan.FromMilliseconds(MEDIUM_DELAY_MS)); + l.Wait(TimeSpan.FromMilliseconds(MEDIUM_DELAY_MS)); outerInstance.threadShouldThrow(); } catch (Exception e) when (e.IsInterruptedException()) @@ -283,10 +283,10 @@ public override void Run() * timed await times out if not counted down before timeout */ [Test] - public void TestAwaitTimeout() + public void TestWaitTimeout() // LUCENENET: renamed from TestAwaitTimeout (Wait() was Await()) { - CountDownLatch l = new CountDownLatch(1); - ThreadJob t = new ThreadAnonymousClassForTestAwaitTimeout(this, l); + CountdownLatch l = new CountdownLatch(1); + ThreadJob t = new ThreadAnonymousClassForTestWaitTimeout(this, l); t.Start(); try { @@ -299,12 +299,12 @@ public void TestAwaitTimeout() } } - private sealed class ThreadAnonymousClassForTestAwaitTimeout : ThreadJob + private sealed class ThreadAnonymousClassForTestWaitTimeout : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestAwaitTimeout { - private readonly CountDownLatchTest outerInstance; - private readonly CountDownLatch l; + private readonly CountdownLatchTest outerInstance; + private readonly CountdownLatch l; - public ThreadAnonymousClassForTestAwaitTimeout(CountDownLatchTest outerInstance, CountDownLatch l) + public ThreadAnonymousClassForTestWaitTimeout(CountdownLatchTest outerInstance, CountdownLatch l) { this.outerInstance = outerInstance; this.l = l; @@ -315,7 +315,7 @@ public override void Run() try { outerInstance.threadAssertTrue(l.Count > 0); - outerInstance.threadAssertFalse(l.Await(TimeSpan.FromMilliseconds(SHORT_DELAY_MS))); + outerInstance.threadAssertFalse(l.Wait(TimeSpan.FromMilliseconds(SHORT_DELAY_MS))); outerInstance.threadAssertTrue(l.Count > 0); } catch (Exception ie) when (ie.IsInterruptedException()) @@ -331,13 +331,13 @@ public override void Run() [Test] public void TestToString() { - CountDownLatch s = new CountDownLatch(2); + CountdownLatch s = new CountdownLatch(2); string us = s.ToString(); assertTrue(us.IndexOf("Count = 2", StringComparison.Ordinal) >= 0); - s.CountDown(); + s.Signal(); string s1 = s.ToString(); assertTrue(s1.IndexOf("Count = 1", StringComparison.Ordinal) >= 0); - s.CountDown(); + s.Signal(); string s2 = s.ToString(); assertTrue(s2.IndexOf("Count = 0", StringComparison.Ordinal) >= 0); } diff --git a/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs b/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs index efbd87a970..968dd3cd1b 100644 --- a/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs +++ b/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs @@ -148,7 +148,7 @@ public override void TestFinished(Description description) public class Nested2 : WithNestedTests.AbstractNestedTest { public const int TOTAL_ITERS = 10; - public static CountDownLatch Die; + public static CountdownLatch Die; public static Thread Zombie; public static int TestNum; @@ -158,7 +158,7 @@ public class Nested2 : WithNestedTests.AbstractNestedTest public static void Setup() { Debug.Assert(Zombie == null); - Die = new CountDownLatch(1); + Die = new CountdownLatch(1); TestNum = 0; } diff --git a/src/Lucene.Net/Support/Threading/CountDownLatch.cs b/src/Lucene.Net/Support/Threading/CountdownLatch.cs similarity index 86% rename from src/Lucene.Net/Support/Threading/CountDownLatch.cs rename to src/Lucene.Net/Support/Threading/CountdownLatch.cs index b159d06f3b..203ef30cdd 100644 --- a/src/Lucene.Net/Support/Threading/CountDownLatch.cs +++ b/src/Lucene.Net/Support/Threading/CountdownLatch.cs @@ -27,30 +27,30 @@ namespace Lucene.Net.Support.Threading /// A synchronization aid that allows one or more threads to wait until /// a set of operations being performed in other threads completes. /// - /// A is initialized with a given count. - /// The methods block until the current count reaches - /// zero due to invocations of the method, after which + /// A is initialized with a given count. + /// The methods block until the current count reaches + /// zero due to invocations of the method, after which /// all waiting threads are released and any subsequent invocations of - /// return immediately. This is a one-shot phenomenon: + /// return immediately. This is a one-shot phenomenon: /// the count cannot be reset. /// - /// A is a versatile synchronization tool - /// and can be used for a number of purposes. A + /// A is a versatile synchronization tool + /// and can be used for a number of purposes. A /// initialized with a count of one serves as a simple on/off latch, or gate: - /// all threads invoking wait at the gate until it is - /// opened by a thread invoking . A - /// initialized to N can be used to make + /// all threads invoking wait at the gate until it is + /// opened by a thread invoking . A + /// initialized to N can be used to make /// one thread wait until N threads have completed some action, or /// some action has been completed N times. /// - /// A useful property of a is that it - /// doesn't require that threads calling wait for + /// A useful property of a is that it + /// doesn't require that threads calling wait for /// the count to reach zero before proceeding, it simply prevents any - /// thread from proceeding past an until all + /// thread from proceeding past an until all /// threads could pass. /// /// LUCENENET specific: This type is similar to , - /// but unlike , + /// but unlike , /// can be called any number of times after the count reaches zero without /// throwing. This matches Java's CountDownLatch.countDown() semantics. /// @@ -61,19 +61,19 @@ namespace Lucene.Net.Support.Threading /// the first time a waiter blocks. Callers should dispose instances when they are no /// longer needed, typically via a using statement. /// - internal class CountDownLatch : IDisposable + internal class CountdownLatch : IDisposable { // LUCENENET: the current count and ManualResetEventSlim for signaling, instead of Java's Sync class private volatile int _currentCount; private readonly ManualResetEventSlim _event; /// - /// Constructs a initialized with the given . + /// Constructs a initialized with the given . /// - /// The number of times must be invoked - /// before threads can pass through + /// The number of times must be invoked + /// before threads can pass through /// if is negative - public CountDownLatch(int count) + public CountdownLatch(int count) { if (count < 0) { @@ -95,7 +95,7 @@ public CountDownLatch(int count) /// dormant until one of two things happen: /// /// The count reaches zero due to invocations of the - /// method; or + /// method; or /// Some other thread interrupts the current thread. /// /// @@ -105,7 +105,7 @@ public CountDownLatch(int count) /// /// if the current thread is /// interrupted while waiting. - public void Await() + public void Wait() { _event.Wait(Timeout.Infinite); } @@ -123,7 +123,7 @@ public void Await() /// dormant until one of three things happen: /// /// The count reaches zero due to invocations of the - /// method; or + /// method; or /// Some other thread interrupts the current thread; or /// The specified waiting time elapses. /// @@ -144,7 +144,7 @@ public void Await() /// if the waiting time elapsed before the count reached zero. /// if the current thread is /// interrupted while waiting. - public bool Await(TimeSpan timeout) + public bool Wait(TimeSpan timeout) { return _event.Wait(timeout); } @@ -159,7 +159,7 @@ public bool Await(TimeSpan timeout) /// /// If the current count equals zero then nothing happens. /// - public void CountDown() + public void Signal() { if (_currentCount <= 0) { @@ -186,7 +186,7 @@ public void CountDown() /// to make the internal count 64-bit. /// /// The returned value is clamped at zero. The internal counter may - /// briefly drop below zero under concurrent + /// briefly drop below zero under concurrent /// calls that race past the early-return guard, but that detail is /// hidden here to match Java's getCount() semantics, which never /// returns a negative value. From a738352b9cbac513543f7edcbe660161119ca11d Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Mon, 22 Jun 2026 16:26:47 -0600 Subject: [PATCH 10/20] Replace CountdownLatch with J2N.Threading type --- .../Index/TestConcurrentMergeScheduler.cs | 4 +- .../Index/TestDocumentsWriterStallControl.cs | 7 +- .../Index/TestIndexWriterNRTIsCurrent.cs | 13 +- .../Support/Threading/CountdownLatchTest.cs | 345 ------------------ .../Support/Threading/CountdownLatch.cs | 219 ----------- 5 files changed, 9 insertions(+), 579 deletions(-) delete mode 100644 src/Lucene.Net.Tests/Support/Threading/CountdownLatchTest.cs delete mode 100644 src/Lucene.Net/Support/Threading/CountdownLatch.cs diff --git a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs index b3e43486d9..5725c5f147 100644 --- a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs +++ b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs @@ -1,9 +1,9 @@ +using J2N.Threading; using J2N.Threading.Atomic; using Lucene.Net.Attributes; using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Store; -using Lucene.Net.Support.Threading; using Lucene.Net.Util; using NUnit.Framework; using RandomizedTesting.Generators; @@ -311,7 +311,7 @@ public virtual void TestMaxMergeCount() IndexWriter w = new IndexWriter(dir, iwc); Document doc = new Document(); doc.Add(NewField("field", "field", TextField.TYPE_NOT_STORED)); - while (enoughMergesWaiting.Count != 0 && !failed) + while (enoughMergesWaiting.CurrentCount != 0 && !failed) { for (int i = 0; i < 10; i++) { diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs index f3fad0d4ae..6f16b70a49 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs @@ -1,6 +1,5 @@ using J2N.Threading; using J2N.Threading.Atomic; -using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; @@ -174,7 +173,7 @@ public virtual void TestAccquireReleaseRace() sync.leftCheckpoint.Wait(); } Assert.IsFalse(checkPoint); - Assert.AreEqual(0, sync.waiter.Count); + Assert.AreEqual(0, sync.waiter.CurrentCount); if (checkPointProbability >= Random.NextSingle()) { sync.Reset(numStallers + numReleasers, numStallers + numReleasers + numWaiters); @@ -273,7 +272,7 @@ public override void Run() } catch (Exception e) when (e.IsInterruptedException()) { - Console.WriteLine("[Waiter] got interrupted - wait count: " + sync.waiter.Count); + Console.WriteLine("[Waiter] got interrupted - wait count: " + sync.waiter.CurrentCount); throw new Util.ThreadInterruptedException(e); } } @@ -327,7 +326,7 @@ public override void Run() } catch (Exception e) when (e.IsInterruptedException()) { - Console.WriteLine("[Updater] got interrupted - wait count: " + sync.waiter.Count); + Console.WriteLine("[Updater] got interrupted - wait count: " + sync.waiter.CurrentCount); throw new Util.ThreadInterruptedException(e); } // LUCENENET: Not sure why this catch block was added, but I suspect it was for debugging purposes. Commented it rather than removing it because diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs index e94b4940a5..7a3e8935de 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs @@ -1,10 +1,8 @@ using J2N.Threading; using Lucene.Net.Documents; -using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; -using System.IO; using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; @@ -41,7 +39,7 @@ public class TestIndexWriterNRTIsCurrent : LuceneTestCase public class ReaderHolder { internal volatile DirectoryReader reader; - internal volatile bool stop = false; + internal volatile bool stop; } [Test] @@ -53,7 +51,7 @@ public virtual void TestIsCurrentWithThreads() ReaderHolder holder = new ReaderHolder(); ReaderThread[] threads = new ReaderThread[AtLeast(3)]; CountdownLatch latch = new CountdownLatch(1); - WriterThread writerThread = new WriterThread(holder, writer, AtLeast(500), Random, latch); + WriterThread writerThread = new WriterThread(holder, writer, AtLeast(500), latch); for (int i = 0; i < threads.Length; i++) { threads[i] = new ReaderThread(holder, latch); @@ -90,8 +88,7 @@ public class WriterThread : ThreadJob internal readonly CountdownLatch latch; internal Exception failed; - internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, Random random, CountdownLatch latch) - : base() + internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, CountdownLatch latch) { this.holder = holder; this.writer = writer; @@ -102,7 +99,7 @@ internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, Rando public override void Run() { DirectoryReader currentReader = null; - Random random = LuceneTestCase.Random; + Random random = Random; try { Document doc = new Document(); @@ -188,7 +185,6 @@ public sealed class ReaderThread : ThreadJob internal Exception failed; internal ReaderThread(ReaderHolder holder, CountdownLatch latch) - : base() { this.holder = holder; this.latch = latch; @@ -228,7 +224,6 @@ public override void Run() } failed = e; holder.stop = true; - return; } finally { diff --git a/src/Lucene.Net.Tests/Support/Threading/CountdownLatchTest.cs b/src/Lucene.Net.Tests/Support/Threading/CountdownLatchTest.cs deleted file mode 100644 index 54af9d8fa4..0000000000 --- a/src/Lucene.Net.Tests/Support/Threading/CountdownLatchTest.cs +++ /dev/null @@ -1,345 +0,0 @@ -// Some code adapted from Apache Harmony: https://github.com/apache/harmony/blob/02970cb7227a335edd2c8457ebdde0195a735733/classlib/modules/concurrent/src/test/java/CountDownLatchTest.java - -using J2N.Threading; -using Lucene.Net.Support.Threading; -using NUnit.Framework; -using System; -using System.Threading; -using Assert = Lucene.Net.TestFramework.Assert; - -namespace Lucene.Net.Support.Threading -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - [TestFixture] - public class CountdownLatchTest : JSR166TestCase - { - //public static void main(String[] args) - //{ - // junit.textui.TestRunner.run(suite()); - //} - //public static Test suite() - //{ - // return new TestSuite(CountDownLatchTest.class); - //} - - /** - * negative constructor argument throws IAE - */ - [Test] - public void TestConstructor() - { - try - { - _ = new CountdownLatch(-1); - shouldThrow(); - } - catch (ArgumentOutOfRangeException) - { - // success - } - } - - /** - * getCount returns initial count and decreases after countDown - */ - [Test] - public void TestGetCount() - { - CountdownLatch l = new CountdownLatch(2); - assertEquals(2, l.Count); - l.Signal(); - assertEquals(1, l.Count); - } - - /** - * countDown decrements count when positive and has no effect when zero - */ - [Test] - public void TestSignal() // LUCENENET: renamed from TestCountDown (Signal() was CountDown()) - { - CountdownLatch l = new CountdownLatch(1); - assertEquals(1, l.Count); - l.Signal(); - assertEquals(0, l.Count); - l.Signal(); - assertEquals(0, l.Count); - } - - /** - * await returns after countDown to zero, but not before - */ - [Test] - public void TestWait() // LUCENENET: renamed from TestAwait (Wait() was Await()) - { - CountdownLatch l = new CountdownLatch(2); - - ThreadJob t = new ThreadAnonymousClassForTestWait(this, l); - t.Start(); - try - { - assertEquals(l.Count, 2); - Thread.Sleep(SHORT_DELAY_MS); - l.Signal(); - assertEquals(l.Count, 1); - l.Signal(); - assertEquals(l.Count, 0); - t.Join(); - } - catch (Exception e) when (e.IsInterruptedException()) - { - unexpectedException(); - } - } - - private sealed class ThreadAnonymousClassForTestWait : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestAwait - { - private readonly CountdownLatchTest outerInstance; - private readonly CountdownLatch l; - - public ThreadAnonymousClassForTestWait(CountdownLatchTest outerInstance, CountdownLatch l) - { - this.outerInstance = outerInstance; - this.l = l; - } - - public override void Run() - { - try - { - outerInstance.threadAssertTrue(l.Count > 0); - l.Wait(); - outerInstance.threadAssertTrue(l.Count == 0); - } - catch (Exception e) when (e.IsInterruptedException()) - { - outerInstance.threadUnexpectedException(); - } - } - } - - /** - * timed await returns after countDown to zero - */ - [Test] - public void TestTimedWait() // LUCENENET: renamed from TestTimedAwait (Wait() was Await()) - { - CountdownLatch l = new CountdownLatch(2); - - ThreadJob t = new ThreadAnonymousClassForTestTimedWait(this, l); - t.Start(); - try - { - assertEquals(l.Count, 2); - Thread.Sleep(SHORT_DELAY_MS); - l.Signal(); - assertEquals(l.Count, 1); - l.Signal(); - assertEquals(l.Count, 0); - t.Join(); - } - catch (Exception e) when (e.IsInterruptedException()) - { - unexpectedException(); - } - } - - private sealed class ThreadAnonymousClassForTestTimedWait : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestTimedAwait - { - private readonly CountdownLatchTest outerInstance; - private readonly CountdownLatch l; - - public ThreadAnonymousClassForTestTimedWait(CountdownLatchTest outerInstance, CountdownLatch l) - { - this.outerInstance = outerInstance; - this.l = l; - } - - public override void Run() - { - try - { - outerInstance.threadAssertTrue(l.Count > 0); - outerInstance.threadAssertTrue(l.Wait(TimeSpan.FromMilliseconds(SMALL_DELAY_MS))); - } - catch (Exception e) when (e.IsInterruptedException()) - { - outerInstance.threadUnexpectedException(); - } - } - } - - /** - * await throws IE if interrupted before counted down - */ - [Test] - public void TestWait_InterruptedException() // LUCENENET: renamed from TestAwait_InterruptedException (Wait() was Await()) - { - CountdownLatch l = new CountdownLatch(1); - ThreadJob t = new ThreadAnonymousClassForTestWaitInterruptedException(this, l); - t.Start(); - try - { - assertEquals(l.Count, 1); - t.Interrupt(); - t.Join(); - } - catch (Exception e) when (e.IsInterruptedException()) - { - unexpectedException(); - } - } - - private sealed class ThreadAnonymousClassForTestWaitInterruptedException : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestAwaitInterruptedException - { - private readonly CountdownLatchTest outerInstance; - private readonly CountdownLatch l; - - public ThreadAnonymousClassForTestWaitInterruptedException(CountdownLatchTest outerInstance, CountdownLatch l) - { - this.outerInstance = outerInstance; - this.l = l; - } - - public override void Run() - { - try - { - outerInstance.threadAssertTrue(l.Count > 0); - l.Wait(); - outerInstance.threadShouldThrow(); - } - catch (Exception e) when (e.IsInterruptedException()) - { - // success - } - } - } - - /** - * timed await throws IE if interrupted before counted down - */ - [Test] - public void TestTimedWait_InterruptedException() // LUCENENET: renamed from TestTimedAwait_InterruptedException (Wait() was Await()) - { - CountdownLatch l = new CountdownLatch(1); - ThreadJob t = new ThreadAnonymousClassForTestTimedWaitInterruptedException(this, l); - t.Start(); - try - { - Thread.Sleep(SHORT_DELAY_MS); - assertEquals(l.Count, 1); - t.Interrupt(); - t.Join(); - } - catch (Exception e) when (e.IsInterruptedException()) - { - unexpectedException(); - } - } - - private sealed class ThreadAnonymousClassForTestTimedWaitInterruptedException : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestTimedAwaitInterruptedException - { - private readonly CountdownLatchTest outerInstance; - private readonly CountdownLatch l; - - public ThreadAnonymousClassForTestTimedWaitInterruptedException(CountdownLatchTest outerInstance, CountdownLatch l) - { - this.outerInstance = outerInstance; - this.l = l; - } - - public override void Run() - { - try - { - outerInstance.threadAssertTrue(l.Count > 0); - l.Wait(TimeSpan.FromMilliseconds(MEDIUM_DELAY_MS)); - outerInstance.threadShouldThrow(); - } - catch (Exception e) when (e.IsInterruptedException()) - { - // success - } - } - } - - /** - * timed await times out if not counted down before timeout - */ - [Test] - public void TestWaitTimeout() // LUCENENET: renamed from TestAwaitTimeout (Wait() was Await()) - { - CountdownLatch l = new CountdownLatch(1); - ThreadJob t = new ThreadAnonymousClassForTestWaitTimeout(this, l); - t.Start(); - try - { - assertEquals(l.Count, 1); - t.Join(); - } - catch (Exception e) when (e.IsInterruptedException()) - { - unexpectedException(); - } - } - - private sealed class ThreadAnonymousClassForTestWaitTimeout : ThreadJob // LUCENENET: renamed from ThreadAnonymousClassForTestAwaitTimeout - { - private readonly CountdownLatchTest outerInstance; - private readonly CountdownLatch l; - - public ThreadAnonymousClassForTestWaitTimeout(CountdownLatchTest outerInstance, CountdownLatch l) - { - this.outerInstance = outerInstance; - this.l = l; - } - - public override void Run() - { - try - { - outerInstance.threadAssertTrue(l.Count > 0); - outerInstance.threadAssertFalse(l.Wait(TimeSpan.FromMilliseconds(SHORT_DELAY_MS))); - outerInstance.threadAssertTrue(l.Count > 0); - } - catch (Exception ie) when (ie.IsInterruptedException()) - { - outerInstance.threadUnexpectedException(); - } - } - } - - /** - * toString indicates current count - */ - [Test] - public void TestToString() - { - CountdownLatch s = new CountdownLatch(2); - string us = s.ToString(); - assertTrue(us.IndexOf("Count = 2", StringComparison.Ordinal) >= 0); - s.Signal(); - string s1 = s.ToString(); - assertTrue(s1.IndexOf("Count = 1", StringComparison.Ordinal) >= 0); - s.Signal(); - string s2 = s.ToString(); - assertTrue(s2.IndexOf("Count = 0", StringComparison.Ordinal) >= 0); - } - } -} diff --git a/src/Lucene.Net/Support/Threading/CountdownLatch.cs b/src/Lucene.Net/Support/Threading/CountdownLatch.cs deleted file mode 100644 index 203ef30cdd..0000000000 --- a/src/Lucene.Net/Support/Threading/CountdownLatch.cs +++ /dev/null @@ -1,219 +0,0 @@ -// Some code adapted from Apache Harmony: https://github.com/apache/harmony/blob/02970cb7227a335edd2c8457ebdde0195a735733/classlib/modules/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java -// Other aspects adapted from .NET's CountdownEvent: https://github.com/dotnet/runtime/blob/38496302e54e1b6fb11a998b297492f3fdfbfd0c/src/libraries/System.Threading/src/System/Threading/CountdownEvent.cs - -using System; -using System.Threading; - -namespace Lucene.Net.Support.Threading -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /// - /// A synchronization aid that allows one or more threads to wait until - /// a set of operations being performed in other threads completes. - /// - /// A is initialized with a given count. - /// The methods block until the current count reaches - /// zero due to invocations of the method, after which - /// all waiting threads are released and any subsequent invocations of - /// return immediately. This is a one-shot phenomenon: - /// the count cannot be reset. - /// - /// A is a versatile synchronization tool - /// and can be used for a number of purposes. A - /// initialized with a count of one serves as a simple on/off latch, or gate: - /// all threads invoking wait at the gate until it is - /// opened by a thread invoking . A - /// initialized to N can be used to make - /// one thread wait until N threads have completed some action, or - /// some action has been completed N times. - /// - /// A useful property of a is that it - /// doesn't require that threads calling wait for - /// the count to reach zero before proceeding, it simply prevents any - /// thread from proceeding past an until all - /// threads could pass. - /// - /// LUCENENET specific: This type is similar to , - /// but unlike , - /// can be called any number of times after the count reaches zero without - /// throwing. This matches Java's CountDownLatch.countDown() semantics. - /// - /// - /// LUCENENET specific: Unlike Java's java.util.concurrent.CountDownLatch, - /// which is not AutoCloseable, this type implements - /// because it holds an unmanaged synchronization handle that may be lazily allocated - /// the first time a waiter blocks. Callers should dispose instances when they are no - /// longer needed, typically via a using statement. - /// - internal class CountdownLatch : IDisposable - { - // LUCENENET: the current count and ManualResetEventSlim for signaling, instead of Java's Sync class - private volatile int _currentCount; - private readonly ManualResetEventSlim _event; - - /// - /// Constructs a initialized with the given . - /// - /// The number of times must be invoked - /// before threads can pass through - /// if is negative - public CountdownLatch(int count) - { - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "count < 0"); - } - - _currentCount = count; - _event = new ManualResetEventSlim(count == 0); - } - - /// - /// Causes the current thread to wait until the latch has counted down to - /// zero, unless the thread is interrupted. - /// - /// If the current count is zero then this method returns immediately. - /// - /// If the current count is greater than zero then the current - /// thread becomes disabled for thread scheduling purposes and lies - /// dormant until one of two things happen: - /// - /// The count reaches zero due to invocations of the - /// method; or - /// Some other thread interrupts the current thread. - /// - /// - /// If the current thread is interrupted while waiting, a - /// is thrown by the underlying - /// . - /// - /// if the current thread is - /// interrupted while waiting. - public void Wait() - { - _event.Wait(Timeout.Infinite); - } - - /// - /// Causes the current thread to wait until the latch has counted down to - /// zero, unless the thread is interrupted, or the specified waiting time - /// elapses. - /// - /// If the current count is zero then this method returns immediately - /// with the value true. - /// - /// If the current count is greater than zero then the current - /// thread becomes disabled for thread scheduling purposes and lies - /// dormant until one of three things happen: - /// - /// The count reaches zero due to invocations of the - /// method; or - /// Some other thread interrupts the current thread; or - /// The specified waiting time elapses. - /// - /// - /// If the count reaches zero then the method returns with the - /// value true. - /// - /// If the current thread is interrupted while waiting, a - /// is thrown by the underlying - /// . - /// - /// If the specified waiting time elapses then the value false - /// is returned. If the time is less than or equal to zero, the method - /// will not wait at all. - /// - /// The maximum time to wait. - /// true if the count reached zero, or false - /// if the waiting time elapsed before the count reached zero. - /// if the current thread is - /// interrupted while waiting. - public bool Wait(TimeSpan timeout) - { - return _event.Wait(timeout); - } - - /// - /// Decrements the count of the latch, releasing all waiting threads if - /// the count reaches zero. - /// - /// If the current count is greater than zero then it is decremented. - /// If the new count is zero then all waiting threads are re-enabled for - /// thread scheduling purposes. - /// - /// If the current count equals zero then nothing happens. - /// - public void Signal() - { - if (_currentCount <= 0) - { - // Try to avoid unnecessary decrementing of the count below zero, - // but a couple concurrent races below zero are fine - return; - } - - int newCount = Interlocked.Decrement(ref _currentCount); - if (newCount <= 0) - { - _event.Set(); - } - } - - /// - /// Returns the current count. - /// - /// This property is typically used for debugging and testing purposes. - /// - /// - /// In Java, getCount() returns long, but internally it's - /// always an int. Here we retain the Java return type. There is no need - /// to make the internal count 64-bit. - /// - /// The returned value is clamped at zero. The internal counter may - /// briefly drop below zero under concurrent - /// calls that race past the early-return guard, but that detail is - /// hidden here to match Java's getCount() semantics, which never - /// returns a negative value. - /// - public long Count => Math.Max(0, _currentCount); - - /// - /// Returns a string identifying this latch, as well as its state. - /// The state, in brackets, includes the String "Count =" - /// followed by the current count. - /// - /// a string identifying this latch, as well as its state - public override string ToString() => $"{base.ToString()}[Count = {_currentCount}]"; - - /// - /// Releases the unmanaged synchronization resources held by this latch. - /// - /// - /// LUCENENET specific: Java's CountDownLatch is not AutoCloseable - /// and does not need to be closed. This .NET port may hold an unmanaged - /// synchronization handle that is lazily allocated the first time a waiter - /// blocks, so it must be disposed when no longer needed. - /// - public void Dispose() - { - _event.Dispose(); - GC.SuppressFinalize(this); - } - } -} From fff4a9969acfaf750d219f9601de53806f67c0d2 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Mon, 22 Jun 2026 16:32:42 -0600 Subject: [PATCH 11/20] Remove unused namespaces --- .../Analysis/BaseTokenStreamTestCase.cs | 1 - .../Index/BaseDocValuesFormatTestCase.cs | 3 --- src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs | 2 -- src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs | 2 -- src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs | 2 -- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 1 - src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs | 2 -- src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs | 2 -- src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs | 2 -- src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs | 2 -- src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs | 2 -- src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs | 2 -- src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs | 2 -- 13 files changed, 25 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index b55cebc24d..b760688e90 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -5,7 +5,6 @@ using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Support; -using Lucene.Net.Support.Threading; using Lucene.Net.Util; using RandomizedTesting.Generators; using System; diff --git a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs index 22198db310..09cda0c325 100644 --- a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs @@ -7,14 +7,11 @@ using Lucene.Net.Index.Extensions; using Lucene.Net.Search; using Lucene.Net.Store; -using Lucene.Net.Support; -using Lucene.Net.Support.Threading; using Lucene.Net.Util; using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.Globalization; -using System.Threading; using static Lucene.Net.Index.TermsEnum; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; diff --git a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs index e9483f169e..75d242b632 100644 --- a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs @@ -10,13 +10,11 @@ using Lucene.Net.Search; using Lucene.Net.Store; using Lucene.Net.Support; -using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.IO; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs index 43db8d7458..1f5721c874 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs @@ -3,10 +3,8 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Search; -using Lucene.Net.Support.Threading; using NUnit.Framework; using System; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; namespace Lucene.Net.Index diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs index aa02f6d7fa..059750115d 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs @@ -2,12 +2,10 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Search; -using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; using System.Collections.Generic; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 9e5bcaed72..f652036bc7 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -10,7 +10,6 @@ using Lucene.Net.Index.Extensions; using Lucene.Net.Search; using Lucene.Net.Support; -using Lucene.Net.Support.Threading; using Lucene.Net.Util; using NUnit.Framework; using RandomizedTesting.Generators; diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs index 2699a419db..542511be3c 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs @@ -5,7 +5,6 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Store; -using Lucene.Net.Support; using Lucene.Net.Support.IO; using Lucene.Net.Util; using NUnit.Framework; @@ -18,7 +17,6 @@ using JCG = J2N.Collections.Generic; // ReSharper disable once RedundantUsingDirective - keep until we have an analyzer to look out for accidental NUnit asserts using Assert = Lucene.Net.TestFramework.Assert; -using Lucene.Net.Support.Threading; namespace Lucene.Net.Index { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs index 9d434b8960..fe2deaf349 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -6,10 +6,8 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Store; -using Lucene.Net.Support.Threading; using Lucene.Net.Util; using NUnit.Framework; -using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.Threading; diff --git a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs index f702a92035..8fb3539ab7 100644 --- a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs @@ -3,12 +3,10 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Support; -using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; using System.Collections.Generic; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; diff --git a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs index d52d3b6d34..db3abd66b1 100644 --- a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs @@ -3,12 +3,10 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using Lucene.Net.Support; -using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; using System.Collections.Generic; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; diff --git a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs index 8160ef303b..c665cc0d5c 100644 --- a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs +++ b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs @@ -1,9 +1,7 @@ using J2N.Threading; using Lucene.Net.Documents; -using Lucene.Net.Support.Threading; using NUnit.Framework; using System; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; namespace Lucene.Net.Search diff --git a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs index b83507fa52..95854014e2 100644 --- a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs +++ b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Threading; -using Lucene.Net.Support.Threading; using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; diff --git a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs index c98c0fdab1..1de3e16fc6 100644 --- a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs +++ b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs @@ -1,10 +1,8 @@ using J2N.Collections.Generic.Extensions; using J2N.Threading; -using Lucene.Net.Support.Threading; using NUnit.Framework; using System; using System.Collections.Generic; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; From bb2f3b8ba6b4cbbf46c56887bbb9941c4d454ee7 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Wed, 1 Jul 2026 16:16:53 -0600 Subject: [PATCH 12/20] PR feedback --- .../Analysis/BaseTokenStreamTestCase.cs | 2 +- .../Index/BaseDocValuesFormatTestCase.cs | 4 +- .../Index/TestBagOfPositions.cs | 4 +- .../Index/TestBagOfPostings.cs | 4 +- .../Index/TestBinaryDocValuesUpdates.cs | 2 +- .../Index/TestConcurrentMergeScheduler.cs | 2 +- .../Index/TestDocValuesIndexing.cs | 2 +- .../Index/TestDocValuesWithThreads.cs | 2 +- .../Index/TestDocumentsWriterDeleteQueue.cs | 3 +- .../Index/TestDocumentsWriterStallControl.cs | 11 +++- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 51 ++++++------------- .../Index/TestIndexWriterDelete.cs | 5 +- .../Index/TestIndexWriterExceptions.cs | 1 + .../Index/TestIndexWriterNRTIsCurrent.cs | 2 +- .../Index/TestIndexWriterThreadsToSegments.cs | 19 +++---- .../Index/TestIndexWriterWithThreads.cs | 14 ++++- .../Index/TestMixedDocValuesUpdates.cs | 2 +- .../Index/TestNumericDocValuesUpdates.cs | 2 +- .../Search/TestAutomatonQuery.cs | 2 +- .../TestControlledRealTimeReopenThread.cs | 5 +- .../Search/TestLiveFieldValues.cs | 2 +- .../Search/TestSameScoresWithThreads.cs | 2 +- .../Search/TestSearcherManager.cs | 5 +- 23 files changed, 72 insertions(+), 76 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index b760688e90..e6e612d9e4 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -710,7 +710,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in // now test with multiple threads: note we do the EXACT same thing we did before in each thread, // so this should only really fail from another thread if its an actual thread problem int numThreads = TestUtil.NextInt32(random, 2, 4); - using var startingGun = new CountdownLatch(1); + using var startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET var threads = new AnalysisThread[numThreads]; for (int i = 0; i < threads.Length; i++) { diff --git a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs index 09cda0c325..a7e8acbfc9 100644 --- a/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs @@ -3241,7 +3241,7 @@ public virtual void TestThreads() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - using CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass(ir, startingGun); @@ -3377,7 +3377,7 @@ public virtual void TestThreads2() using DirectoryReader ir = DirectoryReader.Open(dir); int numThreads = TestUtil.NextInt32(Random, 2, 7); ThreadJob[] threads = new ThreadJob[numThreads]; - using CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClass2(ir, startingGun); diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs index feb9d39441..4a1d5f69cf 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs @@ -1,7 +1,6 @@ using J2N.Collections.Generic.Extensions; using J2N.Threading; using Lucene.Net.Documents; -using Lucene.Net.Support.Threading; using NUnit.Framework; using RandomizedTesting.Generators; using System; @@ -9,7 +8,6 @@ using System.Collections.Generic; using System.Globalization; using System.Text; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; @@ -117,7 +115,7 @@ public virtual void Test() // else just positions ThreadJob[] threads = new ThreadJob[threadCount]; - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET for (int threadID = 0; threadID < threadCount; threadID++) { diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs b/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs index bee8e28357..0e87e94f5c 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPostings.cs @@ -1,14 +1,12 @@ using J2N.Collections.Generic.Extensions; using J2N.Threading; using Lucene.Net.Documents; -using Lucene.Net.Support.Threading; using NUnit.Framework; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Text; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; @@ -96,7 +94,7 @@ public virtual void Test() } ThreadJob[] threads = new ThreadJob[threadCount]; - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET for (int threadID = 0; threadID < threadCount; threadID++) { diff --git a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs index 75d242b632..d9e3c1665e 100644 --- a/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs @@ -1199,7 +1199,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountdownLatch done = new CountdownLatch(numThreads); + using CountdownLatch done = new CountdownLatch(numThreads); // LUCENENET: CountdownLatch is disposable in .NET AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens diff --git a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs index 5725c5f147..aaa566d8d0 100644 --- a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs +++ b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs @@ -289,7 +289,7 @@ public virtual void TestMaxMergeCount() int maxMergeCount = TestUtil.NextInt32(Random, 1, 5); int maxMergeThreads = TestUtil.NextInt32(Random, 1, maxMergeCount); - CountdownLatch enoughMergesWaiting = new CountdownLatch(maxMergeCount); + using CountdownLatch enoughMergesWaiting = new CountdownLatch(maxMergeCount); // LUCENENET: CountdownLatch is disposable in .NET AtomicInt32 runningMergeCount = new AtomicInt32(0); AtomicBoolean failed = new AtomicBoolean(); diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs index 1f5721c874..79126d072f 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs @@ -512,7 +512,7 @@ public virtual void TestMixedTypesDifferentThreads() Directory dir = NewDirectory(); IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET AtomicBoolean hitExc = new AtomicBoolean(); ThreadJob[] threads = new ThreadJob[3]; for (int i = 0; i < 3; i++) diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs index 059750115d..4459f9985a 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs @@ -76,7 +76,7 @@ public virtual void Test() int numThreads = TestUtil.NextInt32(Random, 2, 5); IList threads = new JCG.List(); - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET for (int t = 0; t < numThreads; t++) { Random threadRandom = new J2N.Randomizer(Random.NextInt64()); diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs index 372e7d733b..fab4c0f058 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Reflection; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; using JCG = J2N.Collections.Generic; @@ -258,7 +257,7 @@ public virtual void TestStressDeleteQueue() ids[i] = Random.Next(); uniqueValues.Add(new Term("id", ids[i].ToString())); } - CountdownLatch latch = new CountdownLatch(1); + using CountdownLatch latch = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET AtomicInt32 index = new AtomicInt32(0); int numThreads = 2 + Random.Next(5); UpdateThread[] threads = new UpdateThread[numThreads]; diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs index 6f16b70a49..829bfecd2b 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs @@ -128,7 +128,7 @@ public virtual void TestAccquireReleaseRace() int numStallers = AtLeast(1); int numReleasers = AtLeast(1); int numWaiters = AtLeast(1); - var sync = new Synchronizer(numStallers + numReleasers, numStallers + numReleasers + numWaiters); + using var sync = new Synchronizer(numStallers + numReleasers, numStallers + numReleasers + numWaiters); var threads = new ThreadJob[numReleasers + numStallers + numWaiters]; IList exceptions = new SynchronizedList(); for (int i = 0; i < numReleasers; i++) @@ -440,7 +440,7 @@ public static void AwaitState(ThreadState state, params ThreadJob[] threads) } } - public sealed class Synchronizer + public sealed class Synchronizer : IDisposable // LUCENENET: CountdownLatch is disposable in .NET { internal volatile CountdownLatch waiter; internal volatile CountdownLatch updateJoin; @@ -462,6 +462,13 @@ public bool Await() { return waiter.Wait(TimeSpan.FromSeconds(10)); } + + public void Dispose() + { + waiter?.Dispose(); + updateJoin?.Dispose(); + leftCheckpoint?.Dispose(); + } } } } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index f652036bc7..c0672ab45a 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -2430,7 +2430,7 @@ public virtual void TestStopwordsPosIncHole2() dir.Dispose(); } - // LUCENENET: this test was removed upstream in commit 2cfcdcc (LUCENE-5871), + // LUCENENET: TestOtherFiles2 was removed upstream in commit 2cfcdcc (LUCENE-5871), // which is the backport that fixes #1284. The fix changes Dispose() so that a // graceful close runs a final CommitInternal followed by RollbackInternal, // which checkpoints the deleter and removes unreferenced files. The original @@ -2438,36 +2438,6 @@ public virtual void TestStopwordsPosIncHole2() // Dispose() therefore no longer holds — _a.frq is now deleted on the very // first close, before the second IndexWriter is opened. We keep the test // commented out at its original location for porting reference. - // - //// here we do better, there is no current segments file, so we don't delete anything. - //// however, if you actually go and make a commit, the next time you run indexwriter - //// this file will be gone. - //[Test] - //public virtual void TestOtherFiles2() - //{ - // Directory dir = NewDirectory(); - // try - // { - // // Create my own random file: - // IndexOutput @out = dir.CreateOutput("_a.frq", NewIOContext(Random)); - // @out.WriteByte((byte)42); - // @out.Dispose(); - // - // new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).Dispose(); - // - // Assert.IsTrue(SlowFileExists(dir, "_a.frq")); - // - // IndexWriter iw = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - // iw.AddDocument(new Document()); - // iw.Dispose(); - // - // Assert.IsFalse(SlowFileExists(dir, "_a.frq")); - // } - // finally - // { - // dir.Dispose(); - // } - //} // LUCENE-4398 [Test] @@ -2950,11 +2920,13 @@ public virtual void TestDeleteSameTermAcrossFields() dir.Dispose(); } + + // LUCENENET: backport LUCENE-4246 (Lucene 5.0) test [Test] public virtual void TestHasUncommittedChangesAfterException() { Analyzer analyzer = new MockAnalyzer(Random); - AssumeTrue("requires doc values", DefaultCodecSupportsDocValues); + AssumeTrue("requires doc values", DefaultCodecSupportsDocValues); // LUCENENET: ensure codec supports doc values Directory directory = NewDirectory(); // we don't use RandomIndexWriter because it might add more docvalues than we expect !!!! @@ -2979,6 +2951,7 @@ public virtual void TestHasUncommittedChangesAfterException() directory.Dispose(); } + // LUCENENET: backport LUCENE-4246 (Lucene 5.0) test [Test] public virtual void TestDoubleClose() { @@ -2991,6 +2964,7 @@ public virtual void TestDoubleClose() dir.Dispose(); } + // LUCENENET: backport LUCENE-4246 (Lucene 5.0) test [Test] public virtual void TestRollbackThenClose() { @@ -3003,6 +2977,7 @@ public virtual void TestRollbackThenClose() dir.Dispose(); } + // LUCENENET: backport LUCENE-4246 (Lucene 5.0) test [Test] public virtual void TestCloseThenRollback() { @@ -3015,13 +2990,15 @@ public virtual void TestCloseThenRollback() dir.Dispose(); } + [LuceneNetSpecific] [Test] public virtual void TestRollbackWhileMergeIsRunning() { Directory dir = NewDirectory(); - CountdownLatch mergeStarted = new CountdownLatch(1); - CountdownLatch closeStarted = new CountdownLatch(1); + // LUCENENET: CountdownLatch is disposable in .NET + using CountdownLatch mergeStarted = new CountdownLatch(1); + using CountdownLatch closeStarted = new CountdownLatch(1); IndexWriterConfig iwc = NewIndexWriterConfig(Random, TEST_VERSION_CURRENT, new MockAnalyzer(Random)); LogDocMergePolicy mp = new LogDocMergePolicy(); @@ -3144,11 +3121,13 @@ public virtual void TestClosingNRTReaderDoesNotCorruptYourIndex() /// /// Make sure that close waits for any still-running commits. + // LUCENENET: backport LUCENE-4246 (Lucene 5.0) test [Test] public virtual void TestCloseDuringCommit() { - CountdownLatch startCommit = new CountdownLatch(1); - CountdownLatch finishCommit = new CountdownLatch(1); + // LUCENENET: CountdownLatch is disposable in .NET + using CountdownLatch startCommit = new CountdownLatch(1); + using CountdownLatch finishCommit = new CountdownLatch(1); Directory dir = NewDirectory(); IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, null); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs index 542511be3c..25c5402d19 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterDelete.cs @@ -354,8 +354,9 @@ public virtual void TestDeleteAllNoDeadLock() RandomIndexWriter modifier = new RandomIndexWriter(Random, dir); int numThreads = AtLeast(2); ThreadJob[] threads = new ThreadJob[numThreads]; - CountdownLatch latch = new CountdownLatch(1); - CountdownLatch doneLatch = new CountdownLatch(numThreads); + // LUCENENET: CountdownLatch is disposable in .NET + using CountdownLatch latch = new CountdownLatch(1); + using CountdownLatch doneLatch = new CountdownLatch(numThreads); for (int i = 0; i < numThreads; i++) { int offset = i; diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs index 74fd3eeb8d..52770a8f7e 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs @@ -1265,6 +1265,7 @@ protected override void Dispose(bool disposing) /// /// If IW hits OOME during indexing, it should refuse to commit any further changes. + // LUCENENET: backport LUCENE-5871 (Lucene 4.10.0) test [Test] public virtual void TestOutOfMemoryErrorRollback() { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs index 7a3e8935de..5a5c335065 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterNRTIsCurrent.cs @@ -50,7 +50,7 @@ public virtual void TestIsCurrentWithThreads() IndexWriter writer = new IndexWriter(dir, conf); ReaderHolder holder = new ReaderHolder(); ReaderThread[] threads = new ReaderThread[AtLeast(3)]; - CountdownLatch latch = new CountdownLatch(1); + using CountdownLatch latch = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET WriterThread writerThread = new WriterThread(holder, writer, AtLeast(500), latch); for (int i = 0; i < threads.Length; i++) { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs index fe2deaf349..7398c57bb9 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -42,15 +42,16 @@ public virtual void TestSegmentCountOnFlushBasic() { Directory dir = NewDirectory(); IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))); - CountdownLatch startingGun = new CountdownLatch(1); - CountdownLatch startDone = new CountdownLatch(2); - CountdownLatch middleGun = new CountdownLatch(1); - CountdownLatch finalGun = new CountdownLatch(1); + // LUCENENET: CountdownLatch is disposable in .NET + using CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startDone = new CountdownLatch(2); + using CountdownLatch middleGun = new CountdownLatch(1); + using CountdownLatch finalGun = new CountdownLatch(1); ThreadJob[] threads = new ThreadJob[2]; for (int i = 0; i < threads.Length; i++) { int threadID = i; - threads[i] = new ThreadAnonymousClassForSegmentCountOnFlushBasic(w, threadID, startingGun, startDone, middleGun, finalGun); + threads[i] = new ThreadSegmentCountOnFlushBasicAnonymousClass(w, threadID, startingGun, startDone, middleGun, finalGun); threads[i].Start(); } @@ -80,7 +81,7 @@ public virtual void TestSegmentCountOnFlushBasic() dir.Dispose(); } - private sealed class ThreadAnonymousClassForSegmentCountOnFlushBasic : ThreadJob + private sealed class ThreadSegmentCountOnFlushBasicAnonymousClass : ThreadJob { private readonly IndexWriter w; private readonly int threadID; @@ -89,7 +90,7 @@ private sealed class ThreadAnonymousClassForSegmentCountOnFlushBasic : ThreadJob private readonly CountdownLatch middleGun; private readonly CountdownLatch finalGun; - public ThreadAnonymousClassForSegmentCountOnFlushBasic(IndexWriter w, int threadID, CountdownLatch startingGun, CountdownLatch startDone, CountdownLatch middleGun, CountdownLatch finalGun) + public ThreadSegmentCountOnFlushBasicAnonymousClass(IndexWriter w, int threadID, CountdownLatch startingGun, CountdownLatch startDone, CountdownLatch middleGun, CountdownLatch finalGun) { this.w = w; this.threadID = threadID; @@ -309,7 +310,7 @@ public virtual void TestManyThreadsClose() RandomIndexWriter w = new RandomIndexWriter(Random, dir); w.DoRandomForceMerge = false; ThreadJob[] threads = new ThreadJob[TestUtil.NextInt32(Random, 4, 30)]; - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET for (int i = 0; i < threads.Length; i++) { threads[i] = new ThreadAnonymousClassForManyThreadsClose(w, startingGun); @@ -387,7 +388,7 @@ public virtual void TestDocsStuckInRAMForever() iwc.SetCodec(codec); iwc.SetMergePolicy(NoMergePolicy.NO_COMPOUND_FILES); IndexWriter w = new IndexWriter(dir, iwc); - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET ThreadJob[] threads = new ThreadJob[2]; for (int i = 0; i < threads.Length; i++) { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs index bfed359471..a86e3f52d5 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs @@ -561,7 +561,7 @@ public virtual void TestIOExceptionDuringWriteSegmentWithThreadsOnlyOnce() public virtual void TestOpenTwoIndexWritersOnDifferentThreads() { Directory dir = NewDirectory(); - CountdownLatch oneIWConstructed = new CountdownLatch(1); + using CountdownLatch oneIWConstructed = new CountdownLatch(1); // LUCENENET: CoutdownLatch is disposable in .NET DelayedIndexAndCloseRunnable thread1 = new DelayedIndexAndCloseRunnable(dir, oneIWConstructed); DelayedIndexAndCloseRunnable thread2 = new DelayedIndexAndCloseRunnable(dir, oneIWConstructed); @@ -594,10 +594,14 @@ public virtual void TestOpenTwoIndexWritersOnDifferentThreads() finally { dir.Dispose(); + + // LUCENENET: CountdownLatch is disposable in .NET + thread1.Dispose(); + thread2.Dispose(); } } - internal class DelayedIndexAndCloseRunnable : ThreadJob + internal class DelayedIndexAndCloseRunnable : ThreadJob, IDisposable { private readonly Directory dir; internal bool failed = false; @@ -636,6 +640,12 @@ public override void Run() // return; // LUCENENET: redundant return } } + + // LUCENENET: CountdownLatch is disposable in .NET + public void Dispose() + { + startIndexing?.Dispose(); + } } // LUCENE-4147 diff --git a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs index 8fb3539ab7..1b1e9d12c8 100644 --- a/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestMixedDocValuesUpdates.cs @@ -267,7 +267,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountdownLatch done = new CountdownLatch(numThreads); + using CountdownLatch done = new CountdownLatch(numThreads); // LUCENENET: CountdownLatch is disposable in .NET AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens diff --git a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs index db3abd66b1..872f2ce9d5 100644 --- a/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs +++ b/src/Lucene.Net.Tests/Index/TestNumericDocValuesUpdates.cs @@ -1138,7 +1138,7 @@ public virtual void TestStressMultiThreading() writer.AddDocument(doc); } - CountdownLatch done = new CountdownLatch(numThreads); + using CountdownLatch done = new CountdownLatch(numThreads); // LUCENENET: CountdownLatch is disposable in .NET AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100)); // same thread updates a field as well as reopens diff --git a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs index c665cc0d5c..5d6deec36b 100644 --- a/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs +++ b/src/Lucene.Net.Tests/Search/TestAutomatonQuery.cs @@ -229,7 +229,7 @@ public virtual void TestHashCodeWithThreads() { queries[i] = new AutomatonQuery(new Term("bogus", "bogus"), AutomatonTestUtil.RandomAutomaton(Random)); } - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET int numThreads = TestUtil.NextInt32(Random, 2, 5); ThreadJob[] threads = new ThreadJob[numThreads]; for (int threadID = 0; threadID < numThreads; threadID++) diff --git a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs index e9e258a31e..33c2b12cca 100644 --- a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs +++ b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs @@ -396,8 +396,9 @@ public virtual void TestThreadStarvationNoDeleteNRTReader() IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)); conf.SetMergePolicy(Random.NextBoolean() ? NoMergePolicy.COMPOUND_FILES : NoMergePolicy.NO_COMPOUND_FILES); Directory d = NewDirectory(); - CountdownLatch latch = new CountdownLatch(1); - CountdownLatch signal = new CountdownLatch(1); + // LUCENENET: CountdownLatch is disposable in .NET + using CountdownLatch latch = new CountdownLatch(1); + using CountdownLatch signal = new CountdownLatch(1); LatchedIndexWriter _writer = new LatchedIndexWriter(d, conf, latch, signal); TrackingIndexWriter writer = new TrackingIndexWriter(_writer); diff --git a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs index 95854014e2..f148446fa6 100644 --- a/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs +++ b/src/Lucene.Net.Tests/Search/TestLiveFieldValues.cs @@ -67,7 +67,7 @@ public virtual void Test() Console.WriteLine(numThreads + " threads"); } - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET IList threads = new JCG.List(); int iters = AtLeast(1000); diff --git a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs index 1de3e16fc6..1a84c89645 100644 --- a/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs +++ b/src/Lucene.Net.Tests/Search/TestSameScoresWithThreads.cs @@ -90,7 +90,7 @@ public virtual void Test() if (answers.Count > 0) { - CountdownLatch startingGun = new CountdownLatch(1); + using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET int numThreads = TestUtil.NextInt32(Random, 2, 5); ThreadJob[] threads = new ThreadJob[numThreads]; for (int threadID = 0; threadID < numThreads; threadID++) diff --git a/src/Lucene.Net.Tests/Search/TestSearcherManager.cs b/src/Lucene.Net.Tests/Search/TestSearcherManager.cs index 31888bc726..7262756e5d 100644 --- a/src/Lucene.Net.Tests/Search/TestSearcherManager.cs +++ b/src/Lucene.Net.Tests/Search/TestSearcherManager.cs @@ -272,8 +272,9 @@ public virtual void TestIntermediateClose() IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetMergeScheduler(new ConcurrentMergeScheduler())); writer.AddDocument(new Document()); writer.Commit(); - CountdownLatch awaitEnterWarm = new CountdownLatch(1); - CountdownLatch awaitClose = new CountdownLatch(1); + // LUCENENET: CountdownLatch is disposable in .NET + using CountdownLatch awaitEnterWarm = new CountdownLatch(1); + using CountdownLatch awaitClose = new CountdownLatch(1); AtomicBoolean triedReopen = new AtomicBoolean(false); //TaskScheduler es = Random().NextBoolean() ? null : Executors.newCachedThreadPool(new NamedThreadFactory("testIntermediateClose")); TaskScheduler es = Random.NextBoolean() ? null : TaskScheduler.Default; From f1c1d4889ab2146502708cf9355bdc3000a80225 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 2 Jul 2026 17:13:55 -0600 Subject: [PATCH 13/20] Revert change to TestMaxFailuresRule --- src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs b/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs index 968dd3cd1b..efbd87a970 100644 --- a/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs +++ b/src/Lucene.Net.Tests/Util/TestMaxFailuresRule.cs @@ -148,7 +148,7 @@ public override void TestFinished(Description description) public class Nested2 : WithNestedTests.AbstractNestedTest { public const int TOTAL_ITERS = 10; - public static CountdownLatch Die; + public static CountDownLatch Die; public static Thread Zombie; public static int TestNum; @@ -158,7 +158,7 @@ public class Nested2 : WithNestedTests.AbstractNestedTest public static void Setup() { Debug.Assert(Zombie == null); - Die = new CountdownLatch(1); + Die = new CountDownLatch(1); TestNum = 0; } From 76204674d653b7cd0a3912757a6fb8e1e38acb0e Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 2 Jul 2026 17:17:41 -0600 Subject: [PATCH 14/20] Add comment for backport fix --- src/Lucene.Net/Index/DocumentsWriter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Lucene.Net/Index/DocumentsWriter.cs b/src/Lucene.Net/Index/DocumentsWriter.cs index 1cada48a1d..32fe6fb4dd 100644 --- a/src/Lucene.Net/Index/DocumentsWriter.cs +++ b/src/Lucene.Net/Index/DocumentsWriter.cs @@ -538,6 +538,7 @@ internal bool UpdateDocuments(IEnumerable> docs, An int dwptNumDocs = dwpt.NumDocsInRAM; try { + // LUCENENET: backport fix from Lucene 4.10 in LUCENE-5871 dwpt.UpdateDocuments(docs, analyzer, delTerm); } finally From a483e36406e79d09d20a63f2392bd8881924145e Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 2 Jul 2026 17:20:00 -0600 Subject: [PATCH 15/20] Add comment for backport fix --- src/Lucene.Net/Index/DocumentsWriter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Lucene.Net/Index/DocumentsWriter.cs b/src/Lucene.Net/Index/DocumentsWriter.cs index 32fe6fb4dd..8cb6ea9ab6 100644 --- a/src/Lucene.Net/Index/DocumentsWriter.cs +++ b/src/Lucene.Net/Index/DocumentsWriter.cs @@ -538,11 +538,12 @@ internal bool UpdateDocuments(IEnumerable> docs, An int dwptNumDocs = dwpt.NumDocsInRAM; try { - // LUCENENET: backport fix from Lucene 4.10 in LUCENE-5871 + // LUCENENET: backport fix from Lucene 4.10.0 in LUCENE-5871 dwpt.UpdateDocuments(docs, analyzer, delTerm); } finally { + // LUCENENET: backport fix from Lucene 4.10.0 in LUCENE-5871 // We don't know how many documents were actually // counted as indexed, so we must subtract here to // accumulate our separate counter: @@ -589,9 +590,11 @@ internal bool UpdateDocument(IEnumerable doc, Analyzer analyzer try { dwpt.UpdateDocument(doc, analyzer, delTerm); + // LUCENENET: removed incrementAndGet call in backport of fix from Lucene 4.10.0 in LUCENE-5871 } finally { + // LUCENENET: backport fix from Lucene 4.10.0 in LUCENE-5871 // We don't know whether the document actually // counted as being indexed, so we must subtract here to // accumulate our separate counter: @@ -739,6 +742,7 @@ internal void SubtractFlushedNumDocs(int numFlushed) { oldValue = numDocsInRAM; } + // LUCENENET: backport fix from Lucene 4.10.0 in LUCENE-5871 if (Debugging.AssertsEnabled) Debugging.Assert(numDocsInRAM >= 0); } From 40a0a6cc4a65868b9c86d9f12bdf1577674a5565 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Sun, 5 Jul 2026 14:53:52 -0600 Subject: [PATCH 16/20] PR feedback: anonymous class naming --- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 24 +++++++++---------- .../Index/TestIndexWriterThreadsToSegments.cs | 18 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index c0672ab45a..6e6f4acc3e 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -3004,9 +3004,9 @@ public virtual void TestRollbackWhileMergeIsRunning() LogDocMergePolicy mp = new LogDocMergePolicy(); mp.MergeFactor = 2; iwc.SetMergePolicy(mp); - iwc.SetInfoStream(new InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(closeStarted)); + iwc.SetInfoStream(new RollbackWhileMergeIsRunningInfoStreamAnonymousClass(closeStarted)); - iwc.SetMergeScheduler(new ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(mergeStarted, closeStarted)); + iwc.SetMergeScheduler(new RollbackWhileMergeIsRunningConcurrentMergeSchedulerAnonymousClass(mergeStarted, closeStarted)); IndexWriter w = new IndexWriter(dir, iwc); w.AddDocument(new Document()); w.Commit(); @@ -3016,11 +3016,11 @@ public virtual void TestRollbackWhileMergeIsRunning() dir.Dispose(); } - private sealed class InfoStreamAnonymousClassForRollbackWhileMergeIsRunning : InfoStream + private sealed class RollbackWhileMergeIsRunningInfoStreamAnonymousClass : InfoStream { private readonly CountdownLatch closeStarted; - public InfoStreamAnonymousClassForRollbackWhileMergeIsRunning(CountdownLatch closeStarted) + public RollbackWhileMergeIsRunningInfoStreamAnonymousClass(CountdownLatch closeStarted) { this.closeStarted = closeStarted; } @@ -3043,12 +3043,12 @@ protected override void Dispose(bool disposing) } } - private sealed class ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning : ConcurrentMergeScheduler + private sealed class RollbackWhileMergeIsRunningConcurrentMergeSchedulerAnonymousClass : ConcurrentMergeScheduler { private readonly CountdownLatch mergeStarted; private readonly CountdownLatch closeStarted; - public ConcurrentMergeSchedulerAnonymousClassForRollbackWhileMergeIsRunning(CountdownLatch mergeStarted, CountdownLatch closeStarted) + public RollbackWhileMergeIsRunningConcurrentMergeSchedulerAnonymousClass(CountdownLatch mergeStarted, CountdownLatch closeStarted) { this.mergeStarted = mergeStarted; this.closeStarted = closeStarted; @@ -3132,10 +3132,10 @@ public virtual void TestCloseDuringCommit() Directory dir = NewDirectory(); IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, null); // infostream that "takes a long time" to commit - iwc.SetInfoStream(new InfoStreamAnonymousClassForCloseDuringCommit(startCommit)); + iwc.SetInfoStream(new CloseDuringCommitInfoStreamAnonymousClass(startCommit)); IndexWriter iw = new IndexWriter(dir, iwc); Document doc = new Document(); - new ThreadAnonymousClassForCloseDuringCommit(iw, finishCommit).Start(); + new CloseDuringCommitThreadAnonymousClass(iw, finishCommit).Start(); startCommit.Wait(); try { @@ -3150,11 +3150,11 @@ public virtual void TestCloseDuringCommit() dir.Dispose(); } - private sealed class InfoStreamAnonymousClassForCloseDuringCommit : InfoStream + private sealed class CloseDuringCommitInfoStreamAnonymousClass : InfoStream { private readonly CountdownLatch startCommit; - public InfoStreamAnonymousClassForCloseDuringCommit(CountdownLatch startCommit) + public CloseDuringCommitInfoStreamAnonymousClass(CountdownLatch startCommit) { this.startCommit = startCommit; } @@ -3185,12 +3185,12 @@ protected override void Dispose(bool disposing) } } - private sealed class ThreadAnonymousClassForCloseDuringCommit : ThreadJob + private sealed class CloseDuringCommitThreadAnonymousClass : ThreadJob { private readonly IndexWriter iw; private readonly CountdownLatch finishCommit; - public ThreadAnonymousClassForCloseDuringCommit(IndexWriter iw, CountdownLatch finishCommit) + public CloseDuringCommitThreadAnonymousClass(IndexWriter iw, CountdownLatch finishCommit) { this.iw = iw; this.finishCommit = finishCommit; diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs index 7398c57bb9..96dda5a661 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterThreadsToSegments.cs @@ -235,7 +235,7 @@ public virtual void TestSegmentCountOnFlushRandom() for (int i = 0; i < threads.Length; i++) { - threads[i] = new ThreadAnonymousClassForSegmentCountOnFlushRandom(w, indexingCount, maxThreadCount, barrier, ITERS); + threads[i] = new SegmentCountOnFlushRandomThreadAnonymousClass(w, indexingCount, maxThreadCount, barrier, ITERS); threads[i].Start(); } @@ -247,7 +247,7 @@ public virtual void TestSegmentCountOnFlushRandom() IOUtils.Dispose(checker, w, dir); } - private sealed class ThreadAnonymousClassForSegmentCountOnFlushRandom : ThreadJob + private sealed class SegmentCountOnFlushRandomThreadAnonymousClass : ThreadJob { private readonly IndexWriter w; private readonly AtomicInt32 indexingCount; @@ -255,7 +255,7 @@ private sealed class ThreadAnonymousClassForSegmentCountOnFlushRandom : ThreadJo private readonly Barrier barrier; private readonly int iters; - public ThreadAnonymousClassForSegmentCountOnFlushRandom(IndexWriter w, AtomicInt32 indexingCount, AtomicInt32 maxThreadCount, Barrier barrier, int iters) + public SegmentCountOnFlushRandomThreadAnonymousClass(IndexWriter w, AtomicInt32 indexingCount, AtomicInt32 maxThreadCount, Barrier barrier, int iters) { this.w = w; this.indexingCount = indexingCount; @@ -313,7 +313,7 @@ public virtual void TestManyThreadsClose() using CountdownLatch startingGun = new CountdownLatch(1); // LUCENENET: CountdownLatch is disposable in .NET for (int i = 0; i < threads.Length; i++) { - threads[i] = new ThreadAnonymousClassForManyThreadsClose(w, startingGun); + threads[i] = new ManyThreadsCloseThreadAnonymousClass(w, startingGun); threads[i].Start(); } @@ -340,12 +340,12 @@ public virtual void TestManyThreadsClose() dir.Dispose(); } - private sealed class ThreadAnonymousClassForManyThreadsClose : ThreadJob + private sealed class ManyThreadsCloseThreadAnonymousClass : ThreadJob { private readonly RandomIndexWriter w; private readonly CountdownLatch startingGun; - public ThreadAnonymousClassForManyThreadsClose(RandomIndexWriter w, CountdownLatch startingGun) + public ManyThreadsCloseThreadAnonymousClass(RandomIndexWriter w, CountdownLatch startingGun) { this.w = w; this.startingGun = startingGun; @@ -393,7 +393,7 @@ public virtual void TestDocsStuckInRAMForever() for (int i = 0; i < threads.Length; i++) { int threadID = i; - threads[i] = new ThreadAnonymousClassForDocsStuckInRAMForever(w, threadID, startingGun); + threads[i] = new DocsStuckInRAMForeverThreadAnonymousClass(w, threadID, startingGun); threads[i].Start(); } @@ -445,13 +445,13 @@ public virtual void TestDocsStuckInRAMForever() dir.Dispose(); } - private sealed class ThreadAnonymousClassForDocsStuckInRAMForever : ThreadJob + private sealed class DocsStuckInRAMForeverThreadAnonymousClass : ThreadJob { private readonly IndexWriter w; private readonly int threadID; private readonly CountdownLatch startingGun; - public ThreadAnonymousClassForDocsStuckInRAMForever(IndexWriter w, int threadID, CountdownLatch startingGun) + public DocsStuckInRAMForeverThreadAnonymousClass(IndexWriter w, int threadID, CountdownLatch startingGun) { this.w = w; this.threadID = threadID; From f72d23eee8a61ac1e6e9ebf543600194a8aafcb7 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Sun, 5 Jul 2026 14:59:13 -0600 Subject: [PATCH 17/20] Fix backport comment --- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 6e6f4acc3e..c570ea4908 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -3121,7 +3121,7 @@ public virtual void TestClosingNRTReaderDoesNotCorruptYourIndex() /// /// Make sure that close waits for any still-running commits. - // LUCENENET: backport LUCENE-4246 (Lucene 5.0) test + // LUCENENET: backport LUCENE-5871 (Lucene 4.10.0) test [Test] public virtual void TestCloseDuringCommit() { From b4036744ce8c998802d1dc7d4c7e0612123305a4 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Mon, 6 Jul 2026 09:04:50 -0600 Subject: [PATCH 18/20] Backport part of 6369012d from LUCENE-5438 --- .../Store/MockDirectoryWrapper.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs b/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs index 32bfbdd627..f7502e96d4 100644 --- a/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs +++ b/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs @@ -616,6 +616,15 @@ private void DeleteFile(string name, bool forced) } } m_input.DeleteFile(name); + // LUCENENET: backport from upstream commit 6369012d ("fix a few nocommits", + // Mike McCandless), which landed on the LUCENE-5438 (NRT replication) branch and + // first shipped in Lucene 6.0.0. Removing a deleted file's name from createdFiles + // lets the same segment name be re-created later without preventDoubleWrite falsely + // reporting "already written to". Without this, a benign upstream segment-name reuse + // on reopen after Dispose(false) (the committed counter can trail a name issued by a + // background merge that was then aborted and its files deleted) surfaced as a flaky + // TestNoWaitClose failure. See #1284. + createdFiles.Remove(name); } finally { From fb6fdb791ea87258614d8ce5e217e7531e7706d7 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Fri, 10 Jul 2026 15:15:00 -0600 Subject: [PATCH 19/20] Fix per-process randomized string hashing in test-framework codec/similarity selection MockRandomPostingsFormat, RandomCodec, and RandomSimilarityProvider chose a per-file/per-field int-stream factory, postings/docvalues format, and similarity by indexing into a collection with `string.GetHashCode()`. On .NET, string hash codes are randomized per process, so a choice made when writing an index (e.g. in the forked child process of TestIndexWriterOnJRECrash) differed from the choice made when reading it back in another process, producing spurious "codec header mismatch" / CheckIndex failures. Java is unaffected because String.hashCode() is deterministic across JVMs. Use J2N's CharSequenceComparer.Ordinal.GetHashCode(), which is deterministic and produces the same value as Java's String.hashCode(), keeping writer and reader in sync. This was a pre-existing test-framework bug (reproduces on master). Verified: TestIndexWriterOnJRECrash.TestNRTThreads_Mem (previously a deterministic failure under seed 0x03c0703dd1d74190:0xfc8ded704ed908f9) now passes on fixed and random seeds; RandomCodec- and similarity-heavy suites remain green. Confirmed against a native build of upstream Lucene 4.8.1, which passes this test. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Codecs/MockRandom/MockRandomPostingsFormat.cs | 11 +++++++++-- src/Lucene.Net.TestFramework/Index/RandomCodec.cs | 15 +++++++++++---- .../Search/RandomSimilarityProvider.cs | 8 +++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs b/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs index 92b0779e26..482f8bbc07 100644 --- a/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs +++ b/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs @@ -1,3 +1,4 @@ +using J2N.Text; using Lucene.Net.Codecs.BlockTerms; using Lucene.Net.Codecs.Lucene41; using Lucene.Net.Codecs.Memory; @@ -97,7 +98,12 @@ public override Int32IndexInput OpenInput(Directory dir, string fileName, IOCont { // Must only use extension, because IW.addIndexes can // rename segment! - Int32StreamFactory f = delegates[(Math.Abs(salt ^ GetExtension(fileName).GetHashCode())) % delegates.Count]; + // LUCENENET specific: use J2N's CharSequenceComparer.Ordinal.GetHashCode(), which produces + // the same value as Java's String.hashCode(). The .NET string.GetHashCode() is randomized + // per-process, so the factory chosen when writing (e.g. in a forked child process, as in + // TestIndexWriterOnJRECrash) would differ from the one chosen when reading in another process, + // causing "codec header mismatch" failures. A deterministic hash keeps writer and reader in sync. + Int32StreamFactory f = delegates[(Math.Abs(salt ^ CharSequenceComparer.Ordinal.GetHashCode(GetExtension(fileName)))) % delegates.Count]; if (LuceneTestCase.Verbose) { Console.WriteLine("MockRandomCodec: read using int factory " + f + " from fileName=" + fileName); @@ -107,7 +113,8 @@ public override Int32IndexInput OpenInput(Directory dir, string fileName, IOCont public override Int32IndexOutput CreateOutput(Directory dir, string fileName, IOContext context) { - Int32StreamFactory f = delegates[(Math.Abs(salt ^ GetExtension(fileName).GetHashCode())) % delegates.Count]; + // LUCENENET specific: deterministic (Java-parity) hash; see the note in OpenInput(). + Int32StreamFactory f = delegates[(Math.Abs(salt ^ CharSequenceComparer.Ordinal.GetHashCode(GetExtension(fileName)))) % delegates.Count]; if (LuceneTestCase.Verbose) { Console.WriteLine("MockRandomCodec: write using int factory " + f + " to fileName=" + fileName); diff --git a/src/Lucene.Net.TestFramework/Index/RandomCodec.cs b/src/Lucene.Net.TestFramework/Index/RandomCodec.cs index 757eb321c7..da4ed4e40d 100644 --- a/src/Lucene.Net.TestFramework/Index/RandomCodec.cs +++ b/src/Lucene.Net.TestFramework/Index/RandomCodec.cs @@ -1,3 +1,4 @@ +using J2N.Text; using Lucene.Net.Codecs; using Lucene.Net.Codecs.Asserting; using Lucene.Net.Codecs.Bloom; @@ -84,11 +85,16 @@ public override PostingsFormat GetPostingsFormatForField(string name) { if (!previousMappings.TryGetValue(name, out PostingsFormat codec) || codec is null) { - codec = formats[Math.Abs(perFieldSeed ^ name.GetHashCode()) % formats.Count]; + // LUCENENET specific: use J2N's CharSequenceComparer.Ordinal.GetHashCode(), which produces + // the same value as Java's String.hashCode(). The .NET string.GetHashCode() is randomized + // per-process, so a field's format chosen when writing (e.g. in a forked child process, as + // in TestIndexWriterOnJRECrash) would differ from the one chosen when reading in another + // process, causing "codec header mismatch" failures. A deterministic hash keeps them in sync. + codec = formats[Math.Abs(perFieldSeed ^ CharSequenceComparer.Ordinal.GetHashCode(name)) % formats.Count]; if (codec is SimpleTextPostingsFormat && perFieldSeed % 5 != 0) { // make simpletext rarer, choose again - codec = formats[Math.Abs(perFieldSeed ^ name.ToUpperInvariant().GetHashCode()) % formats.Count]; + codec = formats[Math.Abs(perFieldSeed ^ CharSequenceComparer.Ordinal.GetHashCode(name.ToUpperInvariant())) % formats.Count]; } previousMappings[name] = codec; // Safety: @@ -107,11 +113,12 @@ public override DocValuesFormat GetDocValuesFormatForField(string name) { if (!previousDVMappings.TryGetValue(name, out DocValuesFormat codec) || codec is null) { - codec = dvFormats[Math.Abs(perFieldSeed ^ name.GetHashCode()) % dvFormats.Count]; + // LUCENENET specific: deterministic (Java-parity) hash; see the note in GetPostingsFormatForField(). + codec = dvFormats[Math.Abs(perFieldSeed ^ CharSequenceComparer.Ordinal.GetHashCode(name)) % dvFormats.Count]; if (codec is SimpleTextDocValuesFormat && perFieldSeed % 5 != 0) { // make simpletext rarer, choose again - codec = dvFormats[Math.Abs(perFieldSeed ^ name.ToUpperInvariant().GetHashCode()) % dvFormats.Count]; + codec = dvFormats[Math.Abs(perFieldSeed ^ CharSequenceComparer.Ordinal.GetHashCode(name.ToUpperInvariant())) % dvFormats.Count]; } previousDVMappings[name] = codec; // Safety: diff --git a/src/Lucene.Net.TestFramework/Search/RandomSimilarityProvider.cs b/src/Lucene.Net.TestFramework/Search/RandomSimilarityProvider.cs index bdbbe82b3c..45ea5f4aac 100644 --- a/src/Lucene.Net.TestFramework/Search/RandomSimilarityProvider.cs +++ b/src/Lucene.Net.TestFramework/Search/RandomSimilarityProvider.cs @@ -1,4 +1,5 @@ using J2N.Collections.Generic.Extensions; +using J2N.Text; using Lucene.Net.Diagnostics; using Lucene.Net.Search.Similarities; using Lucene.Net.Support.Threading; @@ -78,7 +79,12 @@ public override Similarity Get(string field) if (Debugging.AssertsEnabled) Debugging.Assert(field != null); if (!previousMappings.TryGetValue(field, out Similarity sim) || sim is null) { - sim = knownSims[Math.Max(0, Math.Abs(perFieldSeed ^ field.GetHashCode())) % knownSims.Count]; + // LUCENENET specific: use J2N's CharSequenceComparer.Ordinal.GetHashCode(), which produces + // the same value as Java's String.hashCode(). The .NET string.GetHashCode() is randomized + // per-process, so the per-field similarity chosen in one process would differ from another, + // breaking reproducibility across processes (e.g. the forked TestIndexWriterOnJRECrash) and + // diverging from Java. A deterministic hash keeps the choice stable. + sim = knownSims[Math.Max(0, Math.Abs(perFieldSeed ^ CharSequenceComparer.Ordinal.GetHashCode(field))) % knownSims.Count]; previousMappings[field] = sim; } return sim; From fadeb23ec39b9ac099f4083ade4386c2a8c4b904 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Tue, 21 Jul 2026 09:02:38 -0600 Subject: [PATCH 20/20] Fix IndexWriter.Dispose breaking the standard Dispose contract, #1399 --- .../ByTask/Tasks/CloseIndexTask.cs | 2 +- .../ThreadedIndexingAndSearchingTestCase.cs | 2 +- src/Lucene.Net.Tests/Index/TestAddIndexes.cs | 2 +- .../Index/TestBackwardsCompatibility.cs | 6 +- .../Index/TestBackwardsCompatibility3x.cs | 6 +- .../Index/TestConcurrentMergeScheduler.cs | 6 +- src/Lucene.Net.Tests/Index/TestCrash.cs | 2 +- .../Index/TestFlushByRamOrCountsPolicy.cs | 4 +- .../Index/TestIndexWriterMerging.cs | 2 +- .../Index/TestIndexWriterOnDiskFull.cs | 4 +- .../Index/TestIndexWriterReader.cs | 2 +- .../Index/TestIndexWriterWithThreads.cs | 10 +- src/Lucene.Net/Index/IndexWriter.cs | 109 +++++------------- src/Lucene.Net/Index/MergePolicy.cs | 2 +- src/Lucene.Net/Index/MergeState.cs | 2 +- 15 files changed, 57 insertions(+), 104 deletions(-) diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs index a3677dab51..9bbb31d195 100644 --- a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs +++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs @@ -48,7 +48,7 @@ public override int DoLogic() infoStream.Dispose(); } #pragma warning disable 612, 618 - iw.Dispose(doWait); + iw.Close(doWait); #pragma warning restore 612, 618 RunData.IndexWriter = null; } diff --git a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs index cf99b3b78d..be87565652 100644 --- a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs @@ -773,7 +773,7 @@ public virtual void RunTest(string testName) DoClose(); #pragma warning disable 612, 618 - m_writer.Dispose(false); + m_writer.Close(false); #pragma warning restore 612, 618 // Cannot shutdown until after writer is closed because diff --git a/src/Lucene.Net.Tests/Index/TestAddIndexes.cs b/src/Lucene.Net.Tests/Index/TestAddIndexes.cs index d79d81bed7..0efdde9016 100644 --- a/src/Lucene.Net.Tests/Index/TestAddIndexes.cs +++ b/src/Lucene.Net.Tests/Index/TestAddIndexes.cs @@ -805,7 +805,7 @@ internal virtual void Close(bool doWait) { didClose = true; #pragma warning disable 612, 618 - writer2.Dispose(doWait); + writer2.Close(doWait); #pragma warning restore 612, 618 } diff --git a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs index 9ddee48986..58ff181f3a 100644 --- a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs +++ b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility.cs @@ -314,7 +314,7 @@ public virtual void TestUnsupportedOldIndexes() if (writer != null) { #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 } writer = null; @@ -1053,7 +1053,7 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() AddDoc(w, id++); } #pragma warning disable 612, 618 - w.Dispose(false); + w.Close(false); #pragma warning restore 612, 618 } @@ -1066,7 +1066,7 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() IndexWriter iw = new IndexWriter(dir, iwc_); iw.AddIndexes(ramDir); #pragma warning disable 612, 618 - iw.Dispose(false); + iw.Close(false); #pragma warning restore 612, 618 // determine count of segments in modified index diff --git a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs index 214c37e3d0..3fd0c9c08a 100644 --- a/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs +++ b/src/Lucene.Net.Tests/Index/TestBackwardsCompatibility3x.cs @@ -245,7 +245,7 @@ public virtual void TestUnsupportedOldIndexes() if (writer != null) { #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 } writer = null; @@ -958,7 +958,7 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() AddDoc(w, id++); } #pragma warning disable 612, 618 - w.Dispose(false); + w.Close(false); #pragma warning restore 612, 618 } @@ -970,7 +970,7 @@ public virtual void TestUpgradeOldSingleSegmentIndexWithAdditions() IndexWriter w_ = new IndexWriter(dir, iwc_); w_.AddIndexes(ramDir); #pragma warning disable 612, 618 - w_.Dispose(false); + w_.Close(false); #pragma warning restore 612, 618 // determine count of segments in modified index diff --git a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs index aaa566d8d0..4a7bb67fea 100644 --- a/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs +++ b/src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs @@ -68,7 +68,7 @@ public override void Eval(MockDirectoryWrapper dir) // LUCENENET specific: for these to work in release mode, we have added [MethodImpl(MethodImplOptions.NoInlining)] // to each possible target of the StackTraceHelper. If these change, so must the attribute on the target methods. bool isDoFlush = StackTraceHelper.DoesStackTraceContainMethod(nameof(DocumentsWriterPerThread.Flush)); - bool isClose = StackTraceHelper.DoesStackTraceContainMethod(nameof(IndexWriter.Close)) || // LUCENENET NOTE: Close is aggressively inlined, so likely won't hit this case, but would hit Dispose + bool isClose = StackTraceHelper.DoesStackTraceContainMethod(nameof(IndexWriter.Close)) || // LUCENENET NOTE: Close is marked NoInlining so it stays on the stack; Dispose is checked as well as a belt-and-suspenders StackTraceHelper.DoesStackTraceContainMethod(nameof(IndexWriter.Dispose)); if (isDoFlush && !isClose && Random.NextBoolean()) @@ -262,7 +262,7 @@ public virtual void TestNoWaitClose() writer.Commit(); #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 IndexReader reader = DirectoryReader.Open(directory); @@ -319,7 +319,7 @@ public virtual void TestMaxMergeCount() } } #pragma warning disable 612, 618 - w.Dispose(false); + w.Close(false); #pragma warning restore 612, 618 dir.Dispose(); } diff --git a/src/Lucene.Net.Tests/Index/TestCrash.cs b/src/Lucene.Net.Tests/Index/TestCrash.cs index 4efcd9e74a..d31683cccb 100644 --- a/src/Lucene.Net.Tests/Index/TestCrash.cs +++ b/src/Lucene.Net.Tests/Index/TestCrash.cs @@ -211,7 +211,7 @@ public virtual void TestCrashAfterCloseNoWait() MockDirectoryWrapper dir = (MockDirectoryWrapper)writer.Directory; #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 dir.Crash(); diff --git a/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs b/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs index a462f57ead..6f8182faef 100644 --- a/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs +++ b/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs @@ -314,9 +314,7 @@ public virtual void TestStallControl() Assert.IsTrue(docsWriter.flushControl.stallControl.WasStalled); } AssertActiveBytesAfter(flushControl); -#pragma warning disable 612, 618 - writer.Dispose(true); -#pragma warning restore 612, 618 + writer.Dispose(); dir.Dispose(); } } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs index 7f81d04358..1055de5d7e 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterMerging.cs @@ -434,7 +434,7 @@ public virtual void TestNoWaitClose() t1.Start(); #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 t1.Join(); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs index ba7e316352..0ee46a519a 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterOnDiskFull.cs @@ -665,7 +665,7 @@ public virtual void TestImmediateDiskFull() try { #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 Assert.Fail("did not hit disk full"); } @@ -677,7 +677,7 @@ public virtual void TestImmediateDiskFull() // cleanly close: dir.MaxSizeInBytes = 0; #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 dir.Dispose(); } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs index e5b689dd43..b61559a202 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs @@ -516,7 +516,7 @@ internal virtual void Close(bool doWait) mainWriter.WaitForMerges(); } #pragma warning disable 612, 618 - mainWriter.Dispose(doWait); + mainWriter.Close(doWait); #pragma warning restore 612, 618 } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs index a86e3f52d5..4824b89287 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs @@ -198,7 +198,7 @@ public virtual void TestImmediateDiskFullWithThreads() // cleanly close: dir.MaxSizeInBytes = 0; #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 dir.Dispose(); } @@ -270,7 +270,7 @@ public virtual void TestCloseWithThreads() Console.WriteLine("\nTEST: now close"); } #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 // Make sure threads that are adding docs are not hung: @@ -348,7 +348,7 @@ public virtual void TestMultipleThreadsFailure(Failure failure) try { #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 success = true; } @@ -356,7 +356,7 @@ public virtual void TestMultipleThreadsFailure(Failure failure) { failure.ClearDoFail(); #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 } if (Verbose) @@ -417,7 +417,7 @@ public virtual void TestSingleThreadFailure(Failure failure) failure.ClearDoFail(); writer.AddDocument(doc); #pragma warning disable 612, 618 - writer.Dispose(false); + writer.Close(false); #pragma warning restore 612, 618 dir.Dispose(); } diff --git a/src/Lucene.Net/Index/IndexWriter.cs b/src/Lucene.Net/Index/IndexWriter.cs index b6f9c3fd12..64dcbf5533 100644 --- a/src/Lucene.Net/Index/IndexWriter.cs +++ b/src/Lucene.Net/Index/IndexWriter.cs @@ -1117,7 +1117,11 @@ private void Shutdown(bool waitForMerges) [MethodImpl(MethodImplOptions.NoInlining)] // Stack trace needed intact in TestConcurrentMergeScheduler and TestIndexWriterWithThreads public void Dispose() { - Dispose(disposing: true, waitForMerges: true); + // LUCENENET: waitForMerges is threaded into the shutdown path directly here (via + // the private Shutdown(bool)) rather than through the Dispose(bool) hook, so subclasses + // see the conventional Dispose(bool disposing) signature. See #1399. + Shutdown(waitForMerges: true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -1129,100 +1133,51 @@ public void Dispose() /// when exceptions are thrown. /// /// NOTE: It is dangerous to always call - /// Dispose(false), especially when is not open + /// Close(false), especially when is not open /// for very long, because this can result in "merge /// starvation" whereby long merges will never have a /// chance to finish. This will cause too many segments in /// your index over time, which leads to all sorts of /// problems like slow searches, too much RAM and too /// many file descriptors used by readers, etc. - /// - /// NOTE: This overload should not be called when implementing a finalizer. - /// Instead, call with disposing set to - /// false and waitForMerges set to true. /// /// If true, this call will block /// until all merges complete; else, it will ask all /// running merges to abort, wait until those merges have /// finished (which should be at most a few seconds), and /// then return. - // LUCENENET: doc-comment updated and [Obsolete] applied to match upstream LUCENE-5871 - // (commit 2cfcdcc, first released in 4.10.0). Upstream marked this overload @Deprecated - // because the new Shutdown contract makes "close without waiting for merges" easy to - // misuse; prefer Commit() followed by Rollback() to abort merges and then close. - // - // The deprecation generates CS0618 warnings on existing in-tree callers. This mirrors - // upstream's posture exactly: branch_4x kept javac [deprecation] warnings at the - // method's call sites and did not migrate them when 2cfcdcc landed. The intent was - // always to remove the overload entirely on the next major release: trunk did exactly - // that in LUCENE-4246 (commit 8559eaf, Lucene 5.0), deleting close(boolean) and - // migrating all the trunk call sites in the same commit. - [Obsolete("To abort merges and then close, call Commit() and then Rollback() instead.")] + // LUCENENET: this maps to upstream close(boolean). Upstream (branch_4x) exposed it as a + // public method; LUCENE-5871 (commit 2cfcdcc, first released in 4.10.0) marked it + // @Deprecated because the shutdown contract makes "close without waiting for merges" easy + // to misuse, and trunk LUCENE-4246 (commit 8559eaf, Lucene 5.0) deleted it entirely. We + // keep it public but [Obsolete] to mirror the 4.10 state and preserve the 4.8.1 capability. + // It also serves the test/benchmark call sites that need the "abort merges on close" path. + [Obsolete("To abort merges and then close, call Rollback() instead, or Dispose() to close waiting for merges.")] [MethodImpl(MethodImplOptions.NoInlining)] // Stack trace needed intact in TestConcurrentMergeScheduler and TestIndexWriterWithThreads - [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] - [SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "This is Lucene's alternate path to Dispose() and we must suppress the finalizer here.")] - [SuppressMessage("Usage", "S2953:Methods named \"Dispose\" should implement \"IDisposable.Dispose\"", Justification = "This is Lucene's alternate path to Dispose() and we must suppress the finalizer here.")] - [SuppressMessage("Usage", "S3971:\"GC.SuppressFinalize\" should not be called", Justification = "This is Lucene's alternate path to Dispose() and we must suppress the finalizer here.")] - public void Dispose(bool waitForMerges) + public void Close(bool waitForMerges) { - Dispose(disposing: true, waitForMerges); - GC.SuppressFinalize(this); + Shutdown(waitForMerges); } /// - /// Disposes the index with or without waiting for currently - /// running merges to finish. This is only meaningful when - /// using a that runs merges in background - /// threads. - /// - /// This call will block - /// until all merges complete; else, it will ask all - /// running merges to abort, wait until those merges have - /// finished (which should be at most a few seconds), and - /// then return. - /// - /// - /// NOTE: Always be sure to call base.Dispose(disposing, waitForMerges) - /// when overriding this method. - /// - /// NOTE: When implementing a finalizer in a subclass, this overload should be called - /// with set to false and - /// set to true. - /// - /// NOTE: If this method hits an - /// you should immediately dispose the writer, again. See - /// for details. - /// - /// NOTE: It is dangerous to always call - /// with set to false, - /// especially when is not open - /// for very long, because this can result in "merge - /// starvation" whereby long merges will never have a - /// chance to finish. This will cause too many segments in - /// your index over time. + /// Releases resources used by the and optionally + /// releases the managed resources. + /// + /// The disposing of the index itself (committing, waiting for merges, and releasing + /// the write lock) is performed by the public overload before + /// this method is invoked, so that the waitForMerges flag does not need to appear + /// on this inheritance hook. + /// Subclasses that override this method to release their own resources should call + /// base.Dispose(disposing). /// - /// If true, this call will block - /// until all merges complete; else, it will ask all - /// running merges to abort, wait until those merges have - /// finished (which should be at most a few seconds), and - /// then return. /// true to release both managed and unmanaged resources; /// false to release only unmanaged resources. - // LUCENENET specific - Added this overload to allow subclasses to dispose resources - // in one place without also having to override Dispose(bool). - [MethodImpl(MethodImplOptions.NoInlining)] // Stack trace needed intact in TestConcurrentMergeScheduler and TestIndexWriterWithThreads - protected virtual void Dispose(bool disposing, bool waitForMerges) - { - if (disposing) - { - Close(waitForMerges); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] // LUCENENET NOTE: this will interfere with stack trace inspection in tests, but that case should be covered by Dispose above which is NoInlining - internal void Close(bool waitForMerges) // LUCENENET: made internal for test purposes + // LUCENENET specific - restored the conventional Dispose(bool disposing) signature. See #1399. + // The index shutdown (which needs the waitForMerges flag) is driven directly from the public + // Dispose() entry point via Shutdown(bool), so the waitForMerges state stays off the protected + // inheritance surface and this matches the standard .NET dispose pattern. + protected virtual void Dispose(bool disposing) { - Shutdown(waitForMerges); } // LUCENENET: upstream 2cfcdcc had private assertEventQueueAfterClose() between @@ -2094,7 +2049,7 @@ internal string NewSegmentName() /// you should immediately dispose the writer. See /// for details. /// - /// NOTE: if you call + /// NOTE: if you call /// with false, which aborts all running merges, /// then any thread still running this method might hit a /// . @@ -2276,7 +2231,7 @@ private bool MaxNumSegmentsMergesPending() /// you should immediately dispose the writer. See /// for details. /// - /// NOTE: if you call + /// NOTE: if you call /// with false, which aborts all running merges, /// then any thread still running this method might hit a /// . @@ -3335,7 +3290,7 @@ public virtual void AddIndexes(params Directory[] dirs) /// . /// /// - /// NOTE: if you call with false, which + /// NOTE: if you call with false, which /// aborts all running merges, then any thread still running this method might /// hit a . /// diff --git a/src/Lucene.Net/Index/MergePolicy.cs b/src/Lucene.Net/Index/MergePolicy.cs index a552f647ae..86c75cee1e 100644 --- a/src/Lucene.Net/Index/MergePolicy.cs +++ b/src/Lucene.Net/Index/MergePolicy.cs @@ -546,7 +546,7 @@ protected MergeException(SerializationInfo info, StreamingContext context) /// /// Thrown when a merge was explicitly aborted because - /// was called with + /// was called with /// false. Normally this exception is /// privately caught and suppressed by . /// diff --git a/src/Lucene.Net/Index/MergeState.cs b/src/Lucene.Net/Index/MergeState.cs index 0d738d081b..14da40ea33 100644 --- a/src/Lucene.Net/Index/MergeState.cs +++ b/src/Lucene.Net/Index/MergeState.cs @@ -245,7 +245,7 @@ public virtual void Work(double units) } /// - /// If you use this: IW.Dispose(false) cannot abort your merge! + /// If you use this: IW.Close(false) cannot abort your merge! /// /// @lucene.internal ///