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); } } }