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);
}
}
}
}