ln.threading/ln.threading/CCALock.cs

125 lines
3.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
namespace ln.threading
{
public enum CCAState {
READ, // Read access may be acquired
WAIT, // Write access is to be acquired after current reads are released
WRITE // Write access is acquired
}
public class CCALock
{
public CCAState State { get; private set; }
public IEnumerable<Lock> Waiting => waiting;
public IEnumerable<Lock> Locks => locks;
HashSet<Lock> locks = new HashSet<Lock>();
Lock writeWait;
HashSet<Lock> waiting = new HashSet<Lock>();
public CCALock()
{}
public Lock AcquireRead() => AcquireLock(false, -1);
public Lock AcquireWrite() => AcquireLock(true, -1);
public Lock AcquireRead(int timeout) => AcquireLock(false, timeout);
public Lock AcquireWrite(int timeout) => AcquireLock(true, timeout);
public Lock AcquireLock(bool writeaccess,int timeout)
{
Lock lck = new Lock(this);
lock (this)
{
WaitForRead(lck, timeout);
if (writeaccess)
{
writeWait = lck;
if (locks.Count > 0)
{
State = CCAState.WAIT;
lock (writeWait)
{
Monitor.Exit(this);
if (!Monitor.Wait(writeWait, timeout))
{
Monitor.Enter(this);
State = CCAState.READ;
throw new TimeoutException();
}
Monitor.Enter(this);
}
}
State = CCAState.WRITE;
}
locks.Add(lck);
lck.Acquired = true;
}
return lck;
}
private void WaitForRead(Lock lck,int timeout)
{
while (State != CCAState.READ)
{
waiting.Add(lck);
if (!Monitor.Wait(this, timeout))
throw new TimeoutException();
waiting.Remove(lck);
}
}
public void ReleaseLock(Lock lck)
{
lock (this)
{
locks.Remove(lck);
if ((State == CCAState.WAIT) && (locks.Count == 0))
{
lock (writeWait)
{
Monitor.Pulse(writeWait);
}
}
else if (State == CCAState.WRITE)
{
if (writeWait != lck)
throw new LockingException();
writeWait = null;
State = CCAState.READ;
Monitor.PulseAll(this);
}
}
}
public class Lock : IDisposable
{
public CCALock CCALock { get; }
public bool Acquired { get; internal set; }
public Lock(CCALock cca)
{
CCALock = cca;
}
public void Dispose()
{
if (Acquired)
CCALock.ReleaseLock(this);
}
}
}
}