mscorlib(4.0.0.0) API with additions
SemaphoreSlim.cs
1 using System.Diagnostics;
3 using System.Security;
6 
7 namespace System.Threading
8 {
10  [ComVisible(false)]
11  [DebuggerDisplay("Current Count = {m_currentCount}")]
12  [__DynamicallyInvokable]
13  [HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
14  public class SemaphoreSlim : IDisposable
15  {
16  private sealed class TaskNode : Task<bool>, IThreadPoolWorkItem
17  {
18  internal TaskNode Prev;
19 
20  internal TaskNode Next;
21 
22  internal TaskNode()
23  {
24  }
25 
26  [SecurityCritical]
27  void IThreadPoolWorkItem.ExecuteWorkItem()
28  {
29  bool flag = TrySetResult(result: true);
30  }
31 
32  [SecurityCritical]
33  void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
34  {
35  }
36  }
37 
38  private volatile int m_currentCount;
39 
40  private readonly int m_maxCount;
41 
42  private volatile int m_waitCount;
43 
44  private object m_lockObj;
45 
46  private volatile ManualResetEvent m_waitHandle;
47 
48  private TaskNode m_asyncHead;
49 
50  private TaskNode m_asyncTail;
51 
52  private static readonly Task<bool> s_trueTask = new Task<bool>(canceled: false, result: true, (TaskCreationOptions)16384, default(CancellationToken));
53 
54  private const int NO_MAXIMUM = int.MaxValue;
55 
56  private static Action<object> s_cancellationTokenCanceledEventHandler = CancellationTokenCanceledEventHandler;
57 
60  [__DynamicallyInvokable]
61  public int CurrentCount
62  {
63  [__DynamicallyInvokable]
64  get
65  {
66  return m_currentCount;
67  }
68  }
69 
73  [__DynamicallyInvokable]
75  {
76  [__DynamicallyInvokable]
77  get
78  {
79  CheckDispose();
80  if (m_waitHandle != null)
81  {
82  return m_waitHandle;
83  }
84  lock (m_lockObj)
85  {
86  if (m_waitHandle == null)
87  {
88  m_waitHandle = new ManualResetEvent(m_currentCount != 0);
89  }
90  }
91  return m_waitHandle;
92  }
93  }
94 
99  [__DynamicallyInvokable]
100  public SemaphoreSlim(int initialCount)
101  : this(initialCount, int.MaxValue)
102  {
103  }
104 
110  [__DynamicallyInvokable]
111  public SemaphoreSlim(int initialCount, int maxCount)
112  {
113  if (initialCount < 0 || initialCount > maxCount)
114  {
115  throw new ArgumentOutOfRangeException("initialCount", initialCount, GetResourceString("SemaphoreSlim_ctor_InitialCountWrong"));
116  }
117  if (maxCount <= 0)
118  {
119  throw new ArgumentOutOfRangeException("maxCount", maxCount, GetResourceString("SemaphoreSlim_ctor_MaxCountWrong"));
120  }
121  m_maxCount = maxCount;
122  m_lockObj = new object();
123  m_currentCount = initialCount;
124  }
125 
128  [__DynamicallyInvokable]
129  public void Wait()
130  {
131  Wait(-1, default(CancellationToken));
132  }
133 
139  [__DynamicallyInvokable]
140  public void Wait(CancellationToken cancellationToken)
141  {
142  Wait(-1, cancellationToken);
143  }
144 
152  [__DynamicallyInvokable]
153  public bool Wait(TimeSpan timeout)
154  {
155  long num = (long)timeout.TotalMilliseconds;
156  if (num < -1 || num > int.MaxValue)
157  {
158  throw new ArgumentOutOfRangeException("timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
159  }
160  return Wait((int)timeout.TotalMilliseconds, default(CancellationToken));
161  }
162 
175  [__DynamicallyInvokable]
176  public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
177  {
178  long num = (long)timeout.TotalMilliseconds;
179  if (num < -1 || num > int.MaxValue)
180  {
181  throw new ArgumentOutOfRangeException("timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
182  }
183  return Wait((int)timeout.TotalMilliseconds, cancellationToken);
184  }
185 
192  [__DynamicallyInvokable]
193  public bool Wait(int millisecondsTimeout)
194  {
195  return Wait(millisecondsTimeout, default(CancellationToken));
196  }
197 
208  [__DynamicallyInvokable]
209  public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
210  {
211  CheckDispose();
212  if (millisecondsTimeout < -1)
213  {
214  throw new ArgumentOutOfRangeException("totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
215  }
216  cancellationToken.ThrowIfCancellationRequested();
217  uint startTime = 0u;
218  if (millisecondsTimeout != -1 && millisecondsTimeout > 0)
219  {
220  startTime = TimeoutHelper.GetTime();
221  }
222  bool flag = false;
223  Task<bool> task = null;
224  bool lockTaken = false;
225  CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this);
226  try
227  {
228  SpinWait spinWait = default(SpinWait);
229  while (m_currentCount == 0 && !spinWait.NextSpinWillYield)
230  {
231  spinWait.SpinOnce();
232  }
233  try
234  {
235  }
236  finally
237  {
238  Monitor.Enter(m_lockObj, ref lockTaken);
239  if (lockTaken)
240  {
241  m_waitCount++;
242  }
243  }
244  if (m_asyncHead != null)
245  {
246  task = WaitAsync(millisecondsTimeout, cancellationToken);
247  }
248  else
249  {
250  OperationCanceledException ex = null;
251  if (m_currentCount == 0)
252  {
253  if (millisecondsTimeout == 0)
254  {
255  return false;
256  }
257  try
258  {
259  flag = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken);
260  }
261  catch (OperationCanceledException ex2)
262  {
263  ex = ex2;
264  }
265  }
266  if (m_currentCount > 0)
267  {
268  flag = true;
269  m_currentCount--;
270  }
271  else if (ex != null)
272  {
273  throw ex;
274  }
275  if (m_waitHandle != null && m_currentCount == 0)
276  {
277  m_waitHandle.Reset();
278  }
279  }
280  }
281  finally
282  {
283  if (lockTaken)
284  {
285  m_waitCount--;
286  Monitor.Exit(m_lockObj);
287  }
288  cancellationTokenRegistration.Dispose();
289  }
290  return task?.GetAwaiter().GetResult() ?? flag;
291  }
292 
293  private bool WaitUntilCountOrTimeout(int millisecondsTimeout, uint startTime, CancellationToken cancellationToken)
294  {
295  int num = -1;
296  while (m_currentCount == 0)
297  {
298  cancellationToken.ThrowIfCancellationRequested();
299  if (millisecondsTimeout != -1)
300  {
301  num = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout);
302  if (num <= 0)
303  {
304  return false;
305  }
306  }
307  if (!Monitor.Wait(m_lockObj, num))
308  {
309  return false;
310  }
311  }
312  return true;
313  }
314 
317  [__DynamicallyInvokable]
318  public Task WaitAsync()
319  {
320  return WaitAsync(-1, default(CancellationToken));
321  }
322 
329  [__DynamicallyInvokable]
330  public Task WaitAsync(CancellationToken cancellationToken)
331  {
332  return WaitAsync(-1, cancellationToken);
333  }
334 
341  [__DynamicallyInvokable]
342  public Task<bool> WaitAsync(int millisecondsTimeout)
343  {
344  return WaitAsync(millisecondsTimeout, default(CancellationToken));
345  }
346 
353  [__DynamicallyInvokable]
354  public Task<bool> WaitAsync(TimeSpan timeout)
355  {
356  return WaitAsync(timeout, default(CancellationToken));
357  }
358 
367  [__DynamicallyInvokable]
368  public Task<bool> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
369  {
370  long num = (long)timeout.TotalMilliseconds;
371  if (num < -1 || num > int.MaxValue)
372  {
373  throw new ArgumentOutOfRangeException("timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
374  }
375  return WaitAsync((int)timeout.TotalMilliseconds, cancellationToken);
376  }
377 
387  [__DynamicallyInvokable]
388  public Task<bool> WaitAsync(int millisecondsTimeout, CancellationToken cancellationToken)
389  {
390  CheckDispose();
391  if (millisecondsTimeout < -1)
392  {
393  throw new ArgumentOutOfRangeException("totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
394  }
395  if (cancellationToken.IsCancellationRequested)
396  {
397  return Task.FromCancellation<bool>(cancellationToken);
398  }
399  lock (m_lockObj)
400  {
401  if (m_currentCount > 0)
402  {
403  m_currentCount--;
404  if (m_waitHandle != null && m_currentCount == 0)
405  {
406  m_waitHandle.Reset();
407  }
408  return s_trueTask;
409  }
410  TaskNode taskNode = CreateAndAddAsyncWaiter();
411  return (millisecondsTimeout == -1 && !cancellationToken.CanBeCanceled) ? taskNode : WaitUntilCountOrTimeoutAsync(taskNode, millisecondsTimeout, cancellationToken);
412  }
413  }
414 
415  private TaskNode CreateAndAddAsyncWaiter()
416  {
417  TaskNode taskNode = new TaskNode();
418  if (m_asyncHead == null)
419  {
420  m_asyncHead = taskNode;
421  m_asyncTail = taskNode;
422  }
423  else
424  {
425  m_asyncTail.Next = taskNode;
426  taskNode.Prev = m_asyncTail;
427  m_asyncTail = taskNode;
428  }
429  return taskNode;
430  }
431 
432  private bool RemoveAsyncWaiter(TaskNode task)
433  {
434  bool result = m_asyncHead == task || task.Prev != null;
435  if (task.Next != null)
436  {
437  task.Next.Prev = task.Prev;
438  }
439  if (task.Prev != null)
440  {
441  task.Prev.Next = task.Next;
442  }
443  if (m_asyncHead == task)
444  {
445  m_asyncHead = task.Next;
446  }
447  if (m_asyncTail == task)
448  {
449  m_asyncTail = task.Prev;
450  }
451  task.Next = (task.Prev = null);
452  return result;
453  }
454 
455  private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int millisecondsTimeout, CancellationToken cancellationToken)
456  {
457  using (CancellationTokenSource cts = cancellationToken.CanBeCanceled ? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default(CancellationToken)) : new CancellationTokenSource())
458  {
459  Task<Task> task = Task.WhenAny(asyncWaiter, Task.Delay(millisecondsTimeout, cts.Token));
460  if (asyncWaiter == await task.ConfigureAwait(continueOnCapturedContext: false))
461  {
462  cts.Cancel();
463  return true;
464  }
465  }
466  lock (m_lockObj)
467  {
468  if (RemoveAsyncWaiter(asyncWaiter))
469  {
470  cancellationToken.ThrowIfCancellationRequested();
471  return false;
472  }
473  }
474  return await asyncWaiter.ConfigureAwait(continueOnCapturedContext: false);
475  }
476 
481  [__DynamicallyInvokable]
482  public int Release()
483  {
484  return Release(1);
485  }
486 
494  [__DynamicallyInvokable]
495  public int Release(int releaseCount)
496  {
497  CheckDispose();
498  if (releaseCount < 1)
499  {
500  throw new ArgumentOutOfRangeException("releaseCount", releaseCount, GetResourceString("SemaphoreSlim_Release_CountWrong"));
501  }
502  lock (m_lockObj)
503  {
504  int currentCount = m_currentCount;
505  int num = currentCount;
506  if (m_maxCount - currentCount < releaseCount)
507  {
508  throw new SemaphoreFullException();
509  }
510  currentCount += releaseCount;
511  int waitCount = m_waitCount;
512  if (currentCount == 1 || waitCount == 1)
513  {
514  Monitor.Pulse(m_lockObj);
515  }
516  else if (waitCount > 1)
517  {
518  Monitor.PulseAll(m_lockObj);
519  }
520  if (m_asyncHead != null)
521  {
522  int num2 = currentCount - waitCount;
523  while (num2 > 0 && m_asyncHead != null)
524  {
525  currentCount--;
526  num2--;
527  TaskNode asyncHead = m_asyncHead;
528  RemoveAsyncWaiter(asyncHead);
529  QueueWaiterTask(asyncHead);
530  }
531  }
532  m_currentCount = currentCount;
533  if (m_waitHandle == null)
534  {
535  return num;
536  }
537  if (num != 0)
538  {
539  return num;
540  }
541  if (currentCount <= 0)
542  {
543  return num;
544  }
545  m_waitHandle.Set();
546  return num;
547  }
548  }
549 
550  [SecuritySafeCritical]
551  private static void QueueWaiterTask(TaskNode waiterTask)
552  {
553  ThreadPool.UnsafeQueueCustomWorkItem(waiterTask, forceGlobal: false);
554  }
555 
557  [__DynamicallyInvokable]
558  public void Dispose()
559  {
560  Dispose(disposing: true);
561  GC.SuppressFinalize(this);
562  }
563 
567  [__DynamicallyInvokable]
568  protected virtual void Dispose(bool disposing)
569  {
570  if (disposing)
571  {
572  if (m_waitHandle != null)
573  {
574  m_waitHandle.Close();
575  m_waitHandle = null;
576  }
577  m_lockObj = null;
578  m_asyncHead = null;
579  m_asyncTail = null;
580  }
581  }
582 
583  private static void CancellationTokenCanceledEventHandler(object obj)
584  {
585  SemaphoreSlim semaphoreSlim = obj as SemaphoreSlim;
586  lock (semaphoreSlim.m_lockObj)
587  {
588  Monitor.PulseAll(semaphoreSlim.m_lockObj);
589  }
590  }
591 
592  private void CheckDispose()
593  {
594  if (m_lockObj == null)
595  {
596  throw new ObjectDisposedException(null, GetResourceString("SemaphoreSlim_Disposed"));
597  }
598  }
599 
600  private static string GetResourceString(string str)
601  {
602  return Environment.GetResourceString(str);
603  }
604  }
605 }
static Task< Task > WhenAny(params Task[] tasks)
Creates a task that will complete when any of the supplied tasks have completed.
Definition: Task.cs:4588
Represents a lightweight alternative to T:System.Threading.Semaphore that limits the number of thread...
bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
Blocks the current thread until it can enter the T:System.Threading.SemaphoreSlim,...
Propagates notification that operations should be canceled.
new ConfiguredTaskAwaitable< TResult > ConfigureAwait(bool continueOnCapturedContext)
Configures an awaiter used to await this T:System.Threading.Tasks.Task`1.
Definition: Task.cs:393
Encapsulates operating system–specific objects that wait for exclusive access to shared resources.
Definition: WaitHandle.cs:15
void ThrowIfCancellationRequested()
Throws a T:System.OperationCanceledException if this token has had cancellation requested.
void Wait(CancellationToken cancellationToken)
Blocks the current thread until it can enter the T:System.Threading.SemaphoreSlim,...
static void PulseAll(object obj)
Notifies all waiting threads of a change in the object's state.
Definition: Monitor.cs:299
static void SuppressFinalize(object obj)
Requests that the common language runtime not call the finalizer for the specified object.
Definition: GC.cs:308
Task< bool > WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
Asynchronously waits to enter the T:System.Threading.SemaphoreSlim, using a T:System....
Provides support for spin-based waiting.
Definition: SpinWait.cs:8
bool Set()
Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
Task< bool > WaitAsync(TimeSpan timeout)
Asynchronously waits to enter the T:System.Threading.SemaphoreSlim, using a T:System....
Provides a mechanism for releasing unmanaged resources.To browse the .NET Framework source code for t...
Definition: IDisposable.cs:8
Definition: __Canon.cs:3
int Release()
Releases the T:System.Threading.SemaphoreSlim object once.
The exception that is thrown when the value of an argument is outside the allowable range of values a...
Provides a mechanism that synchronizes access to objects.
Definition: Monitor.cs:13
Represents a callback delegate that has been registered with a T:System.Threading....
double TotalMilliseconds
Gets the value of the current T:System.TimeSpan structure expressed in whole and fractional milliseco...
Definition: TimeSpan.cs:180
bool CanBeCanceled
Gets whether this token is capable of being in the canceled state.
SemaphoreSlim(int initialCount)
Initializes a new instance of the T:System.Threading.SemaphoreSlim class, specifying the initial numb...
void Dispose()
Releases all resources used by the current instance of the T:System.Threading.SemaphoreSlim class.
static Task Delay(TimeSpan delay)
Creates a task that completes after a specified time interval.
Definition: Task.cs:4339
bool IsCancellationRequested
Gets whether cancellation has been requested for this token.
int CurrentCount
Gets the number of remaining threads that can enter the T:System.Threading.SemaphoreSlim object.
bool NextSpinWillYield
Gets whether the next call to M:System.Threading.SpinWait.SpinOnce will yield the processor,...
Definition: SpinWait.cs:34
Task WaitAsync(CancellationToken cancellationToken)
Asynchronously waits to enter the T:System.Threading.SemaphoreSlim, while observing a T:System....
TaskCreationOptions
Specifies flags that control optional behavior for the creation and execution of tasks.
bool Wait(TimeSpan timeout)
Blocks the current thread until it can enter the T:System.Threading.SemaphoreSlim,...
WaitHandle AvailableWaitHandle
Returns a T:System.Threading.WaitHandle that can be used to wait on the semaphore.
int Release(int releaseCount)
Releases the T:System.Threading.SemaphoreSlim object a specified number of times.
SecurityAction
Specifies the security actions that can be performed using declarative security.
static void Enter(object obj)
Acquires an exclusive lock on the specified object.
virtual void Dispose(bool disposing)
Releases the unmanaged resources used by the T:System.Threading.SemaphoreSlim, and optionally release...
Task WaitAsync()
Asynchronously waits to enter the T:System.Threading.SemaphoreSlim.
Task< bool > WaitAsync(int millisecondsTimeout, CancellationToken cancellationToken)
Asynchronously waits to enter the T:System.Threading.SemaphoreSlim, using a 32-bit signed integer to ...
Notifies one or more waiting threads that an event has occurred. This class cannot be inherited.
void Wait()
Blocks the current thread until it can enter the T:System.Threading.SemaphoreSlim.
static void Exit(object obj)
Releases an exclusive lock on the specified object.
SemaphoreSlim(int initialCount, int maxCount)
Initializes a new instance of the T:System.Threading.SemaphoreSlim class, specifying the initial and ...
bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
Blocks the current thread until it can enter the T:System.Threading.SemaphoreSlim,...
The exception that is thrown when the Overload:System.Threading.Semaphore.Release method is called on...
virtual void Close()
Releases all resources held by the current T:System.Threading.WaitHandle.
Definition: WaitHandle.cs:714
Controls the system garbage collector, a service that automatically reclaims unused memory.
Definition: GC.cs:11
The exception that is thrown in a thread upon cancellation of an operation that the thread was execut...
void SpinOnce()
Performs a single spin.
Definition: SpinWait.cs:48
static void Pulse(object obj)
Notifies a thread in the waiting queue of a change in the locked object's state.
Definition: Monitor.cs:280
Represents a time interval.To browse the .NET Framework source code for this type,...
Definition: TimeSpan.cs:12
new TaskAwaiter< TResult > GetAwaiter()
Gets an awaiter used to await this T:System.Threading.Tasks.Task`1.
Definition: Task.cs:384
The exception that is thrown when a call is made to the M:System.Threading.Thread....
Task< bool > WaitAsync(int millisecondsTimeout)
Asynchronously waits to enter the T:System.Threading.SemaphoreSlim, using a 32-bit signed integer to ...
bool Wait(int millisecondsTimeout)
Blocks the current thread until it can enter the T:System.Threading.SemaphoreSlim,...
void Dispose()
Releases all resources used by the current instance of the T:System.Threading.CancellationTokenRegist...
bool Reset()
Sets the state of the event to nonsignaled, causing threads to block.
Provides a pool of threads that can be used to execute tasks, post work items, process asynchronous I...
Definition: ThreadPool.cs:14
Represents an asynchronous operation that can return a value.
Definition: Task.cs:18