SemaphoreSlim.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // SemaphoreSlim.cs
  2. //
  3. // Copyright (c) 2008 Jérémie "Garuma" Laval
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. //
  24. using System;
  25. using System.Diagnostics;
  26. #if NET_4_0 || MOBILE
  27. namespace System.Threading
  28. {
  29. [System.Diagnostics.DebuggerDisplayAttribute ("Current Count = {currCount}")]
  30. public class SemaphoreSlim : IDisposable
  31. {
  32. const int spinCount = 10;
  33. const int deepSleepTime = 20;
  34. readonly int maxCount;
  35. int currCount;
  36. bool isDisposed;
  37. EventWaitHandle handle;
  38. public SemaphoreSlim (int initialCount) : this (initialCount, int.MaxValue)
  39. {
  40. }
  41. public SemaphoreSlim (int initialCount, int maxCount)
  42. {
  43. if (initialCount < 0 || initialCount > maxCount || maxCount < 0)
  44. throw new ArgumentOutOfRangeException ("The initialCount argument is negative, initialCount is greater than maxCount, or maxCount is not positive.");
  45. this.maxCount = maxCount;
  46. this.currCount = initialCount;
  47. this.handle = new ManualResetEvent (initialCount == 0);
  48. }
  49. ~SemaphoreSlim ()
  50. {
  51. Dispose(false);
  52. }
  53. public void Dispose ()
  54. {
  55. Dispose(true);
  56. }
  57. protected virtual void Dispose (bool disposing)
  58. {
  59. isDisposed = true;
  60. }
  61. void CheckState ()
  62. {
  63. if (isDisposed)
  64. throw new ObjectDisposedException ("The SemaphoreSlim has been disposed.");
  65. }
  66. public int CurrentCount {
  67. get {
  68. return currCount;
  69. }
  70. }
  71. public int Release ()
  72. {
  73. return Release(1);
  74. }
  75. public int Release (int releaseCount)
  76. {
  77. CheckState ();
  78. if (releaseCount < 1)
  79. throw new ArgumentOutOfRangeException ("releaseCount", "releaseCount is less than 1");
  80. // As we have to take care of the max limit we resort to CAS
  81. int oldValue, newValue;
  82. do {
  83. oldValue = currCount;
  84. newValue = (currCount + releaseCount);
  85. newValue = newValue > maxCount ? maxCount : newValue;
  86. } while (Interlocked.CompareExchange (ref currCount, newValue, oldValue) != oldValue);
  87. handle.Set ();
  88. return oldValue;
  89. }
  90. public void Wait ()
  91. {
  92. Wait (CancellationToken.None);
  93. }
  94. public bool Wait (TimeSpan timeout)
  95. {
  96. return Wait ((int)timeout.TotalMilliseconds, CancellationToken.None);
  97. }
  98. public bool Wait (int millisecondsTimeout)
  99. {
  100. return Wait (millisecondsTimeout, CancellationToken.None);
  101. }
  102. public void Wait (CancellationToken cancellationToken)
  103. {
  104. Wait (-1, cancellationToken);
  105. }
  106. public bool Wait (TimeSpan timeout, CancellationToken cancellationToken)
  107. {
  108. CheckState();
  109. return Wait ((int)timeout.TotalMilliseconds, cancellationToken);
  110. }
  111. public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
  112. {
  113. CheckState ();
  114. if (millisecondsTimeout < -1)
  115. throw new ArgumentOutOfRangeException ("millisecondsTimeout",
  116. "millisecondsTimeout is a negative number other than -1");
  117. Watch sw = Watch.StartNew ();
  118. Func<bool> stopCondition = () => millisecondsTimeout >= 0 && sw.ElapsedMilliseconds > millisecondsTimeout;
  119. do {
  120. bool shouldWait;
  121. int result;
  122. do {
  123. cancellationToken.ThrowIfCancellationRequested ();
  124. if (stopCondition ())
  125. return false;
  126. shouldWait = true;
  127. result = currCount;
  128. if (result > 0)
  129. shouldWait = false;
  130. else
  131. break;
  132. } while (Interlocked.CompareExchange (ref currCount, result - 1, result) != result);
  133. if (!shouldWait) {
  134. if (result == 1)
  135. handle.Reset ();
  136. break;
  137. }
  138. SpinWait wait = new SpinWait ();
  139. while (Thread.VolatileRead (ref currCount) <= 0) {
  140. cancellationToken.ThrowIfCancellationRequested ();
  141. if (stopCondition ())
  142. return false;
  143. if (wait.Count > spinCount)
  144. handle.WaitOne (Math.Min (Math.Max (millisecondsTimeout - (int)sw.ElapsedMilliseconds, 1), deepSleepTime));
  145. else
  146. wait.SpinOnce ();
  147. }
  148. } while (true);
  149. return true;
  150. }
  151. public WaitHandle AvailableWaitHandle {
  152. get {
  153. return handle;
  154. }
  155. }
  156. }
  157. }
  158. #endif