-#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;
}
}
}