view Implab/Parallels/SharedLock.cs @ 187:dd4a3590f9c6 ref20160224

Reworked cancelation handling, if the cancel handler isn't specified the OperationCanceledException will be handled by the error handler Any unhandled OperationCanceledException will cause the promise cancelation
author cin
date Tue, 19 Apr 2016 17:35:20 +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);
        }
    }
}