Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / System.Core / System.Threading / ReaderWriterLockSlim.cs
index 9becf8b06a8e77d088efd2471b750450eb7a65f8..512f97b27cb15619cd97894d61102b3cb895806d 100644 (file)
@@ -123,6 +123,12 @@ namespace System.Threading {
                }
 
                public bool TryEnterReadLock (int millisecondsTimeout)
+               {
+                       bool dummy = false;
+                       return TryEnterReadLock (millisecondsTimeout, ref dummy);
+               }
+
+               bool TryEnterReadLock (int millisecondsTimeout, ref bool success)
                {
                        ThreadLockState ctstate = CurrentThreadState;
 
@@ -137,13 +143,14 @@ namespace System.Threading {
                        // Same idea when recursion is allowed and a write thread wants to
                        // go for a Read too.
                        if (ctstate.LockState.Has (LockState.Upgradable)
-                           || recursionPolicy == LockRecursionPolicy.SupportsRecursion) {
+                           || (!noRecursion && ctstate.LockState.Has (LockState.Write))) {
                                RuntimeHelpers.PrepareConstrainedRegions ();
                                try {}
                                finally {
                                        Interlocked.Add (ref rwlock, RwRead);
-                                       ctstate.LockState ^= LockState.Read;
+                                       ctstate.LockState |= LockState.Read;
                                        ++ctstate.ReaderRecursiveCount;
+                                       success = true;
                                }
 
                                return true;
@@ -179,11 +186,13 @@ namespace System.Threading {
                                                ctstate.LockState ^= LockState.Read;
                                                ++ctstate.ReaderRecursiveCount;
                                                --numReadWaiters;
-                                               return true;
+                                               success = true;
                                        } else {
                                                Interlocked.Add (ref rwlock, -RwRead);
                                        }
                                }
+                               if (success)
+                                       return true;
 
                                writerDoneEvent.Wait (ComputeTimeout (millisecondsTimeout, start));
                        } while (millisecondsTimeout == -1 || (sw.ElapsedMilliseconds - start) < millisecondsTimeout);
@@ -207,10 +216,11 @@ namespace System.Threading {
                                if (!ctstate.LockState.Has (LockState.Read))
                                        throw new SynchronizationLockException ("The current thread has not entered the lock in read mode");
 
-                               ctstate.LockState ^= LockState.Read;
-                               --ctstate.ReaderRecursiveCount;
-                               if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
-                                       readerDoneEvent.Set ();
+                               if (--ctstate.ReaderRecursiveCount == 0) {
+                                       ctstate.LockState ^= LockState.Read;
+                                       if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
+                                               readerDoneEvent.Set ();
+                               }
                        }
                }
 
@@ -230,50 +240,82 @@ namespace System.Threading {
 
                        ++numWriteWaiters;
                        bool isUpgradable = ctstate.LockState.Has (LockState.Upgradable);
+                       bool registered = false;
+                       bool success = false;
 
-                       /* If the code goes there that means we had a read lock beforehand
-                        * that need to be suppressed, we also take the opportunity to register
-                        * our interest in the write lock to avoid other write wannabe process
-                        * coming in the middle
-                        */
-                       if (isUpgradable && rwlock >= RwRead)
-                               if (Interlocked.Add (ref rwlock, RwWaitUpgrade - RwRead) >> RwReadBit == 0)
-                                       readerDoneEvent.Set ();
+                       RuntimeHelpers.PrepareConstrainedRegions ();
+                       try {
+                               /* If the code goes there that means we had a read lock beforehand
+                                * that need to be suppressed, we also take the opportunity to register
+                                * our interest in the write lock to avoid other write wannabe process
+                                * coming in the middle
+                                */
+                               if (isUpgradable && rwlock >= RwRead) {
+                                       try {}
+                                       finally {
+                                               if (Interlocked.Add (ref rwlock, RwWaitUpgrade - RwRead) >> RwReadBit == 0)
+                                                       readerDoneEvent.Set ();
+                                               registered = true;
+                                       }
+                               }
 
-                       int stateCheck = isUpgradable ? RwWaitUpgrade : RwWait;
-                       long start = millisecondsTimeout == -1 ? 0 : sw.ElapsedMilliseconds;
+                               int stateCheck = isUpgradable ? RwWaitUpgrade + RwWait : RwWait;
+                               long start = millisecondsTimeout == -1 ? 0 : sw.ElapsedMilliseconds;
+                               int registration = isUpgradable ? RwWaitUpgrade : RwWait;
 
-                       do {
-                               int state = rwlock;
-
-                               if (state <= stateCheck) {
-                                       if (Interlocked.CompareExchange (ref rwlock, RwWrite, state) == state) {
-                                               writerDoneEvent.Reset ();
-                                               ctstate.LockState ^= LockState.Write;
-                                               ++ctstate.WriterRecursiveCount;
-                                               --numWriteWaiters;
-                                               return true;
+                               do {
+                                       int state = rwlock;
+
+                                       if (state <= stateCheck) {
+                                               try {}
+                                               finally {
+                                                       var toWrite = state + RwWrite - (registered ? registration : 0);
+                                                       if (Interlocked.CompareExchange (ref rwlock, toWrite, state) == state) {
+                                                               writerDoneEvent.Reset ();
+                                                               ctstate.LockState ^= LockState.Write;
+                                                               ++ctstate.WriterRecursiveCount;
+                                                               --numWriteWaiters;
+                                                               registered = false;
+                                                               success = true;
+                                                       }
+                                               }
+                                               if (success)
+                                                       return true;
                                        }
+
                                        state = rwlock;
-                               }
 
-                               // We register our interest in taking the Write lock (if upgradeable it's already done)
-                               if (!isUpgradable)
-                                       while ((state & RwWait) == 0 && Interlocked.CompareExchange (ref rwlock, state | RwWait, state) != state)
-                                               state = rwlock;
+                                       // We register our interest in taking the Write lock (if upgradeable it's already done)
+                                       if (!isUpgradable) {
+                                               while ((state & RwWait) == 0) {
+                                                       try {}
+                                                       finally {
+                                                               if (Interlocked.CompareExchange (ref rwlock, state | RwWait, state) == state)
+                                                                       registered = true;
+                                                       }
+                                                       if (registered)
+                                                               break;
+                                                       state = rwlock;
+                                               }
+                                       }
 
-                               // Before falling to sleep
-                               do {
-                                       if (rwlock <= stateCheck)
-                                               break;
-                                       if ((rwlock & RwWrite) != 0)
-                                               writerDoneEvent.Wait (ComputeTimeout (millisecondsTimeout, start));
-                                       else if ((rwlock >> RwReadBit) > 0)
-                                               readerDoneEvent.Wait (ComputeTimeout (millisecondsTimeout, start));
+                                       // Before falling to sleep
+                                       do {
+                                               if (rwlock <= stateCheck)
+                                                       break;
+                                               if ((rwlock & RwWrite) != 0)
+                                                       writerDoneEvent.Wait (ComputeTimeout (millisecondsTimeout, start));
+                                               else if ((rwlock >> RwReadBit) > 0)
+                                                       readerDoneEvent.Wait (ComputeTimeout (millisecondsTimeout, start));
+                                       } while (millisecondsTimeout < 0 || (sw.ElapsedMilliseconds - start) < millisecondsTimeout);
                                } while (millisecondsTimeout < 0 || (sw.ElapsedMilliseconds - start) < millisecondsTimeout);
-                       } while (millisecondsTimeout < 0 || (sw.ElapsedMilliseconds - start) < millisecondsTimeout);
 
-                       --numWriteWaiters;
+                               --numWriteWaiters;
+                       } finally {
+                               if (registered)
+                                       Interlocked.Add (ref rwlock, isUpgradable ? -RwWaitUpgrade : -RwWait);
+                       }
+
                        return false;
                }
 
@@ -284,19 +326,24 @@ namespace System.Threading {
 
                public void ExitWriteLock ()
                {
-                       ThreadLockState ctstate = CurrentThreadState;
+                       RuntimeHelpers.PrepareConstrainedRegions ();
+                       try {}
+                       finally {
+                               ThreadLockState ctstate = CurrentThreadState;
 
-                       if (!ctstate.LockState.Has (LockState.Write))
-                               throw new SynchronizationLockException ("The current thread has not entered the lock in write mode");
+                               if (!ctstate.LockState.Has (LockState.Write))
+                                       throw new SynchronizationLockException ("The current thread has not entered the lock in write mode");
                        
-                       bool isUpgradable = ctstate.LockState.Has (LockState.Upgradable);
-                       ctstate.LockState ^= LockState.Write;
-                       --ctstate.WriterRecursiveCount;
-
-                       int value = Interlocked.Add (ref rwlock, isUpgradable ? RwRead - RwWrite : -RwWrite);
-                       writerDoneEvent.Set ();
-                       if (isUpgradable && value >> RwReadBit == 1)
-                               readerDoneEvent.Reset ();
+                               if (--ctstate.WriterRecursiveCount == 0) {
+                                       bool isUpgradable = ctstate.LockState.Has (LockState.Upgradable);
+                                       ctstate.LockState ^= LockState.Write;
+
+                                       int value = Interlocked.Add (ref rwlock, isUpgradable ? RwRead - RwWrite : -RwWrite);
+                                       writerDoneEvent.Set ();
+                                       if (isUpgradable && value >> RwReadBit == 1)
+                                               readerDoneEvent.Reset ();
+                               }
+                       }
                }
 
                public void EnterUpgradeableReadLock ()
@@ -322,34 +369,52 @@ namespace System.Threading {
 
                        ++numUpgradeWaiters;
                        long start = millisecondsTimeout == -1 ? 0 : sw.ElapsedMilliseconds;
+                       bool taken = false;
+                       bool success = false;
 
                        // We first try to obtain the upgradeable right
-                       while (!upgradableEvent.IsSet () || !upgradableTaken.TryRelaxedSet ()) {
-                               if (millisecondsTimeout != -1 && (sw.ElapsedMilliseconds - start) > millisecondsTimeout) {
-                                       --numUpgradeWaiters;
-                                       return false;
+                       try {
+                               while (!upgradableEvent.IsSet () || !taken) {
+                                       try {}
+                                       finally {
+                                               taken = upgradableTaken.TryRelaxedSet ();
+                                       }
+                                       if (taken)
+                                               break;
+                                       if (millisecondsTimeout != -1 && (sw.ElapsedMilliseconds - start) > millisecondsTimeout) {
+                                               --numUpgradeWaiters;
+                                               return false;
+                                       }
+
+                                       upgradableEvent.Wait (ComputeTimeout (millisecondsTimeout, start));
                                }
 
-                               upgradableEvent.Wait (ComputeTimeout (millisecondsTimeout, start));
-                       }
+                               upgradableEvent.Reset ();
 
-                       upgradableEvent.Reset ();
+                               RuntimeHelpers.PrepareConstrainedRegions ();
+                               try {
+                                       // Then it's a simple reader lock acquiring
+                                       TryEnterReadLock (ComputeTimeout (millisecondsTimeout, start), ref success);
+                               } finally {
+                                       if (success) {
+                                               ctstate.LockState |= LockState.Upgradable;
+                                               ctstate.LockState &= ~LockState.Read;
+                                               --ctstate.ReaderRecursiveCount;
+                                               ++ctstate.UpgradeableRecursiveCount;
+                                       } else {
+                                               upgradableTaken.Value = false;
+                                               upgradableEvent.Set ();
+                                       }
+                               }
 
-                       // Then it's a simple reader lock acquiring
-                       if (TryEnterReadLock (ComputeTimeout (millisecondsTimeout, start))) {
-                               ctstate.LockState = LockState.Upgradable;
                                --numUpgradeWaiters;
-                               --ctstate.ReaderRecursiveCount;
-                               ++ctstate.UpgradeableRecursiveCount;
-                               return true;
+                       } catch {
+                               // An async exception occured, if we had taken the upgradable mode, release it
+                               if (taken && !success)
+                                       upgradableTaken.Value = false;
                        }
 
-                       upgradableTaken.Value = false;
-                       upgradableEvent.Set ();
-
-                       --numUpgradeWaiters;
-
-                       return false;
+                       return success;
                }
 
                public bool TryEnterUpgradeableReadLock (TimeSpan timeout)
@@ -359,18 +424,24 @@ namespace System.Threading {
               
                public void ExitUpgradeableReadLock ()
                {
-                       ThreadLockState ctstate = CurrentThreadState;
+                       RuntimeHelpers.PrepareConstrainedRegions ();
+                       try {}
+                       finally {
+                               ThreadLockState ctstate = CurrentThreadState;
 
-                       if (!ctstate.LockState.Has (LockState.Upgradable | LockState.Read))
-                               throw new SynchronizationLockException ("The current thread has not entered the lock in upgradable mode");
-                       
-                       upgradableTaken.Value = false;
-                       upgradableEvent.Set ();
+                               if (!ctstate.LockState.Has (LockState.Upgradable | LockState.Read))
+                                       throw new SynchronizationLockException ("The current thread has not entered the lock in upgradable mode");
+
+                               if (--ctstate.UpgradeableRecursiveCount == 0) {
+                                       upgradableTaken.Value = false;
+                                       upgradableEvent.Set ();
+
+                                       ctstate.LockState &= ~LockState.Upgradable;
+                                       if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
+                                               readerDoneEvent.Set ();
+                               }
+                       }
 
-                       ctstate.LockState ^= LockState.Upgradable;
-                       --ctstate.UpgradeableRecursiveCount;
-                       if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
-                               readerDoneEvent.Set ();
                }
 
                public void Dispose ()
@@ -383,7 +454,7 @@ namespace System.Threading {
                                return rwlock >= RwRead && CurrentThreadState.LockState.Has (LockState.Read);
                        }
                }
-               
+
                public bool IsWriteLockHeld {
                        get {
                                return (rwlock & RwWrite) > 0 && CurrentThreadState.LockState.Has (LockState.Write);
@@ -473,7 +544,7 @@ namespace System.Threading {
                        // Detect and prevent recursion
                        LockState ctstate = state.LockState;
 
-                       if (ctstate != LockState.None && noRecursion && (ctstate != LockState.Upgradable || validState == LockState.Upgradable))
+                       if (ctstate != LockState.None && noRecursion && (!ctstate.Has (LockState.Upgradable) || validState == LockState.Upgradable))
                                throw new LockRecursionException ("The current thread has already a lock and recursion isn't supported");
 
                        if (noRecursion)