@@ -88,7 +88,16 @@ public struct SpinWait
8888 /// for here.
8989 /// </remarks>
9090 internal static readonly int SpinCountforSpinBeforeWait = PlatformHelper . IsSingleProcessor ? 1 : 35 ;
91- internal const int Sleep1ThresholdForSpinBeforeWait = 40 ; // should be greater than SpinCountforSpinBeforeWait
91+
92+ /// <summary>
93+ /// Typically, Sleep(1) should not be issued for a spin-wait before a proper wait because it is usually more beneficial
94+ /// to just issue the proper wait. For longer spin-waits (when the spin count is configurable), this value may be used as
95+ /// a threshold for issuing Sleep(1).
96+ /// </summary>
97+ /// <remarks>
98+ /// Should be greater than <see cref="SpinCountforSpinBeforeWait"/> so that Sleep(1) would not be used by default.
99+ /// </remarks>
100+ internal const int Sleep1ThresholdForLongSpinBeforeWait = 40 ;
92101
93102 // The number of times we've spun already.
94103 private int _count ;
@@ -127,12 +136,42 @@ internal set
127136 /// </remarks>
128137 public void SpinOnce ( )
129138 {
130- SpinOnce ( DefaultSleep1Threshold ) ;
139+ SpinOnceCore ( DefaultSleep1Threshold ) ;
140+ }
141+
142+ /// <summary>
143+ /// Performs a single spin.
144+ /// </summary>
145+ /// <param name="sleep1Threshold">
146+ /// A minimum spin count after which <code>Thread.Sleep(1)</code> may be used. A value of <code>-1</code> may be used to
147+ /// disable the use of <code>Thread.Sleep(1)</code>.
148+ /// </param>
149+ /// <exception cref="ArgumentOutOfRangeException">
150+ /// <paramref name="sleep1Threshold"/> is less than <code>-1</code>.
151+ /// </exception>
152+ /// <remarks>
153+ /// This is typically called in a loop, and may change in behavior based on the number of times a
154+ /// <see cref="SpinOnce"/> has been called thus far on this instance.
155+ /// </remarks>
156+ public void SpinOnce ( int sleep1Threshold )
157+ {
158+ if ( sleep1Threshold < - 1 )
159+ {
160+ throw new ArgumentOutOfRangeException ( nameof ( sleep1Threshold ) , sleep1Threshold , SR . ArgumentOutOfRange_NeedNonNegOrNegative1 ) ;
161+ }
162+
163+ if ( sleep1Threshold >= 0 && sleep1Threshold < YieldThreshold )
164+ {
165+ sleep1Threshold = YieldThreshold ;
166+ }
167+
168+ SpinOnceCore ( sleep1Threshold ) ;
131169 }
132170
133- internal void SpinOnce ( int sleep1Threshold )
171+ private void SpinOnceCore ( int sleep1Threshold )
134172 {
135- Debug . Assert ( sleep1Threshold >= YieldThreshold || PlatformHelper . IsSingleProcessor ) ; // so that NextSpinWillYield behaves as requested
173+ Debug . Assert ( sleep1Threshold >= - 1 ) ;
174+ Debug . Assert ( sleep1Threshold < 0 || sleep1Threshold >= YieldThreshold ) ;
136175
137176 // (_count - YieldThreshold) % 2 == 0: The purpose of this check is to interleave Thread.Yield/Sleep(0) with
138177 // Thread.SpinWait. Otherwise, the following issues occur:
@@ -145,7 +184,7 @@ internal void SpinOnce(int sleep1Threshold)
145184 // contention), they may switch between one another, delaying work that can make progress.
146185 if ( (
147186 _count >= YieldThreshold &&
148- ( _count >= sleep1Threshold || ( _count - YieldThreshold ) % 2 == 0 )
187+ ( ( _count >= sleep1Threshold && sleep1Threshold >= 0 ) || ( _count - YieldThreshold ) % 2 == 0 )
149188 ) ||
150189 PlatformHelper . IsSingleProcessor )
151190 {
@@ -164,7 +203,7 @@ internal void SpinOnce(int sleep1Threshold)
164203 // configured to use the (default) coarse-grained system timer.
165204 //
166205
167- if ( _count >= sleep1Threshold )
206+ if ( _count >= sleep1Threshold && sleep1Threshold >= 0 )
168207 {
169208 RuntimeThread . Sleep ( 1 ) ;
170209 }
0 commit comments