This is an automated email from the ASF dual-hosted git repository. nightowl888 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/lucenenet.git
commit b1e0eec678e3a345c807fa95999413065a93d07d Author: Shad Storhaug <[email protected]> AuthorDate: Tue Mar 23 03:12:11 2021 +0700 SWEEP: Added Lucene.Net.Util.ThreadInterruptedException and re-throw it in all of the places that Lucene does. --- .../ByTask/Feeds/EnwikiContentSource.cs | 20 +++++-- src/Lucene.Net.Replicator/ReplicationClient.cs | 13 ++++- .../Store/SlowClosingMockIndexInputWrapper.cs | 8 ++- .../Store/SlowOpeningMockIndexInputWrapper.cs | 2 +- .../Util/ThrottledIndexOutput.cs | 13 ++++- src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs | 10 +++- .../IndexAndTaxonomyReplicationClientTest.cs | 10 +++- .../IndexReplicationClientTest.cs | 10 +++- .../Index/TestDocumentsWriterDeleteQueue.cs | 13 +++-- .../Index/TestDocumentsWriterStallControl.cs | 4 +- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 34 ++++++++---- .../Index/TestIndexWriterReader.cs | 14 ++++- .../Index/TestIndexWriterWithThreads.cs | 10 +++- .../Index/TestSnapshotDeletionPolicy.cs | 11 +++- .../Search/TestControlledRealTimeReopenThread.cs | 14 +++-- .../Search/TestTimeLimitingCollector.cs | 12 +++- .../ExceptionHandling/TestExceptionExtensions.cs | 2 +- src/Lucene.Net/Index/ConcurrentMergeScheduler.cs | 32 +++++++---- .../Index/DocumentsWriterFlushControl.cs | 12 +++- .../Index/DocumentsWriterPerThreadPool.cs | 10 +++- .../Index/DocumentsWriterStallControl.cs | 24 +++++--- src/Lucene.Net/Index/IndexWriter.cs | 19 ++++--- .../Search/ControlledRealTimeReopenThread.cs | 20 +++++-- src/Lucene.Net/Search/IndexSearcher.cs | 3 +- src/Lucene.Net/Search/TimeLimitingCollector.cs | 10 +++- src/Lucene.Net/Store/Lock.cs | 10 +++- src/Lucene.Net/Store/RateLimiter.cs | 10 +++- .../ExceptionHandling/ExceptionExtensions.cs | 10 +++- src/Lucene.Net/Util/ThreadInterruptedException.cs | 64 ++++++++++++++++++++++ 29 files changed, 326 insertions(+), 98 deletions(-) diff --git a/src/Lucene.Net.Benchmark/ByTask/Feeds/EnwikiContentSource.cs b/src/Lucene.Net.Benchmark/ByTask/Feeds/EnwikiContentSource.cs index ce71b77..1e06701 100644 --- a/src/Lucene.Net.Benchmark/ByTask/Feeds/EnwikiContentSource.cs +++ b/src/Lucene.Net.Benchmark/ByTask/Feeds/EnwikiContentSource.cs @@ -77,8 +77,14 @@ namespace Lucene.Net.Benchmarks.ByTask.Feeds { while (tuple == null && nmde == null && !threadDone && !stopped) { - Monitor.Wait(this); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException. Note that it could be thrown above on lock (this). + try + { + Monitor.Wait(this); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } if (tuple != null) { @@ -142,8 +148,14 @@ namespace Lucene.Net.Benchmarks.ByTask.Feeds { while (tuple != null && !stopped) { - Monitor.Wait(this); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException. Note that it could be thrown above on lock (this). + try + { + Monitor.Wait(this); //wait(); + } + catch (System.Threading.ThreadInterruptedException ie) + { + throw new Util.ThreadInterruptedException(ie); + } } tuple = tmpTuple; Monitor.Pulse(this); //notify(); diff --git a/src/Lucene.Net.Replicator/ReplicationClient.cs b/src/Lucene.Net.Replicator/ReplicationClient.cs index 51c07d5..e0c0164 100644 --- a/src/Lucene.Net.Replicator/ReplicationClient.cs +++ b/src/Lucene.Net.Replicator/ReplicationClient.cs @@ -44,6 +44,8 @@ namespace Lucene.Net.Replicator /// </remarks> public class ReplicationClient : IDisposable { + // LUCENENET TODO: Check to ensure ThreadInterruptException is being passed from the background worker to the current thread, as it is required by IndexWriter + //Note: LUCENENET specific, .NET does not work with Threads in the same way as Java does, so we mimic the same behavior using the ThreadPool instead. private class ReplicationThread { @@ -430,7 +432,16 @@ namespace Lucene.Net.Replicator // otherwise, if it's in the middle of replication, we wait for it to // stop. if (updateThread != null) - updateThread.Stop(); + { + try + { + updateThread.Stop(); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } + } updateThread = null; } diff --git a/src/Lucene.Net.TestFramework/Store/SlowClosingMockIndexInputWrapper.cs b/src/Lucene.Net.TestFramework/Store/SlowClosingMockIndexInputWrapper.cs index 5e047cb..c65685d 100644 --- a/src/Lucene.Net.TestFramework/Store/SlowClosingMockIndexInputWrapper.cs +++ b/src/Lucene.Net.TestFramework/Store/SlowClosingMockIndexInputWrapper.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; namespace Lucene.Net.Store { @@ -40,7 +41,10 @@ namespace Lucene.Net.Store { Thread.Sleep(50); } - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } finally { base.Dispose(disposing); diff --git a/src/Lucene.Net.TestFramework/Store/SlowOpeningMockIndexInputWrapper.cs b/src/Lucene.Net.TestFramework/Store/SlowOpeningMockIndexInputWrapper.cs index d6f7ef5..684e118 100644 --- a/src/Lucene.Net.TestFramework/Store/SlowOpeningMockIndexInputWrapper.cs +++ b/src/Lucene.Net.TestFramework/Store/SlowOpeningMockIndexInputWrapper.cs @@ -42,7 +42,7 @@ namespace Lucene.Net.Store catch (Exception ignore) when (ignore.IsThrowable()) { } - throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) + throw new Util.ThreadInterruptedException(ie); } } } diff --git a/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs b/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs index 37590e2..4892032 100644 --- a/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs +++ b/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs @@ -142,9 +142,16 @@ namespace Lucene.Net.Util return; } - Thread.Sleep(TimeSpan.FromMilliseconds(ms)); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException - + try + { + Thread.Sleep(TimeSpan.FromMilliseconds(ms)); + } + catch (Exception e) when (e.IsInterruptedException()) + { +#pragma warning disable IDE0001 // Simplify name + throw new Util.ThreadInterruptedException(e); +#pragma warning restore IDE0001 // Simplify name + } } public override long Length diff --git a/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs b/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs index b293c99..40e98a6 100644 --- a/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs +++ b/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs @@ -77,8 +77,14 @@ namespace Lucene.Net.Facet sTime = random.Next(sTime); } - Thread.Sleep(sTime); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Thread.Sleep(sTime); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } /// <summary> diff --git a/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs b/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs index 6a43d60..d19a252 100644 --- a/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs +++ b/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs @@ -138,8 +138,14 @@ namespace Lucene.Net.Replicator // introducing timeouts is not good, can easily lead to false positives. while (client.IsUpdateThreadAlive) { - Thread.Sleep(100); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException. + try + { + Thread.Sleep(100); + } + catch (Exception e) when (e.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(e); + } try { diff --git a/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs b/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs index 28450cf..de3040f 100644 --- a/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs +++ b/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs @@ -100,8 +100,14 @@ namespace Lucene.Net.Replicator while (client.IsUpdateThreadAlive) { // give client a chance to update - Thread.Sleep(100); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException. + try + { + Thread.Sleep(100); + } + catch (Exception e) when (e.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(e); + } try { diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs index 5daaa7d..593a618 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterDeleteQueue.cs @@ -1,4 +1,4 @@ -using J2N.Threading; +using J2N.Threading; using J2N.Threading.Atomic; using Lucene.Net.Search; using Lucene.Net.Support.Threading; @@ -318,9 +318,14 @@ namespace Lucene.Net.Index public override void Run() { - - latch.Wait(); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + latch.Wait(); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } int i = 0; while ((i = index.GetAndIncrement()) < ids.Length) diff --git a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs index 4f5dd18..3af2be4 100644 --- a/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs +++ b/src/Lucene.Net.Tests/Index/TestDocumentsWriterStallControl.cs @@ -275,7 +275,7 @@ namespace Lucene.Net.Index catch (Exception e) when (e.IsInterruptedException()) { Console.WriteLine("[Waiter] got interrupted - wait count: " + sync.waiter.CurrentCount); - throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) + throw new Util.ThreadInterruptedException(e); } } } @@ -330,7 +330,7 @@ namespace Lucene.Net.Index catch (Exception e) when (e.IsInterruptedException()) { Console.WriteLine("[Updater] got interrupted - wait count: " + sync.waiter.CurrentCount); - throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) + 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 // there may be some value to debugging this way. diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 05f1b09..ce37594 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -1316,7 +1316,7 @@ namespace Lucene.Net.Index w.Dispose(); w = null; //DirectoryReader.Open(dir).Dispose(); - using (var reader = DirectoryReader.Open(dir)) { } + using var reader = DirectoryReader.Open(dir); // Strangely, if we interrupt a thread before // all classes are loaded, the class loader @@ -1330,7 +1330,7 @@ namespace Lucene.Net.Index allowInterrupt = true; } } - catch (ThreadInterruptedException re) // LUCENENET: This was a custom wrapper type named ThreadInterruptedException in Lucene, so leaving the catch block as is + catch (Util.ThreadInterruptedException re) { // NOTE: important to leave this verbosity/noise // on!! this test doesn't repro easily so when @@ -1339,17 +1339,28 @@ namespace Lucene.Net.Index Console.WriteLine("TEST: got interrupt"); Console.WriteLine(re.ToString()); - // LUCENENET NOTE: Since our original exception is ThreadInterruptException instead of InterruptException - // in .NET, our expectation is typically that the InnerException is null (but it doesn't have to be). - // So, this assertion is not needed in .NET. And if we get to this catch block, we already know we have - // the right exception type, so there is nothing to test here. - //Exception e = re.InnerException; - //Assert.IsTrue(e is ThreadInterruptedException); + Exception e = re.InnerException; + Assert.IsTrue(e is System.Threading.ThreadInterruptedException); if (finish) { break; } } + //// LUCENENET specific: + //catch (System.Threading.ThreadInterruptedException re) + //{ + // // NOTE: important to leave this verbosity/noise + // // on!! this test doesn't repro easily so when + // // Jenkins hits a fail we need to study where the + // // interrupts struck! + // Console.WriteLine("TEST: got .NET interrupt"); + // Console.WriteLine(re.ToString()); + + // if (finish) + // { + // break; + // } + //} catch (Exception t) when (t.IsThrowable()) { Console.WriteLine("FAILED; unexpected exception"); @@ -1378,7 +1389,7 @@ namespace Lucene.Net.Index { Thread.Sleep(0); } - catch (ThreadInterruptedException) + catch (Exception ie) when (ie.IsInterruptedException()) { // ignore } @@ -1449,7 +1460,7 @@ namespace Lucene.Net.Index // up front... else we can see a false failure if 2nd // interrupt arrives while class loader is trying to // init this class (in servicing a first interrupt): - //Assert.IsTrue((new ThreadInterruptedException(new Exception("Thread interrupted"))).InnerException is ThreadInterruptedException); + Assert.IsTrue(new Util.ThreadInterruptedException(new System.Threading.ThreadInterruptedException()).InnerException is System.Threading.ThreadInterruptedException); // issue 300 interrupts to child thread int numInterrupts = AtLeast(300); @@ -1494,8 +1505,7 @@ namespace Lucene.Net.Index // up front... else we can see a false failure if 2nd // interrupt arrives while class loader is trying to // init this class (in servicing a first interrupt): - // C# does not have the late load problem. - //Assert.IsTrue((new ThreadInterruptedException(new Exception("Thread interrupted"))).InnerException is ThreadInterruptedException); + Assert.IsTrue((new Util.ThreadInterruptedException(new System.Threading.ThreadInterruptedException())).InnerException is System.Threading.ThreadInterruptedException); // issue 300 interrupts to child thread int numInterrupts = AtLeast(300); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs index b000cf5..9bd112c 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs @@ -508,12 +508,20 @@ namespace Lucene.Net.Index { for (int i = 0; i < outerInstance.numThreads; i++) { - - threads[i].Join(); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + threads[i].Join(); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { +#pragma warning disable IDE0001 // Simplify name + throw new Util.ThreadInterruptedException(ie); +#pragma warning restore IDE0001 // Simplify name + } } } + internal virtual void Close(bool doWait) { didClose = true; diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs index f9b0a71..65b6416 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterWithThreads.cs @@ -119,8 +119,14 @@ namespace Lucene.Net.Index { diskFull = true; - Thread.Sleep(1); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Thread.Sleep(1); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } if (fullCount++ >= 5) { diff --git a/src/Lucene.Net.Tests/Index/TestSnapshotDeletionPolicy.cs b/src/Lucene.Net.Tests/Index/TestSnapshotDeletionPolicy.cs index d330a09..ae923a1 100644 --- a/src/Lucene.Net.Tests/Index/TestSnapshotDeletionPolicy.cs +++ b/src/Lucene.Net.Tests/Index/TestSnapshotDeletionPolicy.cs @@ -226,9 +226,14 @@ namespace Lucene.Net.Index } } - Thread.Sleep(1); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException - + try + { + Thread.Sleep(1); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } while (J2N.Time.NanoTime() / J2N.Time.MillisecondsPerNanosecond < stopTime); // LUCENENET: Use NanoTime() rather than CurrentTimeMilliseconds() for more accurate/reliable results } } diff --git a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs index 302377d..441605a 100644 --- a/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs +++ b/src/Lucene.Net.Tests/Search/TestControlledRealTimeReopenThread.cs @@ -521,12 +521,18 @@ namespace Lucene.Net.Search public override void UpdateDocument(Term term, IEnumerable<IIndexableField> doc, Analyzer analyzer) { base.UpdateDocument(term, doc, analyzer); - if (waitAfterUpdate) + try + { + if (waitAfterUpdate) + { + signal.Reset(signal.CurrentCount == 0 ? 0 : signal.CurrentCount - 1); + latch.Wait(); + } + } + catch (Exception ie) when (ie.IsInterruptedException()) { - signal.Reset(signal.CurrentCount == 0 ? 0 : signal.CurrentCount - 1); - latch.Wait(); + throw new Util.ThreadInterruptedException(ie); } - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException } } diff --git a/src/Lucene.Net.Tests/Search/TestTimeLimitingCollector.cs b/src/Lucene.Net.Tests/Search/TestTimeLimitingCollector.cs index 428855b..a41d341 100644 --- a/src/Lucene.Net.Tests/Search/TestTimeLimitingCollector.cs +++ b/src/Lucene.Net.Tests/Search/TestTimeLimitingCollector.cs @@ -401,8 +401,16 @@ namespace Lucene.Net.Search int docId = doc + docBase; if (slowdown > 0) { - ThreadJob.Sleep(slowdown); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + ThreadJob.Sleep(slowdown); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { +#pragma warning disable IDE0001 // Simplify name + throw new Util.ThreadInterruptedException(ie); +#pragma warning restore IDE0001 // Simplify name + } } if (Debugging.AssertsEnabled) Debugging.Assert(docId >= 0," base={0} doc={1}", docBase, doc); diff --git a/src/Lucene.Net.Tests/Support/ExceptionHandling/TestExceptionExtensions.cs b/src/Lucene.Net.Tests/Support/ExceptionHandling/TestExceptionExtensions.cs index 628b09e..2a9257f 100644 --- a/src/Lucene.Net.Tests/Support/ExceptionHandling/TestExceptionExtensions.cs +++ b/src/Lucene.Net.Tests/Support/ExceptionHandling/TestExceptionExtensions.cs @@ -318,7 +318,7 @@ namespace Lucene typeof(Lucene.UnsupportedOperationException), // Corresponds to Lucene's ThreadInterruptedException - typeof(ThreadInterruptedException), + typeof(Lucene.Net.Util.ThreadInterruptedException), // Corresponds to SecurityException typeof(SecurityException), diff --git a/src/Lucene.Net/Index/ConcurrentMergeScheduler.cs b/src/Lucene.Net/Index/ConcurrentMergeScheduler.cs index e873011..16c3b00 100644 --- a/src/Lucene.Net/Index/ConcurrentMergeScheduler.cs +++ b/src/Lucene.Net/Index/ConcurrentMergeScheduler.cs @@ -434,8 +434,14 @@ namespace Lucene.Net.Index { Message(" too many merges; stalling..."); } - Monitor.Wait(this); - // LUCENENET: Just let ThreadInterruptedException propagate. Note that it could be thrown above on lock (this). + try + { + Monitor.Wait(this); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } if (IsVerbose) @@ -690,15 +696,21 @@ namespace Lucene.Net.Index /// </summary> protected virtual void HandleMergeException(Exception exc) { - // When an exception is hit during merge, IndexWriter - // removes any partial files and then allows another - // merge to run. If whatever caused the error is not - // transient then the exception will keep happening, - // so, we sleep here to avoid saturating CPU in such - // cases: - Thread.Sleep(250); + try + { + // When an exception is hit during merge, IndexWriter + // removes any partial files and then allows another + // merge to run. If whatever caused the error is not + // transient then the exception will keep happening, + // so, we sleep here to avoid saturating CPU in such + // cases: + Thread.Sleep(250); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } throw new MergePolicy.MergeException(exc, m_dir); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException } private bool suppressExceptions; diff --git a/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs b/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs index 8a23eac..124f8b7 100644 --- a/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs +++ b/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs @@ -309,12 +309,18 @@ namespace Lucene.Net.Index public void WaitForFlush() { - lock (this) + lock (this) { while (flushingWriters.Count != 0) { - Monitor.Wait(this); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Monitor.Wait(this); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } } } diff --git a/src/Lucene.Net/Index/DocumentsWriterPerThreadPool.cs b/src/Lucene.Net/Index/DocumentsWriterPerThreadPool.cs index 10240f8..349af3c 100644 --- a/src/Lucene.Net/Index/DocumentsWriterPerThreadPool.cs +++ b/src/Lucene.Net/Index/DocumentsWriterPerThreadPool.cs @@ -350,8 +350,14 @@ namespace Lucene.Net.Index else { // Wait until a thread state frees up: - Monitor.Wait(this); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Monitor.Wait(this); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } } } diff --git a/src/Lucene.Net/Index/DocumentsWriterStallControl.cs b/src/Lucene.Net/Index/DocumentsWriterStallControl.cs index 77303bc..fad1ab4 100644 --- a/src/Lucene.Net/Index/DocumentsWriterStallControl.cs +++ b/src/Lucene.Net/Index/DocumentsWriterStallControl.cs @@ -1,6 +1,7 @@ using J2N.Runtime.CompilerServices; using J2N.Threading; using Lucene.Net.Diagnostics; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -81,15 +82,20 @@ namespace Lucene.Net.Index if (stalled) // react on the first wakeup call! { // don't loop here, higher level logic will re-stall! - - // LUCENENET: make sure not to run IncWaiters / DecrWaiters in Debugging.Assert as that gets - // disabled in production - var result = IncWaiters(); - if (Debugging.AssertsEnabled) Debugging.Assert(result); - Monitor.Wait(this); - result = DecrWaiters(); - if (Debugging.AssertsEnabled) Debugging.Assert(result); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + // LUCENENET: make sure not to run IncWaiters / DecrWaiters in Debugging.Assert as that gets + // disabled in production + var result = IncWaiters(); + if (Debugging.AssertsEnabled) Debugging.Assert(result); + Monitor.Wait(this); + result = DecrWaiters(); + if (Debugging.AssertsEnabled) Debugging.Assert(result); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } } } diff --git a/src/Lucene.Net/Index/IndexWriter.cs b/src/Lucene.Net/Index/IndexWriter.cs index 5daf4ba..adda7ea 100644 --- a/src/Lucene.Net/Index/IndexWriter.cs +++ b/src/Lucene.Net/Index/IndexWriter.cs @@ -157,8 +157,8 @@ namespace Lucene.Net.Index /// <para><b>NOTE</b>: If you call /// <see cref="Thread.Interrupt()"/> on a thread that's within /// <see cref="IndexWriter"/>, <see cref="IndexWriter"/> will try to catch this (eg, if - /// it's in a Wait() or <see cref="Thread.Sleep(int)"/>), and will then throw - /// the unchecked exception <see cref="ThreadInterruptedException"/> + /// it's in a <see cref="Monitor.Wait(object)"/> or <see cref="Thread.Sleep(int)"/>), and will then throw + /// the unchecked exception <see cref="Util.ThreadInterruptedException"/> /// and <b>clear</b> the interrupt status on the thread.</para> /// </remarks> @@ -1178,7 +1178,7 @@ namespace Lucene.Net.Index // any pending merges are waiting: mergeScheduler.Merge(this, MergeTrigger.CLOSING, false); } - catch (ThreadInterruptedException) // LUCENENET: In Lucene, they caught their custom ThreadInterruptedException here, so we are leaving this catch block as is + catch (Util.ThreadInterruptedException) { // ignore any interruption, does not matter interrupted = true; @@ -1198,7 +1198,7 @@ namespace Lucene.Net.Index FinishMerges(waitForMerges && !interrupted); break; } - catch (ThreadInterruptedException) // LUCENENET: In Lucene, they caught their custom ThreadInterruptedException here, so we are leaving this catch block as is + catch (Util.ThreadInterruptedException) { // by setting the interrupted status, the // next call to finishMerges will pass false, @@ -5329,9 +5329,14 @@ namespace Lucene.Net.Index // fails to be called, we wait for at most 1 second // and then return so caller can check if wait // conditions are satisfied: - - Monitor.Wait(this, TimeSpan.FromMilliseconds(1000)); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Monitor.Wait(this, TimeSpan.FromMilliseconds(1000)); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } } diff --git a/src/Lucene.Net/Search/ControlledRealTimeReopenThread.cs b/src/Lucene.Net/Search/ControlledRealTimeReopenThread.cs index 961ea84..954c018 100644 --- a/src/Lucene.Net/Search/ControlledRealTimeReopenThread.cs +++ b/src/Lucene.Net/Search/ControlledRealTimeReopenThread.cs @@ -135,13 +135,21 @@ namespace Lucene.Net.Search { finish = true; reopenCond.Set(); - - Join(); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException - // LUCENENET specific: dispose reset event - reopenCond.Dispose(); - available.Dispose(); + try + { + Join(); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } + finally + { + // LUCENENET specific: dispose reset event + reopenCond.Dispose(); + available.Dispose(); + } } } diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 50e9213..f76d991 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -880,8 +880,7 @@ namespace Lucene.Net.Search } catch (Exception e) when (e.IsInterruptedException()) { - // LUCENENET: Throwing as same type, no need to wrap here - throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) + throw new Util.ThreadInterruptedException(e); } catch (Exception e) { diff --git a/src/Lucene.Net/Search/TimeLimitingCollector.cs b/src/Lucene.Net/Search/TimeLimitingCollector.cs index 651d983..4549503 100644 --- a/src/Lucene.Net/Search/TimeLimitingCollector.cs +++ b/src/Lucene.Net/Search/TimeLimitingCollector.cs @@ -304,8 +304,14 @@ namespace Lucene.Net.Search // TODO: Use System.nanoTime() when Lucene moves to Java SE 5. counter.AddAndGet(resolution); - Thread.Sleep(TimeSpan.FromMilliseconds(Interlocked.Read(ref resolution))); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Thread.Sleep(TimeSpan.FromMilliseconds(Interlocked.Read(ref resolution))); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } } } diff --git a/src/Lucene.Net/Store/Lock.cs b/src/Lucene.Net/Store/Lock.cs index 4ee94d0..0f9d167 100644 --- a/src/Lucene.Net/Store/Lock.cs +++ b/src/Lucene.Net/Store/Lock.cs @@ -136,8 +136,14 @@ namespace Lucene.Net.Store throw e; } - Thread.Sleep(TimeSpan.FromMilliseconds(LOCK_POLL_INTERVAL)); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Thread.Sleep(TimeSpan.FromMilliseconds(LOCK_POLL_INTERVAL)); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } locked = Obtain(); } diff --git a/src/Lucene.Net/Store/RateLimiter.cs b/src/Lucene.Net/Store/RateLimiter.cs index df7ccd0..61e2339 100644 --- a/src/Lucene.Net/Store/RateLimiter.cs +++ b/src/Lucene.Net/Store/RateLimiter.cs @@ -120,8 +120,14 @@ namespace Lucene.Net.Store var pauseNS = targetNS - curNS; if (pauseNS > 0) { - Thread.Sleep(TimeSpan.FromMilliseconds(pauseNS / 1000000)); - // LUCENENET NOTE: No need to catch and rethrow same excepton type ThreadInterruptedException + try + { + Thread.Sleep(TimeSpan.FromMilliseconds(pauseNS / 1000000)); + } + catch (Exception ie) when (ie.IsInterruptedException()) + { + throw new Util.ThreadInterruptedException(ie); + } curNS = Time.NanoTime(); continue; diff --git a/src/Lucene.Net/Support/ExceptionHandling/ExceptionExtensions.cs b/src/Lucene.Net/Support/ExceptionHandling/ExceptionExtensions.cs index 3795b7a..5a25bf6 100644 --- a/src/Lucene.Net/Support/ExceptionHandling/ExceptionExtensions.cs +++ b/src/Lucene.Net/Support/ExceptionHandling/ExceptionExtensions.cs @@ -155,6 +155,10 @@ namespace Lucene // .NET made IOException a SystemExcpetion, but those should not be included here e.IsIOException() || + // .NET made System.Threading.ThreadInterruptedException a SystemException, but we need to ignore it + // because InterruptedException in Java subclasses Exception, not RuntimeException + e is System.Threading.ThreadInterruptedException || + // ObjectDisposedException is a special case because in Lucene the AlreadyClosedException derived // from IOException and was therefore a checked excpetion type. e is ObjectDisposedException || @@ -448,10 +452,10 @@ namespace Lucene [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInterruptedException(this Exception e) { - // LUCENENET: Special case - we only catch under certain scenarios and do not rethrow explicitly. // This exception is the shutdown signal for a thread and it is used in Lucene for control flow. - // However, in .NET it is thrown in more cases than in Java. So, rather than wrapping it in a new - // exception type, we don't catch it unless there is a specific reason to do something other than re-throw. + // Lucene uses a custom Lucene.Net.Util.ThreadInterruptedException exception to handle the signal. + // It is only thrown from certain blocks, and we use UninterruptableMonitor.Enter() to avoid getting + // the System.Threading.ThreadInterruptedException when obtaining locks. return e is ThreadInterruptedException; } diff --git a/src/Lucene.Net/Util/ThreadInterruptedException.cs b/src/Lucene.Net/Util/ThreadInterruptedException.cs new file mode 100644 index 0000000..85dbc89 --- /dev/null +++ b/src/Lucene.Net/Util/ThreadInterruptedException.cs @@ -0,0 +1,64 @@ +using System; +#if FEATURE_SERIALIZABLE_EXCEPTIONS +using System.Runtime.Serialization; +#endif + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + /// <summary> + /// Thrown by Lucene on detecing that <see cref="System.Threading.Thread.Interrupt()"/> had been + /// called. This exception has the specific purpose of being allowed to pass through to the + /// calling thread of <see cref="J2N.Threading.ThreadJob"/> so it reaches the appropriate handler. + /// </summary> + // LUCENENET: In Lucene, this exception was so it could be re-thrown unchecked. It has been + // re-purposed in .NET but used in all the same scenerios. + // LUCENENET: It is no longer good practice to use binary serialization. + // See: https://github.com/dotnet/corefx/issues/23584#issuecomment-325724568 +#if FEATURE_SERIALIZABLE_EXCEPTIONS + [Serializable] +#endif + public class ThreadInterruptedException : Exception, IRuntimeException + { + public ThreadInterruptedException(Exception interruptedException) + : base(interruptedException.Message, interruptedException) + { + } + + public ThreadInterruptedException(string message) : base(message) + { + } + + public ThreadInterruptedException(string message, Exception innerException) : base(message, innerException) + { + } + +#if FEATURE_SERIALIZABLE_EXCEPTIONS + /// <summary> + /// Initializes a new instance of this class with serialized data. + /// </summary> + /// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param> + /// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param> + protected ThreadInterruptedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +}
