diff --git a/src/Lucene.Net/Support/Threading/UninterruptableMonitor.cs b/src/Lucene.Net/Support/Threading/UninterruptableMonitor.cs
index a6196e0a6b..a9eeeaef17 100644
--- a/src/Lucene.Net/Support/Threading/UninterruptableMonitor.cs
+++ b/src/Lucene.Net/Support/Threading/UninterruptableMonitor.cs
@@ -25,19 +25,63 @@ namespace Lucene.Net.Support.Threading
/// A drop-in replacement for that doesn't throw
/// when entering locks, but defers the excepetion until a wait or sleep occurs. This is to mimic the behavior in Java,
/// which does not throw when entering a lock.
+ ///
+ /// NOTE: this is just a best effort. The BCL and other libraries we depend
+ /// on don't take such measures, so any call to an API that we don't own could result
+ /// in a if it attempts to
+ /// aquire a lock. It is not practical to put a try/catch block around every 3rd party
+ /// API call that attempts to lock. As such, Lucene.NET does not support
+ /// and using it is discouraged.
+ /// See https://github.com/apache/lucenenet/issues/526.
///
internal static class UninterruptableMonitor
{
+ ///
+ /// Acquires an exclusive lock on the specified object, and atomically sets a
+ /// value that indicates whether the lock was taken. See
+ /// for more details.
+ ///
+ /// If the lock is interrupted, this method will not throw a
+ /// . Instead,
+ /// it will reset the interrupt state. This matches the behavior of the
+ /// synchronized keyword in Java, which never throws when the current
+ /// thread is in an interrupted state. It allows us to catch
+ /// in a specific part
+ /// of the application, rather than allowing it to be thrown anywhere we atempt
+ /// to lock.
+ ///
+ /// NOTE: this is just a best effort. The BCL and other libraries we depend
+ /// on don't take such measures, so any call to an API that we don't own could result
+ /// in a if it attempts to
+ /// aquire a lock. It is not practical to put a try/catch block around every 3rd party
+ /// API call that attempts to lock. As such, Lucene.NET does not support
+ /// and using it is discouraged.
+ /// See https://github.com/apache/lucenenet/issues/526.
+ ///
public static void Enter(object obj, ref bool lockTaken)
{
// enter the lock and ignore any System.Threading.ThreadInterruptedException
try
{
- Monitor.Enter(obj, ref lockTaken);
+ Monitor.Enter(obj, ref lockTaken); // Fast path - don't allocate retry on stack in this case
}
- catch (Exception ie) when(ie.IsInterruptedException())
+ catch (Exception ie) when (ie.IsInterruptedException())
{
- RetryEnter(obj, ref lockTaken);
+ do
+ {
+ try
+ {
+ // The interrupted exception may have already cleared the flag, and this will
+ // succeed without any more exceptions
+ Monitor.Enter(obj, ref lockTaken);
+ break;
+ }
+ catch (Exception e) when (e.IsInterruptedException())
+ {
+ // try again until we succeed, since an interrupt could have happened since it was cleared
+ }
+ }
+ while (true);
// The lock has been obtained, now reset the interrupted status for the
// current thread
@@ -45,30 +89,51 @@ public static void Enter(object obj, ref bool lockTaken)
}
}
- private static void RetryEnter(object obj, ref bool lockTaken)
- {
- try
- {
- // An interrupted exception may have already cleared the flag, and this will succeed without any more excpetions
- Monitor.Enter(obj, ref lockTaken);
- }
- catch (Exception ie) when (ie.IsInterruptedException())
- {
- // try again until we succeed, since an interrupt could have happened since it was cleared
- RetryEnter(obj, ref lockTaken);
- }
- }
-
+ ///
+ /// Acquires an exclusive lock on the specified object. See
+ /// for more details.
+ ///
+ /// If the lock is interrupted, this method will not throw a
+ /// . Instead,
+ /// it will reset the interrupt state. This matches the behavior of the
+ /// synchronized keyword in Java, which never throws when the current
+ /// thread is in an interrupted state. It allows us to catch
+ /// in a specific part
+ /// of the application, rather than allowing it to be thrown anywhere we atempt
+ /// to lock.
+ ///
+ /// NOTE: this is just a best effort. The BCL and other libraries we depend
+ /// on don't take such measures, so any call to an API that we don't own could result
+ /// in a if it attempts to
+ /// aquire a lock. It is not practical to put a try/catch block around every 3rd party
+ /// API call that attempts to lock. As such, Lucene.NET does not support
+ /// and using it is discouraged.
+ /// See https://github.com/apache/lucenenet/issues/526.
+ ///
public static void Enter(object obj)
{
// enter the lock and ignore any System.Threading.ThreadInterruptedException
try
{
- Monitor.Enter(obj);
+ Monitor.Enter(obj); // Fast path - don't allocate retry on stack in this case
}
catch (Exception ie) when (ie.IsInterruptedException())
{
- RetryEnter(obj);
+ do
+ {
+ try
+ {
+ // The interrupted exception may have already cleared the flag, and this will
+ // succeed without any more exceptions
+ Monitor.Enter(obj);
+ break;
+ }
+ catch (Exception e) when (e.IsInterruptedException())
+ {
+ // try again until we succeed, since an interrupt could have happened since it was cleared
+ }
+ }
+ while (true);
// The lock has been obtained, now reset the interrupted status for the
// current thread
@@ -76,104 +141,180 @@ public static void Enter(object obj)
}
}
- private static void RetryEnter(object obj)
- {
- try
- {
- // An interrupted exception may have already cleared the flag, and this will succeed without any more excpetions
- Monitor.Enter(obj);
- }
- catch (Exception ie) when (ie.IsInterruptedException())
- {
- // try again until we succeed, since an interrupt could have happened since it was cleared
- RetryEnter(obj);
- }
- }
-
+ ///
+ /// Cascades the call to .
+ ///
+ /// Releases an exclusive lock on the specified object.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Exit(object obj)
{
Monitor.Exit(obj);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Determines whether the current thread holds the lock on the
+ /// specified object.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsEntered(object obj)
{
return Monitor.IsEntered(obj);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Attempts to acquire an exclusive lock on the specified object.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryEnter(object obj)
{
return Monitor.TryEnter(obj);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Attempts to acquire an exclusive lock on the specified object, and atomically
+ /// sets a value that indicates whether the lock was taken.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void TryEnter(object obj, ref bool lockTaken)
{
Monitor.TryEnter(obj, ref lockTaken);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Attempts, for the specified number of milliseconds, to acquire an
+ /// exclusive lock on the specified object.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryEnter(object obj, int millisecondsTimeout)
{
return Monitor.TryEnter(obj, millisecondsTimeout);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Attempts, for the specified amount of time, to acquire an exclusive
+ /// lock on the specified object.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryEnter(object obj, TimeSpan timeout)
{
return Monitor.TryEnter(obj, timeout);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Attempts, for the specified number of milliseconds, to acquire an exclusive lock on the specified
+ /// object, and atomically sets a value that indicates whether the lock was taken.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void TryEnter(object obj, int millisecondsTimeout, ref bool lockTaken)
{
Monitor.TryEnter(obj, millisecondsTimeout, ref lockTaken);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Attempts, for the specified amount of time, to acquire an exclusive lock on the specified object,
+ /// and atomically sets a value that indicates whether the lock was taken.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void TryEnter(object obj, TimeSpan timeout, ref bool lockTaken)
{
Monitor.TryEnter(obj, timeout, ref lockTaken);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Notifies a thread in the waiting queue of a change in the locked object's state.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Pulse(object obj)
{
Monitor.Pulse(obj);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Notifies all waiting threads of a change in the object's state.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void PulseAll(object obj)
{
Monitor.PulseAll(obj);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Releases the lock on an object and blocks the current thread until it reacquires the lock.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Wait(object obj)
{
Monitor.Wait(obj);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Releases the lock on an object and blocks the current thread until it reacquires the lock.
+ /// If the specified time-out interval elapses, the thread enters the ready queue.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Wait(object obj, int millisecondsTimeout)
{
Monitor.Wait(obj, millisecondsTimeout);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Releases the lock on an object and blocks the current thread until it reacquires the lock.
+ /// If the specified time-out interval elapses, the thread enters the ready queue.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Wait(object obj, TimeSpan timeout)
{
Monitor.Wait(obj, timeout);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Releases the lock on an object and blocks the current thread until it
+ /// reacquires the lock. If the specified time-out interval elapses, the
+ /// thread enters the ready queue. This method also specifies whether the
+ /// synchronization domain for the context (if in a synchronized context)
+ /// is exited before the wait and reacquired afterward.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Wait(object obj, int millisecondsTimeout, bool exitContext)
{
Monitor.Wait(obj, millisecondsTimeout, exitContext);
}
+ ///
+ /// Cascades the call to .
+ ///
+ /// Releases the lock on an object and blocks the current thread until it reacquires the lock
+ /// If the specified time-out interval elapses, the thread enters the ready queue. This method
+ /// also specifies whether the synchronization domain for the context (if in a synchronized
+ /// context) is exited before the wait and reacquired afterward.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Wait(object obj, TimeSpan timeout, bool exitContext)
{