Code:
/ 4.0 / 4.0 / 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
- CalloutQueueItem.cs
- DataBindingCollectionConverter.cs
- BindingMAnagerBase.cs
- SafeCryptoHandles.cs
- XPathPatternParser.cs
- SiteMapSection.cs
- serverconfig.cs
- SessionIDManager.cs
- CompilerTypeWithParams.cs
- SmiContextFactory.cs
- EventSetter.cs
- ZoneMembershipCondition.cs
- StaticTextPointer.cs
- KeyedHashAlgorithm.cs
- TextBox.cs
- StrongNameMembershipCondition.cs
- FeatureSupport.cs
- EntityContainerEntitySet.cs
- ItemContainerGenerator.cs
- EntityStoreSchemaFilterEntry.cs
- RootBrowserWindowAutomationPeer.cs
- TypedCompletedAsyncResult.cs
- WebPartDescriptionCollection.cs
- ToolStripSplitButton.cs
- RestHandlerFactory.cs
- SingleAnimation.cs
- Brushes.cs
- ReturnEventArgs.cs
- EndOfStreamException.cs
- CroppedBitmap.cs
- NumberAction.cs
- EpmTargetTree.cs
- HttpWebRequest.cs
- HttpRequest.cs
- PageCanvasSize.cs
- ScrollChrome.cs
- ImpersonationContext.cs
- ForwardPositionQuery.cs
- LicenseException.cs
- InvalidDataContractException.cs
- AspNetHostingPermission.cs
- AttributeCollection.cs
- ManagementDateTime.cs
- TextRangeEdit.cs
- Light.cs
- RSATokenProvider.cs
- Button.cs
- WebPartVerbCollection.cs
- ColorConverter.cs
- OrderPreservingPipeliningMergeHelper.cs
- ReferenceEqualityComparer.cs
- ManagementEventArgs.cs
- ExternalFile.cs
- XpsResourceDictionary.cs
- BulletedListDesigner.cs
- RawStylusInputReport.cs
- InvalidEnumArgumentException.cs
- Resources.Designer.cs
- ObjectAnimationBase.cs
- DataControlField.cs
- Baml2006ReaderSettings.cs
- ErrorWrapper.cs
- SqlCommandAsyncResult.cs
- XmlQueryCardinality.cs
- ObjectDataSourceFilteringEventArgs.cs
- UnionCqlBlock.cs
- SessionStateContainer.cs
- ClaimTypes.cs
- BrowserTree.cs
- LineInfo.cs
- DataGridHeaderBorder.cs
- BounceEase.cs
- ChannelPoolSettingsElement.cs
- HandlerFactoryWrapper.cs
- DataGridViewColumnDesignTimeVisibleAttribute.cs
- ProcessThread.cs
- FileVersionInfo.cs
- TextSimpleMarkerProperties.cs
- PhysicalFontFamily.cs
- SqlVersion.cs
- DataSpaceManager.cs
- PowerModeChangedEventArgs.cs
- Point.cs
- TreeNodeStyleCollection.cs
- TextEffectResolver.cs
- DragCompletedEventArgs.cs
- IdnElement.cs
- _ChunkParse.cs
- TextContainerHelper.cs
- COAUTHINFO.cs
- XmlIlGenerator.cs
- ConfigurationManager.cs
- NetworkInformationException.cs
- TextBoxAutoCompleteSourceConverter.cs
- ActivationServices.cs
- LinqDataSourceInsertEventArgs.cs
- RelativeSource.cs
- ToolboxItemAttribute.cs
- LogReserveAndAppendState.cs
- EDesignUtil.cs