1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Linq; 7 using System.Threading; 8 using Xunit; 9 10 public partial class TimerFiringTests 11 { 12 internal const int MaxPositiveTimeoutInMs = 30000; 13 14 [Fact] Timer_Fires_After_DueTime_Ellapses()15 public void Timer_Fires_After_DueTime_Ellapses() 16 { 17 AutoResetEvent are = new AutoResetEvent(false); 18 19 using (var t = new Timer(new TimerCallback((object s) => 20 { 21 are.Set(); 22 }), null, TimeSpan.FromMilliseconds(250), TimeSpan.FromMilliseconds(Timeout.Infinite) /* not relevant */)) 23 { 24 Assert.True(are.WaitOne(TimeSpan.FromMilliseconds(MaxPositiveTimeoutInMs))); 25 } 26 } 27 28 [Fact] Timer_Fires_AndPassesStateThroughCallback()29 public void Timer_Fires_AndPassesStateThroughCallback() 30 { 31 AutoResetEvent are = new AutoResetEvent(false); 32 33 object state = new object(); 34 using (var t = new Timer(new TimerCallback((object s) => 35 { 36 Assert.Same(s, state); 37 are.Set(); 38 }), state, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(Timeout.Infinite) /* not relevant */)) 39 { 40 Assert.True(are.WaitOne(TimeSpan.FromMilliseconds(MaxPositiveTimeoutInMs))); 41 } 42 } 43 44 [Fact] Timer_Fires_AndPassesNullStateThroughCallback()45 public void Timer_Fires_AndPassesNullStateThroughCallback() 46 { 47 AutoResetEvent are = new AutoResetEvent(false); 48 49 using (var t = new Timer(new TimerCallback((object s) => 50 { 51 Assert.Null(s); 52 are.Set(); 53 }), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(Timeout.Infinite) /* not relevant */)) 54 { 55 Assert.True(are.WaitOne(TimeSpan.FromMilliseconds(MaxPositiveTimeoutInMs))); 56 } 57 } 58 59 [Fact] Timer_Fires_After_DueTime_AndOn_Period()60 public void Timer_Fires_After_DueTime_AndOn_Period() 61 { 62 int count = 0; 63 AutoResetEvent are = new AutoResetEvent(false); 64 65 using (var t = new Timer(new TimerCallback((object s) => 66 { 67 if (Interlocked.Increment(ref count) >= 2) 68 { 69 are.Set(); 70 } 71 }), null, TimeSpan.FromMilliseconds(250), TimeSpan.FromMilliseconds(50))) 72 { 73 Assert.True(are.WaitOne(TimeSpan.FromMilliseconds(MaxPositiveTimeoutInMs))); 74 } 75 } 76 77 [Fact] Timer_FiresOnlyOnce_OnDueTime_With_InfinitePeriod()78 public void Timer_FiresOnlyOnce_OnDueTime_With_InfinitePeriod() 79 { 80 int count = 0; 81 AutoResetEvent are = new AutoResetEvent(false); 82 83 using (var t = new Timer(new TimerCallback((object s) => 84 { 85 if (Interlocked.Increment(ref count) >= 2) 86 { 87 are.Set(); 88 } 89 }), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(Timeout.Infinite) /* not relevant */)) 90 { 91 Assert.False(are.WaitOne(TimeSpan.FromMilliseconds(250 /*enough for 2 fires + buffer*/))); 92 } 93 } 94 95 [Fact] Timer_CanDisposeSelfInCallback()96 public void Timer_CanDisposeSelfInCallback() 97 { 98 Timer t = null; 99 AutoResetEvent are = new AutoResetEvent(false); 100 TimerCallback tc = new TimerCallback((object o) => 101 { 102 t.Dispose(); 103 are.Set(); 104 }); 105 t = new Timer(tc, null, -1, -1); 106 t.Change(1, -1); 107 Assert.True(are.WaitOne(MaxPositiveTimeoutInMs)); 108 GC.KeepAlive(t); 109 } 110 111 [Fact] Timer_CanBeDisposedMultipleTimes()112 public void Timer_CanBeDisposedMultipleTimes() 113 { 114 // There's nothing to validate besides that we don't throw an exception, so rely on xunit 115 // to catch any exception that would be thrown and signal a test failure 116 TimerCallback tc = new TimerCallback((object o) => { }); 117 var t = new Timer(tc, null, 100, -1); 118 for (int i = 0; i < 10; i++) 119 t.Dispose(); 120 } 121 122 [Fact] NonRepeatingTimer_ThatHasAlreadyFired_CanChangeAndFireAgain()123 public void NonRepeatingTimer_ThatHasAlreadyFired_CanChangeAndFireAgain() 124 { 125 AutoResetEvent are = new AutoResetEvent(false); 126 TimerCallback tc = new TimerCallback((object o) => are.Set()); 127 using (var t = new Timer(tc, null, 1, Timeout.Infinite)) 128 { 129 Assert.True(are.WaitOne(MaxPositiveTimeoutInMs), "Should have received first timer event"); 130 Assert.False(are.WaitOne(500), "Should not have received a second timer event"); 131 t.Change(10, Timeout.Infinite); 132 Assert.True(are.WaitOne(MaxPositiveTimeoutInMs), "Should have received a second timer event after changing it"); 133 } 134 } 135 136 [Fact] MultpleTimers_PeriodicTimerIsntBlockedByBlockedCallback()137 public void MultpleTimers_PeriodicTimerIsntBlockedByBlockedCallback() 138 { 139 int callbacks = 2; 140 Barrier b = new Barrier(callbacks + 1); 141 Timer t = null; 142 t = new Timer(_ => 143 { 144 if (Interlocked.Decrement(ref callbacks) >= 0) 145 { 146 Assert.True(b.SignalAndWait(MaxPositiveTimeoutInMs)); 147 } 148 t.Dispose(); 149 }, null, -1, -1); 150 t.Change(1, 50); 151 152 Assert.True(b.SignalAndWait(MaxPositiveTimeoutInMs)); 153 GC.KeepAlive(t); 154 } 155 156 [Fact] ManyTimers_EachTimerDoesFire()157 public void ManyTimers_EachTimerDoesFire() 158 { 159 int maxTimers = 10000; 160 CountdownEvent ce = new CountdownEvent(maxTimers); 161 Timer[] timers = System.Linq.Enumerable.Range(0, maxTimers).Select(_ => new Timer(s => ce.Signal(), null, 100 /* enough time to wait on the are */, -1)).ToArray(); 162 try 163 { 164 Assert.True(ce.Wait(MaxPositiveTimeoutInMs), String.Format("Not all timers fired, {0} left of {1}", ce.CurrentCount, maxTimers)); 165 } 166 finally 167 { 168 foreach (Timer t in timers) 169 t.Dispose(); 170 } 171 } 172 173 [Fact] Timer_Constructor_CallbackOnly_Change()174 public void Timer_Constructor_CallbackOnly_Change() 175 { 176 var e = new ManualResetEvent(false); 177 using (var t = new Timer(s => e.Set())) 178 { 179 t.Change(0u, 0u); 180 Assert.True(e.WaitOne(MaxPositiveTimeoutInMs)); 181 } 182 } 183 184 [Fact] Timer_Dispose_WaitHandle_Negative()185 public void Timer_Dispose_WaitHandle_Negative() 186 { 187 Assert.Throws<ArgumentNullException>(() => new Timer(s => { }).Dispose(null)); 188 } 189 190 [Fact] Timer_Dispose_WaitHandle()191 public void Timer_Dispose_WaitHandle() 192 { 193 int tickCount = 0; 194 var someTicksPending = new ManualResetEvent(false); 195 var completeTicks = new ManualResetEvent(false); 196 var allTicksCompleted = new ManualResetEvent(false); 197 var t = 198 new Timer(s => 199 { 200 if (Interlocked.Increment(ref tickCount) == 2) 201 someTicksPending.Set(); 202 Assert.True(completeTicks.WaitOne(MaxPositiveTimeoutInMs)); 203 Interlocked.Decrement(ref tickCount); 204 }, null, 0, 1); 205 Assert.True(someTicksPending.WaitOne(MaxPositiveTimeoutInMs)); 206 completeTicks.Set(); 207 t.Dispose(allTicksCompleted); 208 Assert.True(allTicksCompleted.WaitOne(MaxPositiveTimeoutInMs)); 209 Assert.Equal(0, tickCount); 210 Assert.Throws<ObjectDisposedException>(() => t.Change(0, 0)); 211 } 212 } 213