using System;
using System.Threading;
namespace Implab.Parallels {
///
/// Implements a lightweight mechanism to aquire a shared or an exclusive lock.
///
public class SharedLock {
readonly object m_lock = new object();
// the count of locks currently acquired by clients
int m_locks;
// the count of pending requests for upgrade
int m_upgrades;
bool m_exclusive;
public bool LockExclusive(int timeout) {
lock (m_lock) {
var dt = timeout;
if (m_locks > m_upgrades) {
var t1 = Environment.TickCount;
do {
if (!Monitor.Wait(m_lock, timeout))
return false;
if (m_locks == m_upgrades)
break;
if (timeout > 0) {
dt = timeout - Environment.TickCount + t1;
if (dt < 0)
return false;
}
} while(true);
}
m_exclusive = true;
m_locks ++;
return true;
}
}
public void LockExclusive() {
lock (m_lock) {
while (m_locks > m_upgrades)
Monitor.Wait(m_lock);
m_exclusive = true;
m_locks ++;
}
}
///
/// Acquires a shared lock.
///
/// true, if the shared lock was acquired, false if the specified timeout was expired.
/// Timeout.
public bool LockShared(int timeout) {
lock (m_lock) {
if (!m_exclusive) {
m_locks++;
return true;
}
if (m_locks == m_upgrades) {
m_exclusive = false;
m_locks = 1;
return true;
}
var t1 = Environment.TickCount;
var dt = timeout;
do {
if (!Monitor.Wait(m_lock, dt))
return false;
if (m_locks == m_upgrades || !m_exclusive)
break;
if (timeout >= 0) {
dt = timeout - Environment.TickCount + t1;
if (dt < 0)
return false;
}
} while(true);
m_locks ++;
m_exclusive = false;
return true;
}
}
///
/// Acquires the shared lock.
///
public void LockShared() {
lock (m_lock) {
if (!m_exclusive) {
m_locks++;
} else if (m_locks == m_upgrades) {
m_exclusive = false;
m_locks++;
} else {
while (m_exclusive && m_locks > m_upgrades)
Monitor.Wait(m_lock);
m_locks++;
m_exclusive = false;
}
}
}
///
/// Upgrades the current lock to exclusive level.
///
/// If the current lock is exclusive already the method does nothing.
public void Upgrade() {
lock (m_lock) {
if (!m_exclusive) {
if (m_locks <= m_upgrades)
throw new InvalidOperationException();
if (m_locks - m_upgrades == 1) {
m_exclusive = true;
} else {
m_upgrades++;
while (m_locks > m_upgrades)
Monitor.Wait(m_lock);
m_upgrades--;
m_exclusive = true;
}
}
}
}
///
/// Upgrades the current lock to exclusive level.
///
/// Timeout.
/// true if the current lock was updated, false the specified timeout was expired.
/// If the current lock is exclusive already the method does nothing.
public bool Upgrade(int timeout) {
lock (m_lock) {
if (m_exclusive)
return true;
if (m_locks <= m_upgrades)
throw new InvalidOperationException();
if (m_locks - m_upgrades == 1) {
m_exclusive = true;
} else {
var t1 = Environment.TickCount;
var dt = timeout;
m_upgrades++;
do {
if (!Monitor.Wait(m_lock, dt)) {
m_upgrades--;
return false;
}
// we may get there but the shared lock already aquired
if (m_locks == m_upgrades)
break;
if (timeout >= 0) {
dt = timeout - Environment.TickCount + t1;
if (dt < 0) {
m_upgrades--;
return false;
}
}
} while(true);
m_upgrades--;
m_exclusive = true;
}
return true;
}
}
///
/// Downgrades this lock to shared level.
///
public void Downgrade() {
lock (m_lock)
m_exclusive = false;
}
///
/// Releases the current lock.
///
public void Release() {
lock (m_lock)
// if no more running threads left
if (--m_locks == m_upgrades)
Monitor.PulseAll(m_lock);
}
}
}