using System; using System.Threading; namespace ProcessHacker.Common.Threading { /// /// Provides methods for managing object/resource destruction. /// public sealed class RundownProtection { private object _rundownLock = new object(); private volatile bool _rundownActive = false; private int _refCount = 0; /// /// Attempts to acquire rundown protection. /// /// Whether rundown protection was acquired. public bool Acquire() { Thread.BeginCriticalRegion(); try { lock (_rundownLock) { if (_rundownActive) return false; Interlocked.Increment(ref _refCount); return true; } } finally { Thread.EndCriticalRegion(); } } /// /// Releases rundown protection. /// public void Release() { Thread.BeginCriticalRegion(); try { lock (_rundownLock) { int newRefCount = Interlocked.Decrement(ref _refCount); if (newRefCount < 0) throw new InvalidOperationException("Reference count cannot be negative."); if (_rundownActive) { // If we are the last out, release all waiters. if (newRefCount == 0) Monitor.PulseAll(_rundownLock); } } } finally { Thread.EndCriticalRegion(); } } /// /// Waits for all references to be released while disallowing /// attempts to acquire rundown protection. /// public void Wait() { this.Wait(-1); } /// /// Waits for all references to be released while disallowing /// attempts to acquire rundown protection. /// /// The timeout, in milliseconds. /// Whether all references were released. public bool Wait(int timeout) { lock (_rundownLock) { _rundownActive = true; // If there are no references, we can exit. if (Thread.VolatileRead(ref _refCount) == 0) return true; // Otherwise, wait for the release signal. return Monitor.Wait(_rundownLock, timeout); } } } }