Esempio n. 1
0
        private static void Wait(int spinIndex)
        {
            Debug.Assert(SpinYieldThreshold < SpinSleep0Threshold);

            if (spinIndex < SpinYieldThreshold)
            {
                RuntimeThread.SpinWait(1 << spinIndex);
                return;
            }

            if (spinIndex < SpinSleep0Threshold && RuntimeThread.Yield())
            {
                return;
            }

            /// <see cref="RuntimeThread.Sleep(int)"/> is interruptible. The current operation may not allow thread interrupt
            /// (for instance, <see cref="LowLevelLock.Acquire"/> as part of <see cref="EventWaitHandle.Set"/>). Use the
            /// uninterruptible version of Sleep(0).
            RuntimeThread.UninterruptibleSleep0();

            // Don't want to Sleep(1) in this spin wait:
            //   - Don't want to spin for that long, since a proper wait will follow when the spin wait fails
            //   - Sleep(1) would put the thread into a wait state, and a proper wait will follow when the spin wait fails
            //     anyway (the intended use for this class), so it's preferable to put the thread into the proper wait state
        }
Esempio n. 2
0
        public static void Wait(int spinIndex, int sleep0Threshold, int processorCount)
        {
            Debug.Assert(spinIndex >= 0);
            Debug.Assert(sleep0Threshold >= 0);

            // Wait
            //
            // (spinIndex - Sleep0Threshold) % 2 != 0: The purpose of this check is to interleave Thread.Yield/Sleep(0) with
            // Thread.SpinWait. Otherwise, the following issues occur:
            //   - When there are no threads to switch to, Yield and Sleep(0) become no-op and it turns the spin loop into a
            //     busy-spin that may quickly reach the max spin count and cause the thread to enter a wait state. Completing the
            //     spin loop too early can cause excessive context switcing from the wait.
            //   - If there are multiple threads doing Yield and Sleep(0) (typically from the same spin loop due to contention),
            //     they may switch between one another, delaying work that can make progress.
            if (processorCount > 1 && (spinIndex < sleep0Threshold || (spinIndex - sleep0Threshold) % 2 != 0))
            {
                // Cap the maximum spin count to a value such that many thousands of CPU cycles would not be wasted doing
                // the equivalent of YieldProcessor(), as that that point SwitchToThread/Sleep(0) are more likely to be able to
                // allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is
                // usually better for that.
                //
                // RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration:
                //   - See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value.
                //
                int n = RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration;
                if (spinIndex <= 30 && (1 << spinIndex) < n)
                {
                    n = 1 << spinIndex;
                }
                RuntimeThread.SpinWait(n);
                return;
            }

            /// <see cref="RuntimeThread.Sleep(int)"/> is interruptible. The current operation may not allow thread interrupt
            /// (for instance, <see cref="LowLevelLock.Acquire"/> as part of <see cref="EventWaitHandle.Set"/>). Use the
            /// uninterruptible version of Sleep(0). Not doing <see cref="RuntimeThread.Yield"/>, it does not seem to have any
            /// benefit over Sleep(0).
            RuntimeThread.UninterruptibleSleep0();

            // Don't want to Sleep(1) in this spin wait:
            //   - Don't want to spin for that long, since a proper wait will follow when the spin wait fails
            //   - Sleep(1) would put the thread into a wait state, and a proper wait will follow when the spin wait fails
            //     anyway (the intended use for this class), so it's preferable to put the thread into the proper wait state
        }