Mercurial > pub > ImplabNet
view Implab/Parallels/SharedLock.cs @ 138:f75cfa58e3d4 v2
added ICancellable.Cancel(Exception) to allow specify the reason of cancellation
author | cin |
---|---|
date | Tue, 17 Feb 2015 18:16:26 +0300 |
parents | e9e7940c7d98 |
children |
line wrap: on
line source
using System; using System.Threading; using System.Diagnostics; 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); } } }