125 lines
3.4 KiB
C#
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|