diff Implab/Parallels/SharedLock.cs @ 136:e9e7940c7d98 v2

shared locks + tests
author cin
date Mon, 16 Feb 2015 01:14:09 +0300
parents 671f60cd0250
children
line wrap: on
line diff
--- a/Implab/Parallels/SharedLock.cs	Fri Feb 13 02:08:01 2015 +0300
+++ b/Implab/Parallels/SharedLock.cs	Mon Feb 16 01:14:09 2015 +0300
@@ -8,23 +8,53 @@
     /// </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) {
-                if (m_locks > 0 && !Monitor.Wait(m_lock, timeout))
-                    return false;
+                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 = 1;
+                m_locks ++;
                 return true;
             }
         }
 
         public void LockExclusive() {
-            LockExclusive(-1);
+            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) {
@@ -32,45 +62,141 @@
                     return true;
                 }
 
-                if (m_locks == 0) {
+                if (m_locks == m_upgrades) {
                     m_exclusive = false;
                     m_locks = 1;
                     return true;
                 }
-                
-                if (Monitor.Wait(m_lock, timeout)) {
-                    Debug.Assert(m_locks == 0);
-                    m_locks = 1;
+
+                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;
-                    return true;
+                    m_locks++;
+                } else {
+                    while (m_exclusive && m_locks > m_upgrades)
+                        Monitor.Wait(m_lock);
+
+                    m_locks++;
+                    m_exclusive = false;
                 }
-                return false;
             }
         }
 
-        public void LockShared() {
-            LockShared(-1);
-        }
-
-        public void ReleaseShared() {
+        /// <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 || m_locks <= 0)
-                    throw new InvalidOperationException();
-                m_locks--;
-                if (m_locks == 0)
-                    Monitor.PulseAll(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;
+                    }
+                }
             }
         }
 
-        public void ReleaseExclusive() {
+        /// <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 && m_locks != 1)
+                if (m_exclusive)
+                    return true;
+                if (m_locks <= m_upgrades)
                     throw new InvalidOperationException();
-                m_locks = 0;
-                Monitor.PulseAll(m_lock);
+
+                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);
+        }
     }
 }