SharedLock.cs
201 lines
| 6.3 KiB
| text/x-csharp
|
CSharpLexer
|
|
r289 | 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); | ||||
| } | ||||
| } | ||||
| } | ||||
