Line data Source code
1 : using System;
2 : using System.Security.Permissions;
3 : using System.Threading;
4 :
5 : namespace Cqrs.Infrastructure
6 : {
7 : /// <summary>
8 : /// Provides support for spin-based waiting.
9 : /// </summary>
10 : [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)]
11 : public struct SpinWait
12 1 : {
13 : /// <summary>
14 : /// A recommended sleep value of 50.
15 : /// </summary>
16 : public const short DefaultSleepInMilliseconds = 50;
17 :
18 : internal const int YieldThreshold = 10;
19 : internal const int Sleep0EveryHowManyTimes = 5;
20 : internal const int Sleep1EveryHowManyTimes = 20;
21 : private int _count;
22 :
23 : /// <summary>
24 : /// Gets whether the next call to <see cref="M:System.Threading.SpinWait.SpinOnce"/> will yield the processor, triggering a forced context switch.
25 : /// </summary>
26 : ///
27 : /// <returns>
28 : /// Whether the next call to <see cref="M:System.Threading.SpinWait.SpinOnce"/> will yield the processor, triggering a forced context switch.
29 : /// </returns>
30 : public bool NextSpinWillYield
31 : {
32 : get
33 : {
34 : if (_count <= 10)
35 : return Environment.ProcessorCount == 1;
36 : return true;
37 : }
38 : }
39 :
40 : /// <summary>
41 : /// Performs a single spin.
42 : /// </summary>
43 : /// <param name="sleepInMilliseconds">The amount of milliseconds the thread will sleep for.</param>
44 1 : public void SpinOnce(short sleepInMilliseconds = 0)
45 : {
46 : if (NextSpinWillYield)
47 : {
48 : int num = _count >= 10 ? _count - 10 : _count;
49 : if (num % Sleep1EveryHowManyTimes == Sleep1EveryHowManyTimes - 1)
50 : Thread.Sleep(sleepInMilliseconds == 0 ? 1 : sleepInMilliseconds * 2);
51 : else if (num % Sleep0EveryHowManyTimes == Sleep0EveryHowManyTimes - 1)
52 : Thread.Sleep(sleepInMilliseconds);
53 : else
54 : {
55 : Thread.Yield();
56 : if (sleepInMilliseconds >= DefaultSleepInMilliseconds)
57 : Thread.Sleep(sleepInMilliseconds / 10);
58 : }
59 : }
60 : else
61 : Thread.SpinWait(4 << _count);
62 : _count = _count == int.MaxValue ? 10 : _count + 1;
63 : }
64 :
65 : /// <summary>
66 : /// Resets the spin counter.
67 : /// </summary>
68 1 : public void Reset()
69 : {
70 : _count = 0;
71 : }
72 :
73 : /// <summary>
74 : /// Spins until the specified condition is satisfied.
75 : /// </summary>
76 : /// <param name="condition">A delegate to be executed over and over until it returns true.</param>
77 : /// <param name="sleepInMilliseconds">The amount of milliseconds the thread will sleep for.</param>
78 : /// <exception cref="T:System.ArgumentNullException">The <paramref name="condition"/> argument is null.</exception>
79 1 : public static void SpinUntil(Func<bool> condition, short sleepInMilliseconds = 0)
80 : {
81 : SpinUntil(condition, -1, sleepInMilliseconds);
82 : }
83 :
84 : /// <summary>
85 : /// Spins until the specified condition is satisfied or until the specified timeout is expired.
86 : /// </summary>
87 : ///
88 : /// <returns>
89 : /// True if the condition is satisfied within the timeout; otherwise, false
90 : /// </returns>
91 : /// <param name="condition">A delegate to be executed over and over until it returns true.</param>
92 : /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.</param>
93 : /// <param name="sleepInMilliseconds">The amount of milliseconds the thread will sleep for.</param>
94 : /// <exception cref="T:System.ArgumentNullException">The <paramref name="condition"/> argument is null.</exception>
95 : /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than <see cref="F:System.Int32.MaxValue"/>.</exception>
96 1 : public static bool SpinUntil(Func<bool> condition, TimeSpan timeout, short sleepInMilliseconds = 0)
97 : {
98 : long num = (long) timeout.TotalMilliseconds;
99 : if (num < -1L || num > int.MaxValue)
100 : throw new ArgumentOutOfRangeException("timeout", timeout, "SpinWait_SpinUntil_TimeoutWrong");
101 : return SpinUntil(condition, (int) timeout.TotalMilliseconds, sleepInMilliseconds);
102 : }
103 :
104 : /// <summary>
105 : /// Spins until the specified condition is satisfied or until the specified timeout is expired.
106 : /// </summary>
107 : ///
108 : /// <returns>
109 : /// True if the condition is satisfied within the timeout; otherwise, false
110 : /// </returns>
111 : /// <param name="condition">A delegate to be executed over and over until it returns true.</param>
112 : /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param>
113 : /// <param name="sleepInMilliseconds">The amount of milliseconds the thread will sleep for.</param>
114 : /// <exception cref="T:System.ArgumentNullException">The <paramref name="condition"/> argument is null.</exception>
115 : /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an infinite time-out.</exception>
116 1 : public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout, short sleepInMilliseconds = 0)
117 : {
118 : if (millisecondsTimeout < -1)
119 : throw new ArgumentOutOfRangeException("millisecondsTimeout", millisecondsTimeout, "SpinWait_SpinUntil_TimeoutWrong");
120 : if (condition == null)
121 : throw new ArgumentNullException("condition", "SpinWait_SpinUntil_ArgumentNull");
122 : uint num = 0U;
123 : if (millisecondsTimeout != 0 && millisecondsTimeout != -1)
124 : num = TimeoutHelper.GetTime();
125 : SpinWait spinWait = new SpinWait();
126 : while (!condition())
127 : {
128 : if (millisecondsTimeout == 0)
129 : return false;
130 : spinWait.SpinOnce(sleepInMilliseconds);
131 : if (millisecondsTimeout != -1 && spinWait.NextSpinWillYield && millisecondsTimeout <= (TimeoutHelper.GetTime() - num))
132 : return false;
133 : }
134 : return true;
135 : }
136 :
137 : internal static class TimeoutHelper
138 : {
139 : /// <summary>
140 : /// Gets the number of milliseconds elapsed since the system started.
141 : /// </summary>
142 1 : public static uint GetTime()
143 : {
144 : return (uint)Environment.TickCount;
145 : }
146 :
147 : /// <summary>
148 : /// Does some interesting maths.
149 : /// </summary>
150 1 : public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout)
151 : {
152 : uint num1 = GetTime() - startTime;
153 : if (num1 > int.MaxValue)
154 : return 0;
155 : int num2 = originalWaitMillisecondsTimeout - (int)num1;
156 : if (num2 <= 0)
157 : return 0;
158 : return num2;
159 : }
160 : }
161 : }
162 : }
|