Mercurial > pub > ImplabNet
diff Implab/Parallels/SharedLock.cs @ 136:e9e7940c7d98 v2
shared locks + tests
author | cin |
---|---|
date | Mon, 16 Feb 2015 01:14:09 +0300 |
parents | 671f60cd0250 |
children |
line wrap: on
line diff
--- a/Implab/Parallels/SharedLock.cs Fri Feb 13 02:08:01 2015 +0300 +++ b/Implab/Parallels/SharedLock.cs Mon Feb 16 01:14:09 2015 +0300 @@ -8,23 +8,53 @@ /// </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) { - if (m_locks > 0 && !Monitor.Wait(m_lock, timeout)) - return false; + 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 = 1; + m_locks ++; return true; } } public void LockExclusive() { - LockExclusive(-1); + 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) { @@ -32,45 +62,141 @@ return true; } - if (m_locks == 0) { + if (m_locks == m_upgrades) { m_exclusive = false; m_locks = 1; return true; } - - if (Monitor.Wait(m_lock, timeout)) { - Debug.Assert(m_locks == 0); - m_locks = 1; + + 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; - return true; + m_locks++; + } else { + while (m_exclusive && m_locks > m_upgrades) + Monitor.Wait(m_lock); + + m_locks++; + m_exclusive = false; } - return false; } } - public void LockShared() { - LockShared(-1); - } - - public void ReleaseShared() { + /// <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 || m_locks <= 0) - throw new InvalidOperationException(); - m_locks--; - if (m_locks == 0) - Monitor.PulseAll(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; + } + } } } - public void ReleaseExclusive() { + /// <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 && m_locks != 1) + if (m_exclusive) + return true; + if (m_locks <= m_upgrades) throw new InvalidOperationException(); - m_locks = 0; - Monitor.PulseAll(m_lock); + + 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); + } } }