view Implab/Parallels/SharedLock.cs @ 145:706fccb85524 v2

RC: cancellation support for promises + tests
author cin
date Sun, 08 Mar 2015 02:52:27 +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);
        }
    }
}