Merge remote branch 'upstream/master'
[mono.git] / mcs / class / corlib / System.Threading / SemaphoreSlim.cs
index e6cc5599b7473a778c4e02261c754799a2a879a7..455951ad78d7f1f5db2b8883125e2b966884723e 100644 (file)
@@ -1,4 +1,3 @@
-#if NET_4_0
 // SemaphoreSlim.cs
 //
 // Copyright (c) 2008 Jérémie "Garuma" Laval
 using System;
 using System.Diagnostics;
 
+#if NET_4_0
 namespace System.Threading
 {
        public class SemaphoreSlim : IDisposable
        {
-               readonly int max;
-               
+               const int spinCount = 10;
+               const int deepSleepTime = 20;
+
+               readonly int maxCount;
                int currCount;
-               
-               bool isCanceled;
                bool isDisposed;
-               
-               SpinWait wait = new SpinWait ();
-               
-               public SemaphoreSlim (int initial) : this (initial, int.MaxValue)
+
+               EventWaitHandle handle;
+
+               public SemaphoreSlim (int initialCount) : this (initialCount, int.MaxValue)
                {
                }
-               
-               public SemaphoreSlim (int initial, int max)
+
+               public SemaphoreSlim (int initialCount, int maxCount)
                {
-                       if (initial < 0 || initial > max || max < 0)
-                               throw new ArgumentOutOfRangeException ("The initial  argument is negative, initial is greater than max, or max is not positive.");
-                       
-                       this.max = max;
-                       this.currCount = initial;
+                       if (initialCount < 0 || initialCount > maxCount || maxCount < 0)
+                               throw new ArgumentOutOfRangeException ("The initialCount  argument is negative, initialCount is greater than maxCount, or maxCount is not positive.");
+
+                       this.maxCount = maxCount;
+                       this.currCount = initialCount;
+                       this.handle = new ManualResetEvent (initialCount == 0);
                }
-               
+
                ~SemaphoreSlim ()
                {
                        Dispose(false);
                }
-               
+
                public void Dispose ()
                {
                        Dispose(true);
                }
-               
-               protected virtual void Dispose (bool managedRes)
+
+               protected virtual void Dispose (bool disposing)
                {
                        isDisposed = true;
                }
-               
+
                void CheckState ()
                {
-                       if (isCanceled)
-                               throw new OperationCanceledException ("The SemaphoreSlim is canceled.");
                        if (isDisposed)
                                throw new ObjectDisposedException ("The SemaphoreSlim has been disposed.");
                }
-               
+
                public int CurrentCount {
                        get {
                                return currCount;
                        }
                }
-               
-               public bool IsCanceled {
-                       get {
-                               return isCanceled;
-                       }
-               }
-               
-               public void Cancel ()
-               {
-                       isCanceled = true;
-               }
-               
+
                public int Release ()
                {
                        return Release(1);
                }
-               
+
                public int Release (int releaseCount)
                {
                        CheckState ();
-                       if (releaseCount < 0)
-                               throw new ArgumentOutOfRangeException ("releaseCount", "        The releaseCount must be positive.");
-                       
+                       if (releaseCount < 1)
+                               throw new ArgumentOutOfRangeException ("releaseCount", "releaseCount is less than 1");
+
                        // As we have to take care of the max limit we resort to CAS
                        int oldValue, newValue;
                        do {
                                oldValue = currCount;
                                newValue = (currCount + releaseCount);
-                               newValue = newValue > max ? max : newValue;
+                               newValue = newValue > maxCount ? maxCount : newValue;
                        } while (Interlocked.CompareExchange (ref currCount, newValue, oldValue) != oldValue);
-                       
+
+                       handle.Set ();
+
                        return oldValue;
                }
-               
+
                public void Wait ()
                {
-                       CheckState ();
-                       do {
-                               int result = Interlocked.Decrement (ref currCount);
-                               if (result >= 0)
-                                       break;
-                               
-                               // We revert back the operation
-                               Interlocked.Increment (ref currCount);
-                               while (Thread.VolatileRead (ref currCount) <= 0) {
-                                       wait.SpinOnce ();
-                               }
-                       } while (true);
+                       Wait (CancellationToken.None);
                }
-               
-               public bool Wait (TimeSpan ts)
+
+               public bool Wait (TimeSpan timeout)
                {
-                       CheckState();
-                       return Wait ((int)ts.TotalMilliseconds);
+                       return Wait ((int)timeout.TotalMilliseconds, CancellationToken.None);
                }
-               
+
                public bool Wait (int millisecondsTimeout)
+               {
+                       return Wait (millisecondsTimeout, CancellationToken.None);
+               }
+
+               public void Wait (CancellationToken cancellationToken)
+               {
+                       Wait (-1, cancellationToken);
+               }
+
+               public bool Wait (TimeSpan timeout, CancellationToken cancellationToken)
+               {
+                       CheckState();
+                       return Wait ((int)timeout.TotalMilliseconds, cancellationToken);
+               }
+
+               public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
                {
                        CheckState ();
                        if (millisecondsTimeout < -1)
                                throw new ArgumentOutOfRangeException ("millisecondsTimeout",
                                                                       "millisecondsTimeout is a negative number other than -1");
-                       if (millisecondsTimeout == -1) {
-                               Wait ();
-                               return true;
-                       }
-                       
+
+                       Watch sw = Watch.StartNew ();
+
+                       Func<bool> stopCondition = () => millisecondsTimeout >= 0 && sw.ElapsedMilliseconds > millisecondsTimeout;
+
                        do {
-                               int result = Interlocked.Decrement (ref currCount);
-                               if (result >= 0)
+                               bool shouldWait;
+                               int result;
+
+                               do {
+                                       cancellationToken.ThrowIfCancellationRequested ();
+                                       if (stopCondition ())
+                                               return false;
+
+                                       shouldWait = true;
+                                       result = currCount;
+
+                                       if (result > 0)
+                                               shouldWait = false;
+                                       else
+                                               break;
+                               } while (Interlocked.CompareExchange (ref currCount, result - 1, result) != result);
+
+                               if (!shouldWait) {
+                                       if (result == 1)
+                                               handle.Reset ();
                                        break;
-                               
-                               // We revert back the operation
-                               result = Interlocked.Increment (ref currCount);
-                               Watch sw = Watch.StartNew ();
+                               }
+
+                               SpinWait wait = new SpinWait ();
+
                                while (Thread.VolatileRead (ref currCount) <= 0) {
-                                       if (sw.ElapsedMilliseconds > millisecondsTimeout) {
-                                               sw.Stop ();
+                                       cancellationToken.ThrowIfCancellationRequested ();
+                                       if (stopCondition ())
                                                return false;
-                                       }
-                                       wait.SpinOnce ();
+
+                                       if (wait.Count > spinCount)
+                                               handle.WaitOne (Math.Min (Math.Max (millisecondsTimeout - (int)sw.ElapsedMilliseconds, 1), deepSleepTime));
+                                       else
+                                               wait.SpinOnce ();
                                }
                        } while (true);
-                       
+
                        return true;
                }
-               
+
                public WaitHandle AvailableWaitHandle {
                        get {
-                               return null;
+                               return handle;
                        }
                }
        }