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 Waiting => waiting; public IEnumerable Locks => locks; HashSet locks = new HashSet(); Lock writeWait; HashSet waiting = new HashSet(); 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); } } } }