Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Threading / SpinWait.cs / 1305376 / SpinWait.cs
// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // SpinWait.cs // //[....] // // Central spin logic used across the entire code-base. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Runtime.ConstrainedExecution; using System.Security.Permissions; using System.Threading; using System.Diagnostics.Contracts; namespace System.Threading { // SpinWait is just a little value type that encapsulates some common spinning // logic. It ensures we always yield on single-proc machines (instead of using busy // waits), and that we work well on HT. It encapsulates a good mixture of spinning // and real yielding. It's a value type so that various areas of the engine can use // one by allocating it on the stack w/out unnecessary GC allocation overhead, e.g.: // // void f() { // SpinWait wait = new SpinWait(); // while (!p) { wait.SpinOnce(); } // ... // } // // Internally it just maintains a counter that is used to decide when to yield, etc. // // A common usage is to spin before blocking. In those cases, the NextSpinWillYield // property allows a user to decide to fall back to waiting once it returns true: // // void f() { // SpinWait wait = new SpinWait(); // while (!p) { // if (wait.NextSpinWillYield) { /* block! */ } // else { wait.SpinOnce(); } // } // ... // } ////// Provides support for spin-based waiting. /// ////// [HostProtection(Synchronization = true, ExternalThreading = true)] public struct SpinWait { // These constants determine the frequency of yields versus spinning. The // numbers may seem fairly arbitrary, but were derived with at least some // thought in the design document. I fully expect they will need to change // over time as we gain more experience with performance. internal const int YIELD_THRESHOLD = 10; // When to switch over to a true yield. internal const int SLEEP_0_EVERY_HOW_MANY_TIMES = 5; // After how many yields should we Sleep(0)? internal const int SLEEP_1_EVERY_HOW_MANY_TIMES = 20; // After how many yields should we Sleep(1)? // The number of times we've spun already. private int m_count; ////// ///encapsulates common spinning logic. On single-processor machines, yields are /// always used instead of busy waits, and on computers with Intel™ processors employing Hyper-Threading™ /// technology, it helps to prevent hardware thread starvation. SpinWait encapsulates a good mixture of /// spinning and true yielding. /// /// ///is a value type, which means that low-level code can utilize SpinWait without /// fear of unnecessary allocation overheads. SpinWait is not generally useful for ordinary applications. /// In most cases, you should use the synchronization classes provided by the .NET Framework, such as /// . For most purposes where spin waiting is required, however, /// the type should be preferred over the method. /// /// While SpinWait is designed to be used in concurrent applications, it is not designed to be /// used from multiple threads concurrently. SpinWait's members are not thread-safe. If multiple /// threads must spin, each should use its own instance of SpinWait. /// ////// Gets the number of times public int Count { get { return m_count; } } ///has been called on this instance. /// /// Gets whether the next call to ///will yield the processor, triggering a /// forced context switch. /// Whether the next call to ///will yield the processor, triggering a /// forced context switch. /// On a single-CPU machine, public bool NextSpinWillYield { get { return m_count > YIELD_THRESHOLD || PlatformHelper.IsSingleProcessor; } } ///always yields the processor. On machines with /// multiple CPUs, may yield after an unspecified number of calls. /// /// Performs a single spin. /// ////// This is typically called in a loop, and may change in behavior based on the number of times a /// public void SpinOnce() { if (NextSpinWillYield) { // // We must yield. // // We prefer to call Thread.Yield first, triggering a SwitchToThread. This // unfortunately doesn't consider all runnable threads on all OS SKUs. In // some cases, it may only consult the runnable threads whose ideal processor // is the one currently executing code. Thus we oc----ionally issue a call to // Sleep(0), which considers all runnable threads at equal priority. Even this // is insufficient since we may be spin waiting for lower priority threads to // execute; we therefore must call Sleep(1) once in a while too, which considers // all runnable threads, regardless of ideal processor and priority, but may // remove the thread from the scheduler's queue for 10+ms, if the system is // configured to use the (default) coarse-grained system timer. // #if !FEATURE_PAL && !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile CDS providers for Coreclr CdsSyncEtwBCLProvider.Log.SpinWait_NextSpinWillYield(); #endif int yieldsSoFar = (m_count >= YIELD_THRESHOLD ? m_count - YIELD_THRESHOLD : m_count); if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1)) { Thread.Sleep(1); } else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1)) { Thread.Sleep(0); } else { #if PFX_LEGACY_3_5 Platform.Yield(); #else Thread.Yield(); #endif } } else { // // Otherwise, we will spin. // // We do this using the CLR's SpinWait API, which is just a busy loop that // issues YIELD/PAUSE instructions to ensure multi-threaded CPUs can react // intelligently to avoid starving. (These are NOOPs on other CPUs.) We // choose a number for the loop iteration count such that each successive // call spins for longer, to reduce cache contention. We cap the total // number of spins we are willing to tolerate to reduce delay to the caller, // since we expect most callers will eventually block anyway. // Thread.SpinWait(4 << m_count); } // Finally, increment our spin counter. m_count = (m_count == int.MaxValue ? YIELD_THRESHOLD : m_count + 1); } ///has been called thus far on this instance. /// /// Resets the spin counter. /// ////// This makes public void Reset() { m_count = 0; } #region Static Methods #if !FEATURE_CORECLR ///and behave as though no calls /// to had been issued on this instance. If a instance /// is reused many times, it may be useful to reset it to avoid yielding too soon. /// /// Spins until the specified condition is satisfied. /// /// A delegate to be executed over and over until it returns true. ///The public static void SpinUntil(Funcargument is null. condition) { #if DEBUG bool result = #endif SpinUntil(condition, Timeout.Infinite); #if DEBUG Contract.Assert(result); #endif } /// /// Spins until the specified condition is satisfied or until the specified timeout is expired. /// /// A delegate to be executed over and over until it returns true. /// /// Athat represents the number of milliseconds to wait, /// or a TimeSpan that represents -1 milliseconds to wait indefinitely. /// True if the condition is satisfied within the timeout; otherwise, false ///The ///argument is null. public static bool SpinUntil(Func is a negative number /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than /// . condition, TimeSpan timeout) { // Validate the timeout Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( "timeout", timeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong")); } // Call wait with the timeout milliseconds return SpinUntil(condition, (int)timeout.TotalMilliseconds); } /// /// Spins until the specified condition is satisfied or until the specified timeout is expired. /// /// A delegate to be executed over and over until it returns true. /// The number of milliseconds to wait, or(-1) to wait indefinitely. /// True if the condition is satisfied within the timeout; otherwise, false ///The ///argument is null. public static bool SpinUntil(Func is a /// negative number other than -1, which represents an infinite time-out. condition, int millisecondsTimeout) { if (millisecondsTimeout < Timeout.Infinite) { throw new ArgumentOutOfRangeException( "millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong")); } if (condition == null) { throw new ArgumentNullException("condition", Environment.GetResourceString("SpinWait_SpinUntil_ArgumentNull")); } long startTicks = 0; ; if (millisecondsTimeout != 0 && millisecondsTimeout != Timeout.Infinite) { startTicks = DateTime.UtcNow.Ticks; } SpinWait spinner = new SpinWait(); while (!condition()) { if (millisecondsTimeout == 0) { return false; } spinner.SpinOnce(); if (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield) { if (millisecondsTimeout <= (DateTime.UtcNow.Ticks - startTicks) / TimeSpan.TicksPerMillisecond) { return false; } } } return true; } #endif //!FEATURE_CORECLR #endregion } /// /// A helper class to get the number of preocessors, it updates the numbers of processors every sampling interval /// internal static class PlatformHelper { private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; // How often to refresh the count, in milliseconds. private static int s_processorCount = -1; // The last count seen. private static DateTime s_nextProcessorCountRefreshTime = DateTime.MinValue; // The next time we'll refresh. ////// Gets the number of available processors /// internal static int ProcessorCount { get { if (DateTime.UtcNow.CompareTo(s_nextProcessorCountRefreshTime) >= 0) { s_processorCount = Environment.ProcessorCount; s_nextProcessorCountRefreshTime = DateTime.UtcNow.AddMilliseconds(PROCESSOR_COUNT_REFRESH_INTERVAL_MS); } Contract.Assert(s_processorCount > 0 && s_processorCount <= 64, "Processor count not within the expected range (1 - 64)."); return s_processorCount; } } ////// Gets whether the current machine has only a single processor. /// internal static bool IsSingleProcessor { get { return ProcessorCount == 1; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // SpinWait.cs // //[....] // // Central spin logic used across the entire code-base. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Runtime.ConstrainedExecution; using System.Security.Permissions; using System.Threading; using System.Diagnostics.Contracts; namespace System.Threading { // SpinWait is just a little value type that encapsulates some common spinning // logic. It ensures we always yield on single-proc machines (instead of using busy // waits), and that we work well on HT. It encapsulates a good mixture of spinning // and real yielding. It's a value type so that various areas of the engine can use // one by allocating it on the stack w/out unnecessary GC allocation overhead, e.g.: // // void f() { // SpinWait wait = new SpinWait(); // while (!p) { wait.SpinOnce(); } // ... // } // // Internally it just maintains a counter that is used to decide when to yield, etc. // // A common usage is to spin before blocking. In those cases, the NextSpinWillYield // property allows a user to decide to fall back to waiting once it returns true: // // void f() { // SpinWait wait = new SpinWait(); // while (!p) { // if (wait.NextSpinWillYield) { /* block! */ } // else { wait.SpinOnce(); } // } // ... // } ////// Provides support for spin-based waiting. /// ////// [HostProtection(Synchronization = true, ExternalThreading = true)] public struct SpinWait { // These constants determine the frequency of yields versus spinning. The // numbers may seem fairly arbitrary, but were derived with at least some // thought in the design document. I fully expect they will need to change // over time as we gain more experience with performance. internal const int YIELD_THRESHOLD = 10; // When to switch over to a true yield. internal const int SLEEP_0_EVERY_HOW_MANY_TIMES = 5; // After how many yields should we Sleep(0)? internal const int SLEEP_1_EVERY_HOW_MANY_TIMES = 20; // After how many yields should we Sleep(1)? // The number of times we've spun already. private int m_count; ////// ///encapsulates common spinning logic. On single-processor machines, yields are /// always used instead of busy waits, and on computers with Intel™ processors employing Hyper-Threading™ /// technology, it helps to prevent hardware thread starvation. SpinWait encapsulates a good mixture of /// spinning and true yielding. /// /// ///is a value type, which means that low-level code can utilize SpinWait without /// fear of unnecessary allocation overheads. SpinWait is not generally useful for ordinary applications. /// In most cases, you should use the synchronization classes provided by the .NET Framework, such as /// . For most purposes where spin waiting is required, however, /// the type should be preferred over the method. /// /// While SpinWait is designed to be used in concurrent applications, it is not designed to be /// used from multiple threads concurrently. SpinWait's members are not thread-safe. If multiple /// threads must spin, each should use its own instance of SpinWait. /// ////// Gets the number of times public int Count { get { return m_count; } } ///has been called on this instance. /// /// Gets whether the next call to ///will yield the processor, triggering a /// forced context switch. /// Whether the next call to ///will yield the processor, triggering a /// forced context switch. /// On a single-CPU machine, public bool NextSpinWillYield { get { return m_count > YIELD_THRESHOLD || PlatformHelper.IsSingleProcessor; } } ///always yields the processor. On machines with /// multiple CPUs, may yield after an unspecified number of calls. /// /// Performs a single spin. /// ////// This is typically called in a loop, and may change in behavior based on the number of times a /// public void SpinOnce() { if (NextSpinWillYield) { // // We must yield. // // We prefer to call Thread.Yield first, triggering a SwitchToThread. This // unfortunately doesn't consider all runnable threads on all OS SKUs. In // some cases, it may only consult the runnable threads whose ideal processor // is the one currently executing code. Thus we oc----ionally issue a call to // Sleep(0), which considers all runnable threads at equal priority. Even this // is insufficient since we may be spin waiting for lower priority threads to // execute; we therefore must call Sleep(1) once in a while too, which considers // all runnable threads, regardless of ideal processor and priority, but may // remove the thread from the scheduler's queue for 10+ms, if the system is // configured to use the (default) coarse-grained system timer. // #if !FEATURE_PAL && !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile CDS providers for Coreclr CdsSyncEtwBCLProvider.Log.SpinWait_NextSpinWillYield(); #endif int yieldsSoFar = (m_count >= YIELD_THRESHOLD ? m_count - YIELD_THRESHOLD : m_count); if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1)) { Thread.Sleep(1); } else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1)) { Thread.Sleep(0); } else { #if PFX_LEGACY_3_5 Platform.Yield(); #else Thread.Yield(); #endif } } else { // // Otherwise, we will spin. // // We do this using the CLR's SpinWait API, which is just a busy loop that // issues YIELD/PAUSE instructions to ensure multi-threaded CPUs can react // intelligently to avoid starving. (These are NOOPs on other CPUs.) We // choose a number for the loop iteration count such that each successive // call spins for longer, to reduce cache contention. We cap the total // number of spins we are willing to tolerate to reduce delay to the caller, // since we expect most callers will eventually block anyway. // Thread.SpinWait(4 << m_count); } // Finally, increment our spin counter. m_count = (m_count == int.MaxValue ? YIELD_THRESHOLD : m_count + 1); } ///has been called thus far on this instance. /// /// Resets the spin counter. /// ////// This makes public void Reset() { m_count = 0; } #region Static Methods #if !FEATURE_CORECLR ///and behave as though no calls /// to had been issued on this instance. If a instance /// is reused many times, it may be useful to reset it to avoid yielding too soon. /// /// Spins until the specified condition is satisfied. /// /// A delegate to be executed over and over until it returns true. ///The public static void SpinUntil(Funcargument is null. condition) { #if DEBUG bool result = #endif SpinUntil(condition, Timeout.Infinite); #if DEBUG Contract.Assert(result); #endif } /// /// Spins until the specified condition is satisfied or until the specified timeout is expired. /// /// A delegate to be executed over and over until it returns true. /// /// Athat represents the number of milliseconds to wait, /// or a TimeSpan that represents -1 milliseconds to wait indefinitely. /// True if the condition is satisfied within the timeout; otherwise, false ///The ///argument is null. public static bool SpinUntil(Func is a negative number /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than /// . condition, TimeSpan timeout) { // Validate the timeout Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( "timeout", timeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong")); } // Call wait with the timeout milliseconds return SpinUntil(condition, (int)timeout.TotalMilliseconds); } /// /// Spins until the specified condition is satisfied or until the specified timeout is expired. /// /// A delegate to be executed over and over until it returns true. /// The number of milliseconds to wait, or(-1) to wait indefinitely. /// True if the condition is satisfied within the timeout; otherwise, false ///The ///argument is null. public static bool SpinUntil(Func is a /// negative number other than -1, which represents an infinite time-out. condition, int millisecondsTimeout) { if (millisecondsTimeout < Timeout.Infinite) { throw new ArgumentOutOfRangeException( "millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong")); } if (condition == null) { throw new ArgumentNullException("condition", Environment.GetResourceString("SpinWait_SpinUntil_ArgumentNull")); } long startTicks = 0; ; if (millisecondsTimeout != 0 && millisecondsTimeout != Timeout.Infinite) { startTicks = DateTime.UtcNow.Ticks; } SpinWait spinner = new SpinWait(); while (!condition()) { if (millisecondsTimeout == 0) { return false; } spinner.SpinOnce(); if (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield) { if (millisecondsTimeout <= (DateTime.UtcNow.Ticks - startTicks) / TimeSpan.TicksPerMillisecond) { return false; } } } return true; } #endif //!FEATURE_CORECLR #endregion } /// /// A helper class to get the number of preocessors, it updates the numbers of processors every sampling interval /// internal static class PlatformHelper { private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; // How often to refresh the count, in milliseconds. private static int s_processorCount = -1; // The last count seen. private static DateTime s_nextProcessorCountRefreshTime = DateTime.MinValue; // The next time we'll refresh. ////// Gets the number of available processors /// internal static int ProcessorCount { get { if (DateTime.UtcNow.CompareTo(s_nextProcessorCountRefreshTime) >= 0) { s_processorCount = Environment.ProcessorCount; s_nextProcessorCountRefreshTime = DateTime.UtcNow.AddMilliseconds(PROCESSOR_COUNT_REFRESH_INTERVAL_MS); } Contract.Assert(s_processorCount > 0 && s_processorCount <= 64, "Processor count not within the expected range (1 - 64)."); return s_processorCount; } } ////// Gets whether the current machine has only a single processor. /// internal static bool IsSingleProcessor { get { return ProcessorCount == 1; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ExpressionConverter.cs
- ReversePositionQuery.cs
- DataGridViewCellConverter.cs
- StylusCollection.cs
- LabelDesigner.cs
- DbConnectionPool.cs
- LayoutEditorPart.cs
- QilUnary.cs
- TypeFieldSchema.cs
- ModuleConfigurationInfo.cs
- TerminateWorkflow.cs
- CellTreeNode.cs
- PropertyStore.cs
- XmlWrappingReader.cs
- ReadOnlyNameValueCollection.cs
- Part.cs
- TransformerInfoCollection.cs
- _ListenerResponseStream.cs
- DatasetMethodGenerator.cs
- WebAdminConfigurationHelper.cs
- ButtonRenderer.cs
- HwndHost.cs
- ISAPIWorkerRequest.cs
- XmlValidatingReader.cs
- ThumbButtonInfoCollection.cs
- CapiSafeHandles.cs
- RightsManagementPermission.cs
- VisualStyleTypesAndProperties.cs
- DataSourceControlBuilder.cs
- CryptographicAttribute.cs
- SqlConnectionFactory.cs
- RijndaelCryptoServiceProvider.cs
- RoutedEventValueSerializer.cs
- ObjectDataSourceFilteringEventArgs.cs
- ColorAnimationUsingKeyFrames.cs
- SiteMapNodeItem.cs
- WhileDesigner.cs
- MemberMaps.cs
- DesigntimeLicenseContext.cs
- ToolboxItemFilterAttribute.cs
- SafeCryptoHandles.cs
- DataReaderContainer.cs
- AdRotatorDesigner.cs
- CryptographicAttribute.cs
- ListCollectionView.cs
- ClrProviderManifest.cs
- FieldToken.cs
- SoapException.cs
- DataGridViewColumnCollectionEditor.cs
- AlphabeticalEnumConverter.cs
- HistoryEventArgs.cs
- SqlTopReducer.cs
- RunInstallerAttribute.cs
- AutoGeneratedFieldProperties.cs
- ExpandoObject.cs
- listitem.cs
- WebMethodAttribute.cs
- PtsCache.cs
- HttpListener.cs
- RuntimeEnvironment.cs
- TemplateNameScope.cs
- MailAddressParser.cs
- WeakEventManager.cs
- Bidi.cs
- DrawingContextWalker.cs
- DefaultHttpHandler.cs
- EventProvider.cs
- UnsafeNativeMethods.cs
- XPathDocumentNavigator.cs
- ResourceAttributes.cs
- SchemaMapping.cs
- FontFaceLayoutInfo.cs
- VBCodeProvider.cs
- OrderByBuilder.cs
- GestureRecognitionResult.cs
- XmlWellformedWriter.cs
- EnumCodeDomSerializer.cs
- Separator.cs
- XmlCDATASection.cs
- SimplePropertyEntry.cs
- LinqDataSourceHelper.cs
- SafeRightsManagementQueryHandle.cs
- RSAPKCS1SignatureDeformatter.cs
- WebPartsPersonalizationAuthorization.cs
- GeometryDrawing.cs
- XPathAxisIterator.cs
- ControlIdConverter.cs
- PropertyMap.cs
- TypeUtils.cs
- RegexRunnerFactory.cs
- ProviderUtil.cs
- WMIGenerator.cs
- EditingCoordinator.cs
- ExpandSegmentCollection.cs
- MetadataArtifactLoaderCompositeFile.cs
- ConstraintStruct.cs
- TypeBuilderInstantiation.cs
- ToolStripDropDownClosingEventArgs.cs
- StorageBasedPackageProperties.cs
- KeyValuePair.cs