|
1 | 1 | using System;
|
| 2 | +using System.Data.SqlTypes; |
2 | 3 | using System.Threading;
|
| 4 | +using System.Threading.Tasks; |
3 | 5 | using ReactiveDomain.Foundation;
|
4 | 6 | using ReactiveDomain.Messaging.Bus;
|
| 7 | +using ReactiveDomain.Util; |
5 | 8 | using Xunit;
|
6 | 9 |
|
7 | 10 |
|
@@ -40,47 +43,64 @@ public static void ArraySegmentEqual<T>(
|
40 | 43 | }
|
41 | 44 |
|
42 | 45 | /// <summary>
|
43 |
| - /// Asserts the given function will return false before the timeout expires. |
44 |
| - /// Repeatedly evaluates the function until false is returned or the timeout expires. |
45 |
| - /// Will return immediately when the condition is false. |
46 |
| - /// Evaluates the timeout every 10 msec until expired. |
47 |
| - /// Will not yield the thread by default, if yielding is required to resolve deadlocks set yieldThread to true. |
| 46 | + /// Asserts the given function will return false before the timeout expires. |
| 47 | + /// Repeatedly evaluates the function until false is returned or the timeout expires. |
| 48 | + /// Will return immediately when the condition is false. |
| 49 | + /// Evaluates the condition on an exponential back off up to 250 ms until timeout. |
| 50 | + /// Will yield the thread after each evaluation, use the hint yieldThread if it is known the function should yield before evaluation. |
48 | 51 | /// </summary>
|
49 | 52 | /// <param name="func">The function to evaluate.</param>
|
50 | 53 | /// <param name="timeout">A timeout in milliseconds. If not specified, defaults to 1000.</param>
|
51 | 54 | /// <param name="msg">A message to display if the condition is not satisfied.</param>
|
52 |
| - /// <param name="yieldThread">If true, the thread relinquishes the remainder of its time |
53 |
| - /// slice to any thread of equal priority that is ready to run.</param> |
| 55 | + /// <param name="yieldThread">Execution hint to yield thread before evaluation</param> |
54 | 56 | public static void IsOrBecomesFalse(Func<bool> func, int? timeout = null, string msg = null, bool yieldThread = false)
|
55 | 57 | {
|
56 | 58 | IsOrBecomesTrue(() => !func(), timeout, msg, yieldThread);
|
57 | 59 | }
|
58 | 60 |
|
59 | 61 | /// <summary>
|
60 |
| - /// Asserts the given function will return true before the timeout expires. |
61 |
| - /// Repeatedly evaluates the function until true is returned or the timeout expires. |
62 |
| - /// Will return immediately when the condition is true. |
63 |
| - /// Evaluates the timeout every 10 msec until expired. |
64 |
| - /// Will not yield the thread by default, if yielding is required to resolve deadlocks set yieldThread to true. |
| 62 | + /// Asserts the given function will return true before the timeout expires. |
| 63 | + /// Repeatedly evaluates the function until true is returned or the timeout expires. |
| 64 | + /// Will return immediately when the condition is true. |
| 65 | + /// Evaluates the condition on an exponential back off up to 250 ms until timeout. |
| 66 | + /// Will yield the thread after each evaluation, use the hint yieldThread if it is known the function should yield before evaluation. |
65 | 67 | /// </summary>
|
66 | 68 | /// <param name="func">The function to evaluate.</param>
|
67 | 69 | /// <param name="timeout">A timeout in milliseconds. If not specified, defaults to 1000.</param>
|
68 | 70 | /// <param name="msg">A message to display if the condition is not satisfied.</param>
|
69 |
| - /// <param name="yieldThread">If true, the thread relinquishes the remainder of its time |
70 |
| - /// slice to any thread of equal priority that is ready to run.</param> |
| 71 | + /// <param name="yieldThread">Execution hint to yield thread before evaluation</param> |
71 | 72 | public static void IsOrBecomesTrue(Func<bool> func, int? timeout = null, string msg = null, bool yieldThread = false)
|
72 | 73 | {
|
73 |
| - if (yieldThread) Thread.Sleep(0); |
74 |
| - if (!timeout.HasValue) timeout = 1000; |
75 |
| - var waitLoops = timeout / 10; |
| 74 | + if (!yieldThread && func() == true) |
| 75 | + { |
| 76 | + Assert.True(true, msg ?? ""); |
| 77 | + return; |
| 78 | + } |
| 79 | + |
76 | 80 | var result = false;
|
77 |
| - for (int i = 0; i < waitLoops; i++) |
| 81 | + var startTime = Environment.TickCount; //returns MS since machine start |
| 82 | + var endTime = startTime + (timeout ?? 1000); |
| 83 | + |
| 84 | + |
| 85 | + var delay = 1; |
| 86 | + while (true) |
78 | 87 | {
|
79 |
| - if (SpinWait.SpinUntil(func, 10)) |
| 88 | + using (var task = EvaluateAfterDelay(func, TimeSpan.FromMilliseconds(delay))) |
80 | 89 | {
|
81 |
| - result = true; |
82 |
| - break; |
| 90 | + task.Wait(); |
| 91 | + if (task.Result == true) |
| 92 | + { |
| 93 | + result = true; |
| 94 | + break; |
| 95 | + } |
83 | 96 | }
|
| 97 | + var now = Environment.TickCount; |
| 98 | + if ((endTime - now) <= 0) { break; } |
| 99 | + if (delay < 250) |
| 100 | + { |
| 101 | + delay = delay << 1; |
| 102 | + } |
| 103 | + delay = Math.Min(delay, endTime - now); |
84 | 104 | }
|
85 | 105 | Assert.True(result, msg ?? "");
|
86 | 106 | }
|
@@ -116,5 +136,17 @@ public static void AtLeastModelVersion(ReadModelBase readModel, int expectedVers
|
116 | 136 | {
|
117 | 137 | IsOrBecomesTrue(() => readModel.Version >= expectedVersion, timeout, msg, yieldThread);
|
118 | 138 | }
|
| 139 | + /// <summary> |
| 140 | + /// Evaluates the given function after the supplied delay. |
| 141 | + /// The current thread will yield at the start of the delay. |
| 142 | + /// </summary> |
| 143 | + /// <param name="func">The function to evaluate.</param> |
| 144 | + /// <param name="delay">A delay timeSpan.</param> |
| 145 | + /// <param name="cancellationToken">A token to cancel evaluation, TaskCanceledException will be thrown.</param> |
| 146 | + public static async Task<bool> EvaluateAfterDelay(Func<bool> func, TimeSpan delay, CancellationToken cancellationToken = default) |
| 147 | + { |
| 148 | + await Task.Delay(delay, cancellationToken); |
| 149 | + return func(); |
| 150 | + } |
119 | 151 | }
|
120 | 152 | }
|
0 commit comments