From this page:
Every lock acquisition might throw an exception. Be prepared for it.
Most locks lazily allocate an event if a lock acquisition encounters contention, including CLR monitors. This allocation can fail during low resource conditions, causing OOMs originating from the entrance to the lock. (Note that a typical non-blocking spin lock cannot fail with OOM, which allows it to be used in some resource constrained scenarios such as inside a CER.) Similarly, a host like SQL Server can perform deadlock detection and even break those deadlocks by generating exceptions that originate from the Enter statement, manifesting as a System.Runtime.InteropServices.COMException.
Often there isn’t much that can be done in response to such an exception. But reliability- and security-sensitive code that must deal with failure robustly should consider this case. We would have liked it to be the case that host deadlocks can be responded to intelligently, but most library code can’t intelligently unwind to a safe point on the stack so that it can back-off and retry the operation. There is simply too much cross-library intermixing on a typical stack. This is why timeout-based Monitor acquisitions with TryEnter are typically a bad idea for deadlock prevention.
So as you can read, it seems that under resource constrained conditions we might get exceptions thrown from the Enter method of Monitor which lock(o) uses internally.
So perhaps your solution is something like a spin-wait?
{
uint iters = 0;
while (!cond) {
if ((++iters % 50) == 0) {
// Every so often we sleep with a 1ms timeout (see #30 for justification).
Thread.Sleep(1);
} else if (Environment.ProcessorCount == 1) {
// On a single-CPU machine we yield the thread.
Thread.Sleep(0);
} else {
// Issue YIELD instructions to let the other hardware thread move.
Thread.SpinWait(25);
}
}
}
Where cond could be some
private volatile int cond = 0
used with e.g. Interlocked.CompareExchange where you change to e.g. Thread.Current.ManagedThreadID or something else non-zero?