|
|
using System;
|
|
|
using System.Threading;
|
|
|
|
|
|
namespace Implab.Parallels {
|
|
|
/// <summary>
|
|
|
/// Implements a lightweight mechanism to aquire a shared or an exclusive lock.
|
|
|
/// </summary>
|
|
|
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 ++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Acquires a shared lock.
|
|
|
/// </summary>
|
|
|
/// <returns><c>true</c>, if the shared lock was acquired, <c>false</c> if the specified timeout was expired.</returns>
|
|
|
/// <param name="timeout">Timeout.</param>
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Acquires the shared lock.
|
|
|
/// </summary>
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Upgrades the current lock to exclusive level.
|
|
|
/// </summary>
|
|
|
/// <remarks>If the current lock is exclusive already the method does nothing.</remarks>
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Upgrades the current lock to exclusive level.
|
|
|
/// </summary>
|
|
|
/// <param name="timeout">Timeout.</param>
|
|
|
/// <returns><c>true</c> if the current lock was updated, <c>false</c> the specified timeout was expired.</returns>
|
|
|
/// <remarks>If the current lock is exclusive already the method does nothing.</remarks>
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Downgrades this lock to shared level.
|
|
|
/// </summary>
|
|
|
public void Downgrade() {
|
|
|
lock (m_lock)
|
|
|
m_exclusive = false;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Releases the current lock.
|
|
|
/// </summary>
|
|
|
public void Release() {
|
|
|
lock (m_lock)
|
|
|
// if no more running threads left
|
|
|
if (--m_locks == m_upgrades)
|
|
|
Monitor.PulseAll(m_lock);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|