Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Threading / SemaphoreSlim.cs / 1305376 / SemaphoreSlim.cs
// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // SemaphoreSlim.cs // //[....] // // A lightweight semahore class that contains the basic semaphore functions plus some useful functions like interrupt // and wait handle exposing to allow waiting on multiple semaphores. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Collections.Generic; using System.Diagnostics; using System.Security.Permissions; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; // The class will be part of the current System.Threading namespace namespace System.Threading { ////// Limits the number of threads that can access a resource or pool of resources concurrently. /// ////// [ComVisible(false)] [HostProtection(Synchronization = true, ExternalThreading = true)] [DebuggerDisplay("Current Count = {m_currentCount}")] public class SemaphoreSlim : IDisposable { #region Private Fields // The semaphore count, initialized in the constructor to the initial value, every release call incremetns it // and every wait call decrements it as long as its value is positive otherwise the wait will block. // Its value must be between the maximum semaphore value and zero private volatile int m_currentCount; // The maximum semaphore value, it is initialized to Int.MaxValue if the client didn't specify it. it is used // to check if the count excceeded the maxi value or not. private readonly int m_maxCount; // The number of waiting threads, it is set to zero in the constructor and increments before blocking the // threading and decrements it back after that. It is used as flag for the release call to know if there are // waiting threads in the monitor or not. private volatile int m_waitCount; // Dummy object used to in lock statements to protect the semaphore count, wait handle and cancelation private object m_lockObj; // Act as the semaphore wait handle, it's lazily initialized if needed, the first WaitHandle call initialize it // and wait an release sets and resets it respectively as long as it is not null private ManualResetEvent m_waitHandle; // No maximum constant private const int NO_MAXIMUM = Int32.MaxValue; #endregion #region Public properties ////// The ///provides a lightweight semaphore class that doesn't /// use Windows kernel semaphores. /// /// All public and protected members of ///are thread-safe and may be used /// concurrently from multiple threads, with the exception of Dispose, which /// must only be used when all other operations on the have /// completed. /// /// Gets the current count of the ///. /// The current count of the public int CurrentCount { get { return m_currentCount; } } ///. /// Returns a ///that can be used to wait on the semaphore. /// A ///that can be used to wait on the /// semaphore. /// A successful wait on the ///does not imply a successful wait on /// the itself, nor does it decrement the semaphore's /// count. exists to allow a thread to block waiting on multiple /// semaphores, but such a wait should be followed by a true wait on the target semaphore. /// The public WaitHandle AvailableWaitHandle { get { CheckDispose(); // Return it directly if it is not null if (m_waitHandle != null) return m_waitHandle; //lock the count to avoid multiple threads initializing the handle if it is null lock (m_lockObj) { if (m_waitHandle == null) { // The initial state for the wait handle is true if the count is greater than zero // false otherwise m_waitHandle = new ManualResetEvent(m_currentCount != 0); } } return m_waitHandle; } } #endregion #region Constructors ///has been disposed. /// Initializes a new instance of the /// The initial number of requests for the semaphore that can be granted /// concurrently. ///class, specifying /// the initial number of requests that can be granted concurrently. /// public SemaphoreSlim(int initialCount) : this(initialCount, NO_MAXIMUM) { } /// /// is less than 0. /// Initializes a new instance of the /// The initial number of requests for the semaphore that can be granted /// concurrently. /// The maximum number of requests for the semaphore that can be granted /// concurrently. ///class, specifying /// the initial and maximum number of requests that can be granted concurrently. /// public SemaphoreSlim(int initialCount, int maxCount) { if (initialCount < 0 || initialCount > maxCount) { throw new ArgumentOutOfRangeException( "initialCount", initialCount, GetResourceString("SemaphoreSlim_ctor_InitialCountWrong")); } //validate input if (maxCount <= 0) { throw new ArgumentOutOfRangeException("maxCount", maxCount, GetResourceString("SemaphoreSlim_ctor_MaxCountWrong")); } m_maxCount = maxCount; m_lockObj = new object(); m_currentCount = initialCount; } #endregion #region Methods /// /// is less than 0. -or- /// is greater than . -or- /// is less than 0. /// Blocks the current thread until it can enter the ///. /// The current instance has already been /// disposed. public void Wait() { // Call wait with infinite timeout Wait(Timeout.Infinite, new CancellationToken()); } ////// Blocks the current thread until it can enter the /// The, while observing a /// . /// token to /// observe. /// /// was /// canceled. The current instance has already been /// disposed. public void Wait(CancellationToken cancellationToken) { // Call wait with infinite timeout Wait(Timeout.Infinite, cancellationToken); } ////// Blocks the current thread until it can enter the /// A, using a to measure the time interval. /// that represents the number of milliseconds /// to wait, or a that represents -1 milliseconds to wait indefinitely. /// /// true if the current thread successfully entered the ///; /// otherwise, false. public bool Wait(TimeSpan timeout) { // Validate the timeout Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } // Call wait with the timeout milliseconds return Wait((int)timeout.TotalMilliseconds, new CancellationToken()); } /// is a negative /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater /// than . /// Blocks the current thread until it can enter the /// A, using a to measure the time interval, while observing a . /// that represents the number of milliseconds /// to wait, or a that represents -1 milliseconds to wait indefinitely. /// /// The to /// observe. /// true if the current thread successfully entered the ///; /// otherwise, false. /// is a negative /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater /// than . public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) { // Validate the timeout Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } // Call wait with the timeout milliseconds return Wait((int)timeout.TotalMilliseconds, cancellationToken); } /// was canceled. /// Blocks the current thread until it can enter the /// The number of milliseconds to wait, or, using a 32-bit /// signed integer to measure the time interval. /// (-1) to wait indefinitely. /// true if the current thread successfully entered the ///; /// otherwise, false. public bool Wait(int millisecondsTimeout) { return Wait(millisecondsTimeout, new CancellationToken()); } /// is a /// negative number other than -1, which represents an infinite time-out. /// Blocks the current thread until it can enter the /// The number of milliseconds to wait, or, /// using a 32-bit signed integer to measure the time interval, /// while observing a . /// (-1) to /// wait indefinitely. /// The to observe. /// true if the current thread successfully entered the ///; otherwise, false. /// is a negative number other than -1, /// which represents an infinite time-out. public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { CheckDispose(); // Validate input if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } cancellationToken.ThrowIfCancellationRequested(); long startTimeTicks = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0) { startTimeTicks = DateTime.UtcNow.Ticks; } bool lockTaken = false; //Register for cancellation outside of the main lock. //NOTE: Register/deregister inside the lock can deadlock as different lock acquisition orders could // occur for (1)this.m_lockObj and (2)cts.internalLock CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(s_cancellationTokenCanceledEventHandler, this); try { // Perf: first spin wait for the count to be positive, but only up to the first planned yield. // This additional amount of spinwaiting in addition // to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios. // SpinWait spin = new SpinWait(); while (m_currentCount == 0 && !spin.NextSpinWillYield) { spin.SpinOnce(); } // entering the lock and incrementing waiters must not suffer a thread-abort, else we cannot // clean up m_waitCount correctly, which may lead to deadlock due to non-woken waiters. try { } finally { Monitor.Enter(m_lockObj, ref lockTaken); if (lockTaken) { m_waitCount++; } } // If the count > 0 we are good to move on. // If not, then wait if we were given allowed some wait duration if (m_currentCount == 0) { if (millisecondsTimeout == 0) { return false; } // Prepare for the main wait... // wait until the count become greater than zero or the timeout is expired if (!WaitUntilCountOrTimeout(millisecondsTimeout, startTimeTicks, cancellationToken)) { return false; } } // At this point the count should be greater than zero Contract.Assert(m_currentCount > 0); m_currentCount--; // Exposing wait handle which is lazily initialized if needed if (m_waitHandle != null && m_currentCount == 0) { m_waitHandle.Reset(); } } finally { // Release the lock if (lockTaken) { m_waitCount--; Monitor.Exit(m_lockObj); } // Unregister the cancellation callback. cancellationTokenRegistration.Dispose(); } return true; } /// was canceled. /// Local helper function, waits on the monitor until the monitor recieves signal or the /// timeout is expired /// /// The maximum timeout /// The start ticks to calculate the elapsed time /// The CancellationToken to observe. ///true if the monitor recieved a signal, false if the timeout expired private bool WaitUntilCountOrTimeout(int millisecondsTimeout, long startTimeTicks, CancellationToken cancellationToken) { int remainingWaitMilliseconds = Timeout.Infinite; //Wait on the monitor as long as the count is zero while (m_currentCount == 0) { // If cancelled, we throw. Trying to wait could lead to deadlock. cancellationToken.ThrowIfCancellationRequested(); if (millisecondsTimeout != Timeout.Infinite) { remainingWaitMilliseconds = UpdateTimeOut(startTimeTicks, millisecondsTimeout); if (remainingWaitMilliseconds <= 0) { // The thread has expires its timeout return false; } } // ** the actual wait ** if (!Monitor.Wait(m_lockObj, remainingWaitMilliseconds)) { return false; } } return true; } ////// Exits the ///once. /// The previous count of the ///. The current instance has already been /// disposed. public int Release() { return Release(1); } ////// Exits the /// The number of times to exit the semaphore. ///a specified number of times. /// The previous count of the ///. /// is less /// than 1. The ///has /// already reached its maximum size. The current instance has already been /// disposed. public int Release(int releaseCount) { CheckDispose(); // Validate input if (releaseCount < 1) { throw new ArgumentOutOfRangeException( "releaseCount", releaseCount, GetResourceString("SemaphoreSlim_Release_CountWrong")); } lock (m_lockObj) { // If thre release count would result exceeding the maximum count throw SemaphoreFullException if (m_maxCount - m_currentCount < releaseCount) { throw new SemaphoreFullException(); } // Increment the count by the actual release count m_currentCount += releaseCount; if (m_currentCount == 1 || m_waitCount == 1) { Monitor.Pulse(m_lockObj); } else if (m_waitCount > 1) { Monitor.PulseAll(m_lockObj); } // Exposing wait handle if it is not null if (m_waitHandle != null && m_currentCount - releaseCount == 0) { m_waitHandle.Set(); } return m_currentCount - releaseCount; } } ////// Releases all resources used by the current instance of ///. /// /// Unlike most of the members of public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ///, is not /// thread-safe and may not be used concurrently with other members of this instance. /// /// When overridden in a derived class, releases the unmanaged resources used by the /// /// true to release both managed and unmanaged resources; /// false to release only unmanaged resources. ///, and optionally releases the managed resources. /// /// Unlike most of the members of protected virtual void Dispose(bool disposing) { if (disposing) { if (m_waitHandle != null) { m_waitHandle.Close(); m_waitHandle = null; } m_lockObj = null; } } ///, is not /// thread-safe and may not be used concurrently with other members of this instance. /// /// Helper function to measure and update the wait time /// /// The first time (in Ticks) observed when the wait started /// The orginal wait timeoutout in milliseconds ///The new wait time in milliseconds, -1 if the time expired private static int UpdateTimeOut(long startTimeTicks, int originalWaitMillisecondsTimeout) { // The function must be called in case the time out is not infinite Contract.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite); long elapsedMilliseconds = (DateTime.UtcNow.Ticks - startTimeTicks) / TimeSpan.TicksPerMillisecond; // Check the elapsed milliseconds is greater than max int because this property is long if (elapsedMilliseconds > int.MaxValue) { return 0; } // Subtract the elapsed time from the current wait time int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ; if (currentWaitTimeout <= 0) { return 0; } return currentWaitTimeout; } ////// Private helper method to wake up waiters when a cancellationToken gets canceled. /// private static Action
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ClientCultureInfo.cs
- ConnectionString.cs
- SafeCryptoHandles.cs
- SerialErrors.cs
- XmlImplementation.cs
- NavigationEventArgs.cs
- CodeDefaultValueExpression.cs
- Version.cs
- DataSourceGeneratorException.cs
- ToolboxItemSnapLineBehavior.cs
- ObfuscationAttribute.cs
- SemanticTag.cs
- ExtendedPropertyDescriptor.cs
- ControlPersister.cs
- AutomationAttributeInfo.cs
- StructuredTypeInfo.cs
- LinqDataSourceDeleteEventArgs.cs
- CompilationPass2TaskInternal.cs
- NativeMethods.cs
- ProgressBarHighlightConverter.cs
- BufferModesCollection.cs
- ValidationRuleCollection.cs
- CompilerWrapper.cs
- StorageConditionPropertyMapping.cs
- HandlerBase.cs
- CorePropertiesFilter.cs
- CompressStream.cs
- BrowserCapabilitiesCodeGenerator.cs
- SqlCharStream.cs
- ImageSource.cs
- ResourcePool.cs
- ProviderConnectionPointCollection.cs
- OwnerDrawPropertyBag.cs
- WebServiceFault.cs
- CodeLabeledStatement.cs
- TargetConverter.cs
- BrowserCapabilitiesCodeGenerator.cs
- SqlCharStream.cs
- RadioButtonFlatAdapter.cs
- XmlDataSourceDesigner.cs
- WindowsListViewSubItem.cs
- XsltSettings.cs
- SafePipeHandle.cs
- Section.cs
- DataGridCaption.cs
- PathFigure.cs
- XmlSchema.cs
- SqlFacetAttribute.cs
- SoapParser.cs
- MbpInfo.cs
- DynamicRenderer.cs
- ObjectListCommandsPage.cs
- ACL.cs
- AxisAngleRotation3D.cs
- WebControlAdapter.cs
- FileChangeNotifier.cs
- baseaxisquery.cs
- TypeSystem.cs
- ContainerControl.cs
- XmlNullResolver.cs
- PageParser.cs
- SplitterPanelDesigner.cs
- WorkflowView.cs
- EntityDataReader.cs
- CacheManager.cs
- StateChangeEvent.cs
- TraceFilter.cs
- DragEvent.cs
- NamedObject.cs
- PropertyDescriptorCollection.cs
- MessagePropertyAttribute.cs
- InkCanvasFeedbackAdorner.cs
- IImplicitResourceProvider.cs
- LabelEditEvent.cs
- TypeInitializationException.cs
- ListItemCollection.cs
- EntityDataSourceQueryBuilder.cs
- SortFieldComparer.cs
- HtmlInputReset.cs
- TailCallAnalyzer.cs
- EventManager.cs
- versioninfo.cs
- DefaultAsyncDataDispatcher.cs
- ClassHandlersStore.cs
- CapabilitiesUse.cs
- TextDecoration.cs
- Span.cs
- TemplateField.cs
- Parser.cs
- TogglePatternIdentifiers.cs
- ImageKeyConverter.cs
- entityreference_tresulttype.cs
- HandlerMappingMemo.cs
- CommandField.cs
- DbParameterCollection.cs
- XmlWrappingReader.cs
- StringAnimationUsingKeyFrames.cs
- CodePrimitiveExpression.cs
- Deflater.cs
- Renderer.cs