Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Threading / ManualResetEventSlim.cs / 1305376 / ManualResetEventSlim.cs
#pragma warning disable 0420 // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // SlimManualResetEvent.cs // //[....] // // An manual-reset event that mixes a little spinning with a true Win32 event. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Diagnostics; using System.Security.Permissions; using System.Threading; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace System.Threading { // ManualResetEventSlim wraps a manual-reset event internally with a little bit of // spinning. When an event will be set imminently, it is often advantageous to avoid // a 4k+ cycle context switch in favor of briefly spinning. Therefore we layer on to // a brief amount of spinning that should, on the average, make using the slim event // cheaper than using Win32 events directly. This can be reset manually, much like // a Win32 manual-reset would be. // // Notes: // We lazily allocate the Win32 event internally. Therefore, the caller should // always call Dispose to clean it up, just in case. This API is a no-op of the // event wasn't allocated, but if it was, ensures that the event goes away // eagerly, instead of waiting for finalization. ////// Provides a slimmed down version of ///. /// /// All public and protected members of [ComVisible(false)] [DebuggerDisplay("Set = {IsSet}")] [HostProtection(Synchronization = true, ExternalThreading = true)] public class ManualResetEventSlim : IDisposable { // These are the default spin counts we use on single-proc and MP machines. private const int DEFAULT_SPIN_SP = 1; private const int DEFAULT_SPIN_MP = SpinWait.YIELD_THRESHOLD; private object m_lock; // A lock used for waiting and pulsing. Lazily initialized via EnsureLockObjectCreated() // This would normally be tagged 'volatile', but we can avoid this as it is only used nearby // access to Waiters which internally uses the volatile m_combinedState. See specific // comments in Set(bool duringCancellation) private ManualResetEvent m_eventObj; // A true Win32 event used for waiting. // -- State -- // //For a packed word a uint would seem better, but Interlocked.* doesn't support them as uint isn't CLS-compliant. private volatile int m_combinedState; //ie a UInt32. Used for the state items listed below. //1-bit for signalled state private const int SignalledState_BitMask = unchecked((int)0x80000000);//1000 0000 0000 0000 0000 0000 0000 0000 private const int SignalledState_ShiftCount = 31; //1-bit for disposed state private const int Dispose_BitMask = unchecked((int)0x40000000);//0100 0000 0000 0000 0000 0000 0000 0000 //11-bits for m_spinCount private const int SpinCountState_BitMask = unchecked((int)0x3FF80000); //0011 1111 1111 1000 0000 0000 0000 0000 private const int SpinCountState_ShiftCount = 19; private const int SpinCountState_MaxValue = (1 << 11) - 1; //2047 //19-bits for m_waiters. This allows support of 512K threads waiting which should be ample private const int NumWaitersState_BitMask = unchecked((int)0x0007FFFF); // 0000 0000 0000 0111 1111 1111 1111 1111 private const int NumWaitersState_ShiftCount = 0; private const int NumWaitersState_MaxValue = (1 << 19) - 1; //512K-1 // ----------- // #if DEBUG private static int s_nextId; // The next id that will be given out. private int m_id = Interlocked.Increment(ref s_nextId); // A unique id for debugging purposes only. private long m_lastSetTime; private long m_lastResetTime; #endif ///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, and Reset, which should only be used when no other threads are /// accessing the event. /// /// Gets the underlying ///object for this . /// The underlying ///event object fore this . /// Accessing this property forces initialization of an underlying event object if one hasn't /// already been created. To simply wait on this public WaitHandle WaitHandle { get { ThrowIfDisposed(); if (m_eventObj == null) { // Lazily initialize the event object if needed. LazyInitializeEvent(); } return m_eventObj; } } ///, /// the public Wait methods should be preferred. /// /// Gets whether the event is set. /// ///true if the event has is set; otherwise, false. public bool IsSet { get { return 0 != ExtractStatePortion(m_combinedState, SignalledState_BitMask); } private set { UpdateStateAtomically(((value) ? 1 : 0) << SignalledState_ShiftCount, SignalledState_BitMask); } } ////// Gets the number of spin waits that will be occur before falling back to a true wait. /// public int SpinCount { get { return ExtractStatePortionAndShiftRight(m_combinedState, SpinCountState_BitMask, SpinCountState_ShiftCount); } private set { Contract.Assert(value >= 0, "SpinCount is a restricted-width integer. The value supplied is outside the legal range."); Contract.Assert(value <= SpinCountState_MaxValue, "SpinCount is a restricted-width integer. The value supplied is outside the legal range."); // Don't worry about thread safety because it's set one time from the constructor m_combinedState = (m_combinedState & ~SpinCountState_BitMask) | (value << SpinCountState_ShiftCount); } } ////// How many threads are waiting. /// private int Waiters { get { return ExtractStatePortionAndShiftRight(m_combinedState, NumWaitersState_BitMask, NumWaitersState_ShiftCount); } set { //setting to <0 would indicate an internal flaw, hence Assert is appropriate. Contract.Assert(value >= 0, "NumWaiters should never be less than zero. This indicates an internal error."); // it is possible for the max number of waiters to be exceeded via user-code, hence we use a real exception here. if (value >= NumWaitersState_MaxValue) throw new InvalidOperationException(String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_TooManyWaiters"), NumWaitersState_MaxValue)); UpdateStateAtomically(value << NumWaitersState_ShiftCount, NumWaitersState_BitMask); } } //------------------------------------------------------------------------------------ // Constructs a new event, optionally specifying the initial state and spin count. // The defaults are that the event is unsignaled and some reasonable default spin. // ////// Initializes a new instance of the public ManualResetEventSlim() : this(false) { } ////// class with an initial state of nonsignaled. /// /// Initializes a new instance of the /// true to set the initial state signaled; false to set the initial state /// to nonsignaled. public ManualResetEventSlim(bool initialState) { // Specify the defualt spin count, and use default spin if we're // on a multi-processor machine. Otherwise, we won't. Initialize(initialState, DEFAULT_SPIN_MP); } ////// class with a Boolen value indicating whether to set the intial state to signaled. /// /// Initializes a new instance of the /// true to set the initial state to signaled; false to set the initial state /// to nonsignaled. /// The number of spin waits that will occur before falling back to a true /// wait. ////// class with a Boolen value indicating whether to set the intial state to signaled and a specified /// spin count. /// public ManualResetEventSlim(bool initialState, int spinCount) { if (spinCount < 0) { throw new ArgumentOutOfRangeException("spinCount"); } if (spinCount > SpinCountState_MaxValue) { throw new ArgumentOutOfRangeException( "spinCount", String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_SpinCountOutOfRange"), SpinCountState_MaxValue)); } // We will suppress default spin because the user specified a count. Initialize(initialState, spinCount); } /// is less than /// 0 or greater than the maximum allowed value. /// Initializes the internal state of the event. /// /// Whether the event is set initially or not. /// The spin count that decides when the event will block. private void Initialize(bool initialState, int spinCount) { IsSet = initialState; //the spinCount argument has been validated by the ctors. //but we now sanity check our predefined constants. Contract.Assert(DEFAULT_SPIN_SP >= 0, "Internal error - DEFAULT_SPIN_SP is outside the legal range."); Contract.Assert(DEFAULT_SPIN_SP <= SpinCountState_MaxValue, "Internal error - DEFAULT_SPIN_SP is outside the legal range."); SpinCount = PlatformHelper.IsSingleProcessor ? DEFAULT_SPIN_SP : spinCount; } ////// Helper to ensure the lock object is created before first use. /// private void EnsureLockObjectCreated() { Contract.Ensures(m_lock != null); if (m_lock != null) return; object newObj = new object(); Interlocked.CompareExchange(ref m_lock, newObj, null); // failure is benign.. someone else won the ----. } ////// This method lazily initializes the event object. It uses CAS to guarantee that /// many threads racing to call this at once don't result in more than one event /// being stored and used. The event will be signaled or unsignaled depending on /// the state of the thin-event itself, with synchronization taken into account. /// ///True if a new event was created and stored, false otherwise. private bool LazyInitializeEvent() { bool preInitializeIsSet = IsSet; ManualResetEvent newEventObj = new ManualResetEvent(preInitializeIsSet); // We have to CAS this in case we are racing with another thread. We must // guarantee only one event is actually stored in this field. if (Interlocked.CompareExchange(ref m_eventObj, newEventObj, null) != null) { // We ----d with someone else and lost. Destroy the garbage event. newEventObj.Close(); return false; } else { // Now that the event is published, verify that the state hasn't changed since // we snapped the preInitializeState. Another thread could have done that // between our initial observation above and here. The barrier incurred from // the CAS above (in addition to m_state being volatile) prevents this read // from moving earlier and being collapsed with our original one. bool currentIsSet = IsSet; if (currentIsSet != preInitializeIsSet) { Contract.Assert(currentIsSet, "The only safe concurrent transition is from unset->set: detected set->unset."); // We saw it as unsignaled, but it has since become set. lock (newEventObj) { // If our event hasn't already been disposed of, we must set it. if (m_eventObj == newEventObj) { newEventObj.Set(); } } } return true; } } ////// Sets the state of the event to signaled, which allows one or more threads waiting on the event to /// proceed. /// public void Set() { Set(false); } ////// Private helper to actually perform the Set. /// /// Indicates whether we are calling Set() during cancellation. ///The object has been canceled. private void Set(bool duringCancellation) { // We need to ensure that IsSet=true does not get reordered past the read of m_eventObj // This would be a legal movement according to the .NET memory model. // The code is safe as IsSet involves an Interlocked.CompareExchange which provides a full memory barrier. IsSet = true; // If there are waiting threads, we need to pulse them. if (Waiters > 0) { //m_lock is not volatile, but the backing store of Waiters is volatile. //Hence READ(Waiters) was a load-acquire. READ(m_lock) will not reorder above. //and there is no risk of loading a null m_lock and then observing Waiters > 0. //Also, the writes to m_lock and Waiters in Wait(int millisecondsTimeout, CancellationToken cancellationToken) //cannot reorder as all writes are Store-Release in MM Contract.Assert(m_lock != null); //if waiters>0, then m_lock has already been created. lock (m_lock) { Monitor.PulseAll(m_lock); } } ManualResetEvent eventObj = m_eventObj; //Design-decision: do not set the event if we are in cancellation -> better to deadlock than to wake up waiters incorrectly //It would be preferable to wake up the event and have it throw OCE. This requires MRE to implement cancellation logic if (eventObj != null && !duringCancellation) { // We must surround this call to Set in a lock. The reason is fairly subtle. // Sometimes a thread will issue a Wait and wake up after we have set m_state, // but before we have gotten around to setting m_eventObj (just below). That's // because Wait first checks m_state and will only access the event if absolutely // necessary. However, the coding pattern { event.Wait(); event.Dispose() } is // quite common, and we must support it. If the waiter woke up and disposed of // the event object before the setter has finished, however, we would try to set a // now-disposed Win32 event. Crash! To deal with this ----, we use a lock to // protect access to the event object when setting and disposing of it. We also // double-check that the event has not become null in the meantime when in the lock. lock (eventObj) { if (m_eventObj != null) { // If somebody is waiting, we must set the event. m_eventObj.Set(); } } } #if DEBUG m_lastSetTime = DateTime.Now.Ticks; #endif } ////// Sets the state of the event to nonsignaled, which causes threads to block. /// ////// Unlike most of the members of public void Reset() { ThrowIfDisposed(); // If there's an event, reset it. if (m_eventObj != null) { m_eventObj.Reset(); } // There is a ---- here. If another thread Sets the event, we will get into a state // where m_state will be unsignaled, yet the Win32 event object will have been signaled. // This could cause waiting threads to wake up even though the event is in an // unsignaled state. This is fine -- those that are calling Reset concurrently are // responsible for doing "the right thing" -- e.g. rechecking the condition and // resetting the event manually. // And finally set our state back to unsignaled. IsSet = false; #if DEBUG m_lastResetTime = DateTime.Now.Ticks; #endif } ///, is not /// thread-safe and may not be used concurrently with other members of this instance. /// /// Blocks the current thread until the current ///is set. /// /// The maximum number of waiters has been exceeded. /// ////// The caller of this method blocks indefinitely until the current instance is set. The caller will /// return immediately if the event is currently in a set state. /// public void Wait() { Wait(Timeout.Infinite, new CancellationToken()); } ////// Blocks the current thread until the current /// Thereceives a signal, /// while observing a . /// to /// observe. /// /// The maximum number of waiters has been exceeded. /// ////// was /// canceled. /// The caller of this method blocks indefinitely until the current instance is set. The caller will /// return immediately if the event is currently in a set state. /// public void Wait(CancellationToken cancellationToken) { Wait(Timeout.Infinite, cancellationToken); } ////// Blocks the current thread until the current /// Ais set, 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 ///was set; otherwise, /// false. /// is a negative /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater /// than . /// The maximum number of waiters has been exceeded. /// public bool Wait(TimeSpan timeout) { long totalMilliseconds = (long)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) { throw new ArgumentOutOfRangeException("timeout"); } return Wait((int)totalMilliseconds, new CancellationToken()); } ////// Blocks the current thread until the current /// Ais set, 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 ///was set; otherwise, /// false. /// is a negative /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater /// than . /// was canceled. /// The maximum number of waiters has been exceeded. /// public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) { long totalMilliseconds = (long)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) { throw new ArgumentOutOfRangeException("timeout"); } return Wait((int)totalMilliseconds, cancellationToken); } ////// Blocks the current thread until the current /// The number of milliseconds to wait, oris set, using a /// 32-bit signed integer to measure the time interval. /// (-1) to wait indefinitely. /// true if the ///was set; otherwise, /// false. /// is a /// negative number other than -1, which represents an infinite time-out. /// The maximum number of waiters has been exceeded. /// public bool Wait(int millisecondsTimeout) { return Wait(millisecondsTimeout, new CancellationToken()); } ////// Blocks the current thread until the current /// The number of milliseconds to wait, oris set, using a /// 32-bit signed integer to measure the time interval, while observing a . /// (-1) to wait indefinitely. /// The to /// observe. /// true if the ///was set; otherwise, /// false. /// is a /// negative number other than -1, which represents an infinite time-out. /// The maximum number of waiters has been exceeded. /// ///public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); // an early convenience check if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (!IsSet) { if (millisecondsTimeout == 0) { // For 0-timeouts, we just return immediately. return false; } // We spin briefly before falling back to allocating and/or waiting on a true event. long startTimeTicks = 0; bool bNeedTimeoutAdjustment = false; int realMillisecondsTimeout = millisecondsTimeout; //this will be adjusted if necessary. if (millisecondsTimeout != Timeout.Infinite) { // We will account for time spent spinning, so that we can decrement it from our // timeout. In most cases the time spent in this section will be negligible. But // we can't discount the possibility of our thread being switched out for a lengthy // period of time. The timeout adjustments only take effect when and if we actually // decide to block in the kernel below. startTimeTicks = DateTime.UtcNow.Ticks; bNeedTimeoutAdjustment = true; } //spin int HOW_MANY_SPIN_BEFORE_YIELD = 10; int HOW_MANY_YIELD_EVERY_SLEEP_0 = 5; int HOW_MANY_YIELD_EVERY_SLEEP_1 = 20; for (int i = 0; i < SpinCount; i++) { if (IsSet) { return true; } else if (i < HOW_MANY_SPIN_BEFORE_YIELD) { if (i == HOW_MANY_SPIN_BEFORE_YIELD / 2) { #if PFX_LEGACY_3_5 Platform.Yield(); #else Thread.Yield(); #endif } else { Thread.SpinWait(Environment.ProcessorCount * (4 << i)); } } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_1 == 0) { Thread.Sleep(1); } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_0 == 0) { Thread.Sleep(0); } else { #if PFX_LEGACY_3_5 Platform.Yield(); #else Thread.Yield(); #endif } if (i >= 100 && i % 10 == 0) // check the cancellation token if the user passed a very large spin count cancellationToken.ThrowIfCancellationRequested(); } // Now enter the lock and wait. EnsureLockObjectCreated(); // We must register and deregister the token outside of the lock, to avoid deadlocks. using (cancellationToken.Register(s_cancellationTokenCallback, this)) { lock (m_lock) { // Loop to cope with spurious wakeups from other waits being canceled while (!IsSet) { // If our token was canceled, we must throw and exit. cancellationToken.ThrowIfCancellationRequested(); //update timeout (delays in wait commencement are due to spinning and/or spurious wakeups from other waits being canceled) if (bNeedTimeoutAdjustment) { realMillisecondsTimeout = UpdateTimeOut(startTimeTicks, millisecondsTimeout); if (realMillisecondsTimeout <= 0) return false; } // There is a ---- that Set will fail to see that there are waiters as Set does not take the lock, // so after updating waiters, we must check IsSet again. // Also, we must ensure there cannot be any reordering of the assignment to Waiters and the // read from IsSet. This is guaranteed as Waiters{set;} involves an Interlocked.CompareExchange // operation which provides a full memory barrier. // If we see IsSet=false, then we are guaranteed that Set() will see that we are // waiting and will pulse the monitor correctly. Waiters = Waiters + 1; if (IsSet) //This check must occur after updating Waiters. { Waiters--; //revert the increment. return true; } // Now finally perform the wait. try { // ** the actual wait ** if (!Monitor.Wait(m_lock, realMillisecondsTimeout)) return false; //return immediately if the timeout has expired. } finally { // Clean up: we're done waiting. Waiters = Waiters - 1; } // Now just loop back around, and the right thing will happen. Either: // 1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait) // or 2. the wait was successful. (the loop will break) } } } } // automatically disposes (and deregisters) the callback return true; //done. The wait was satisfied. } /// was canceled. /// 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 ((m_combinedState & Dispose_BitMask) != 0) return; // already disposed m_combinedState |= Dispose_BitMask; //set the dispose bit if (disposing) { // We will dispose of the event object. We do this under a lock to protect // against the race condition outlined in the Set method above. ManualResetEvent eventObj = m_eventObj; if (eventObj != null) { lock (eventObj) { eventObj.Close(); m_eventObj = null; } } } } ///, is not /// thread-safe and may not be used concurrently with other members of this instance. /// /// Throw ObjectDisposedException if the MRES is disposed /// private void ThrowIfDisposed() { if ((m_combinedState & Dispose_BitMask) != 0) throw new ObjectDisposedException(Environment.GetResourceString("ManualResetEventSlim_Disposed")); } ////// 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
- SqlUserDefinedAggregateAttribute.cs
- CodeComment.cs
- DataControlPagerLinkButton.cs
- LogEntryHeaderDeserializer.cs
- EventArgs.cs
- X509Logo.cs
- XhtmlTextWriter.cs
- TypeConverterValueSerializer.cs
- MostlySingletonList.cs
- EmbeddedMailObject.cs
- Enum.cs
- ParenthesizePropertyNameAttribute.cs
- validationstate.cs
- BooleanSwitch.cs
- BinaryExpression.cs
- IncrementalCompileAnalyzer.cs
- DependencyObjectType.cs
- DataGridRowEventArgs.cs
- X509LogoTypeExtension.cs
- SqlReferenceCollection.cs
- Constants.cs
- CodeConstructor.cs
- MD5.cs
- MessagePropertyAttribute.cs
- ViewService.cs
- ErrorRuntimeConfig.cs
- EdmItemCollection.cs
- DataGridViewAutoSizeModeEventArgs.cs
- LogManagementAsyncResult.cs
- LinkArea.cs
- DatasetMethodGenerator.cs
- FullTrustAssembly.cs
- SoapCodeExporter.cs
- _SslSessionsCache.cs
- UTF7Encoding.cs
- WebPart.cs
- Helpers.cs
- HexParser.cs
- OperationParameterInfo.cs
- ServiceHttpModule.cs
- KeyboardEventArgs.cs
- BindingMAnagerBase.cs
- TableCellCollection.cs
- RecordBuilder.cs
- DecodeHelper.cs
- ArgumentValue.cs
- SQLByte.cs
- RelOps.cs
- AuthenticationException.cs
- FontNamesConverter.cs
- DBProviderConfigurationHandler.cs
- _ProxyRegBlob.cs
- TextTreeUndo.cs
- DataGridViewTextBoxCell.cs
- IpcClientChannel.cs
- Funcletizer.cs
- WebPartTransformerAttribute.cs
- DebuggerAttributes.cs
- HtmlAnchor.cs
- PeerNameRecordCollection.cs
- SafeSystemMetrics.cs
- StateMachineWorkflowInstance.cs
- CodeIdentifiers.cs
- ObjectQueryState.cs
- TdsParserStateObject.cs
- ConversionContext.cs
- versioninfo.cs
- AssemblyBuilderData.cs
- SqlPersonalizationProvider.cs
- GridViewColumnCollectionChangedEventArgs.cs
- SQLResource.cs
- ZoneButton.cs
- GridItemProviderWrapper.cs
- TypeConvertions.cs
- EventBuilder.cs
- DynamicControlParameter.cs
- RolePrincipal.cs
- NegatedCellConstant.cs
- WebWorkflowRole.cs
- XmlDictionary.cs
- RelativeSource.cs
- WebPartChrome.cs
- ConfigurationStrings.cs
- ChangeDirector.cs
- CommonXSendMessage.cs
- ButtonBase.cs
- ComponentResourceManager.cs
- _UriSyntax.cs
- Frame.cs
- JpegBitmapDecoder.cs
- SqlVersion.cs
- PolyQuadraticBezierSegment.cs
- IImplicitResourceProvider.cs
- IOException.cs
- ToolStripDropDownMenu.cs
- AdornedElementPlaceholder.cs
- XmlElementCollection.cs
- DeferredReference.cs
- Int32RectConverter.cs
- PositiveTimeSpanValidatorAttribute.cs