/Rx.NET/Source/Tests.System.Reactive/Tests/Concurrency/ThreadPoolSchedulerTest.cs
C# | 332 lines | 271 code | 60 blank | 1 comment | 15 complexity | 337ac6ab18c950369b9f69aaf0e0d042 MD5 | raw file
1// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
3#if !NO_THREAD
4using System;
5using System.Collections.Generic;
6using System.Linq;
7using System.Reactive.Concurrency;
8using System.Reactive.Disposables;
9using System.Threading;
10using Microsoft.Reactive.Testing;
11using Microsoft.VisualStudio.TestTools.UnitTesting;
12
13namespace ReactiveTests.Tests
14{
15 [TestClass]
16 public class ThreadPoolSchedulerTest
17 {
18 [TestMethod]
19 public void Schedule_ArgumentChecking()
20 {
21 ReactiveAssert.Throws<ArgumentNullException>(() => ThreadPoolScheduler.Instance.Schedule<int>(42, default(Func<IScheduler, int, IDisposable>)));
22 ReactiveAssert.Throws<ArgumentNullException>(() => ThreadPoolScheduler.Instance.Schedule<int>(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
23 ReactiveAssert.Throws<ArgumentNullException>(() => ThreadPoolScheduler.Instance.Schedule<int>(42, TimeSpan.Zero, default(Func<IScheduler, int, IDisposable>)));
24 }
25
26 [TestMethod]
27 public void Get_Now()
28 {
29 var res = ThreadPoolScheduler.Instance.Now - DateTime.Now;
30 Assert.IsTrue(res.Seconds < 1);
31 }
32
33 [TestMethod]
34 public void ScheduleAction()
35 {
36 var id = Thread.CurrentThread.ManagedThreadId;
37 var nt = ThreadPoolScheduler.Instance;
38 var evt = new ManualResetEvent(false);
39 nt.Schedule(() => { Assert.AreNotEqual(id, Thread.CurrentThread.ManagedThreadId); evt.Set(); });
40 evt.WaitOne();
41 }
42
43#if !NO_CDS
44 [TestMethod]
45 public void ProperRooting_NoGC_SingleShot()
46 {
47 var cts = new CancellationTokenSource();
48
49 new Thread(() =>
50 {
51 while (!cts.IsCancellationRequested)
52 {
53 Thread.Sleep(50);
54 GC.Collect();
55 GC.WaitForPendingFinalizers();
56 }
57 }).Start();
58
59 var tp = ThreadPoolScheduler.Instance;
60 var N = 100;
61 var cd = new CountdownEvent(N);
62 for (int i = 0; i < N; i++)
63 {
64 tp.Schedule(TimeSpan.FromMilliseconds(100 + i), () => { cd.Signal(); });
65 }
66
67 Assert.IsTrue(cd.Wait(TimeSpan.FromMinutes(1)));
68 cts.Cancel();
69 }
70
71 [TestMethod]
72 public void ProperRooting_NoGC_Periodic()
73 {
74 var cts = new CancellationTokenSource();
75
76 new Thread(() =>
77 {
78 while (!cts.IsCancellationRequested)
79 {
80 Thread.Sleep(50);
81 GC.Collect();
82 GC.WaitForPendingFinalizers();
83 }
84 }).Start();
85
86 var tp = ThreadPoolScheduler.Instance;
87 var N = 5;
88 var e = new ManualResetEvent(false);
89 var n = 0;
90 var d = tp.SchedulePeriodic(TimeSpan.FromMilliseconds(80), () => { if (Interlocked.Increment(ref n) == N) e.Set(); });
91
92 Assert.IsTrue(e.WaitOne(TimeSpan.FromMinutes(1)));
93
94 d.Dispose();
95
96 cts.Cancel();
97 }
98#endif
99
100#if !SILVERLIGHT
101 [TestMethod]
102 public void ScheduleActionDueRelative()
103 {
104 var id = Thread.CurrentThread.ManagedThreadId;
105 var nt = ThreadPoolScheduler.Instance;
106 var evt = new ManualResetEvent(false);
107 nt.Schedule(TimeSpan.FromSeconds(0.2), () => { Assert.AreNotEqual(id, Thread.CurrentThread.ManagedThreadId); evt.Set(); });
108 evt.WaitOne();
109 }
110
111 [TestMethod]
112 public void ScheduleActionDue0()
113 {
114 var id = Thread.CurrentThread.ManagedThreadId;
115 var nt = ThreadPoolScheduler.Instance;
116 var evt = new ManualResetEvent(false);
117 nt.Schedule(TimeSpan.FromTicks(0), () => { Assert.AreNotEqual(id, Thread.CurrentThread.ManagedThreadId); evt.Set(); });
118 evt.WaitOne();
119 }
120
121 [TestMethod]
122 public void ScheduleActionDueAbsolute()
123 {
124 var id = Thread.CurrentThread.ManagedThreadId;
125 var nt = ThreadPoolScheduler.Instance;
126 var evt = new ManualResetEvent(false);
127 nt.Schedule(DateTimeOffset.UtcNow + TimeSpan.FromSeconds(0.2), () => { Assert.AreNotEqual(id, Thread.CurrentThread.ManagedThreadId); evt.Set(); });
128 evt.WaitOne();
129 }
130#endif
131
132 [TestMethod]
133 public void ScheduleActionCancel()
134 {
135 var id = Thread.CurrentThread.ManagedThreadId;
136 var nt = ThreadPoolScheduler.Instance;
137 var set = false;
138 var d = nt.Schedule(TimeSpan.FromSeconds(0.2), () => { Assert.Fail(); set = true; });
139 d.Dispose();
140 Thread.Sleep(400);
141 Assert.IsFalse(set);
142 }
143
144#if !NO_PERF
145
146 [TestMethod]
147 public void ScheduleLongRunning_ArgumentChecking()
148 {
149 ReactiveAssert.Throws<ArgumentNullException>(() => ThreadPoolScheduler.Instance.ScheduleLongRunning<int>(42, default(Action<int, ICancelable>)));
150 }
151
152 [TestMethod]
153 public void ScheduleLongRunning()
154 {
155 var id = Thread.CurrentThread.ManagedThreadId;
156 var nt = ThreadPoolScheduler.Instance;
157 var evt = new ManualResetEvent(false);
158 nt.ScheduleLongRunning(42, (x, cancel) => { Assert.AreNotEqual(id, Thread.CurrentThread.ManagedThreadId); evt.Set(); });
159 evt.WaitOne();
160 }
161
162 [TestMethod]
163 public void ScheduleLongRunningCancel()
164 {
165 var nt = ThreadPoolScheduler.Instance;
166
167 var started = new ManualResetEvent(false);
168 var stopped = new ManualResetEvent(false);
169
170 var n = 0;
171
172 var d = nt.ScheduleLongRunning(42, (x, cancel) =>
173 {
174 for (n = 0; !cancel.IsDisposed; n++)
175 {
176 if (n == 10)
177 started.Set();
178 }
179
180 stopped.Set();
181 });
182
183 started.WaitOne();
184 d.Dispose();
185
186 stopped.WaitOne();
187 Assert.IsTrue(n >= 10);
188 }
189
190#if !NO_STOPWATCH
191
192 [TestMethod]
193 public void Stopwatch()
194 {
195 var nt = ThreadPoolScheduler.Instance;
196
197 var sw = nt.StartStopwatch();
198
199 var s0 = sw.Elapsed.Ticks;
200 Thread.Sleep(10);
201 var s1 = sw.Elapsed.Ticks;
202
203 Assert.IsTrue(s1 > s0);
204 }
205
206#endif
207
208 [TestMethod]
209 public void Periodic_ArgumentChecking()
210 {
211 ReactiveAssert.Throws<ArgumentNullException>(() => ThreadPoolScheduler.Instance.SchedulePeriodic(0, TimeSpan.FromSeconds(1), null));
212 ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => ThreadPoolScheduler.Instance.SchedulePeriodic(0, TimeSpan.FromSeconds(-1), _ => _));
213 }
214
215 [TestMethod]
216 public void Periodic_Regular()
217 {
218 Periodic_Impl(TimeSpan.FromMilliseconds(25));
219 }
220
221 [TestMethod]
222 public void Periodic_Zero()
223 {
224 Periodic_Impl(TimeSpan.Zero);
225 }
226
227 private void Periodic_Impl(TimeSpan period)
228 {
229 var gate = new object();
230 var n = 0;
231 var e = new ManualResetEvent(false);
232
233 var lst = new List<int>();
234
235 var d = ThreadPoolScheduler.Instance.SchedulePeriodic(0, period, x =>
236 {
237 lock (gate)
238 {
239 if (n++ == 10)
240 e.Set();
241 }
242
243 lst.Add(x);
244 return x + 1;
245 });
246
247 e.WaitOne();
248 d.Dispose();
249
250 var m = default(int);
251 var k = default(int);
252
253 var i = 0;
254 do
255 {
256 lock (gate)
257 m = n;
258
259 Thread.Sleep(50);
260
261 lock (gate)
262 k = n;
263 } while (m != k && i++ < 10); // Wait for Dispose to reach the timer; should be almost instantaneous due to nop'ing out of the action.
264
265 Assert.AreNotEqual(10, i);
266
267 var res = lst.ToArray();
268 Assert.IsTrue(res.Length >= 10);
269 Assert.IsTrue(res.Take(10).SequenceEqual(Enumerable.Range(0, 10)));
270 }
271
272 [TestMethod]
273 public void Periodic_NonReentrant()
274 {
275 var n = 0;
276 var fail = false;
277
278 var d = ThreadPoolScheduler.Instance.SchedulePeriodic(0, TimeSpan.FromMilliseconds(50), x =>
279 {
280 try
281 {
282 if (Interlocked.Increment(ref n) > 1) // Without an AsyncLock this would fail.
283 fail = true;
284
285 Thread.Sleep(100);
286
287 return x + 1;
288 }
289 finally
290 {
291 Interlocked.Decrement(ref n);
292 }
293 });
294
295 Thread.Sleep(500);
296 d.Dispose();
297
298 Assert.IsFalse(fail);
299 }
300
301#endif
302
303#if DESKTOPCLR
304 [TestMethod]
305 public void No_ThreadPool_Starvation_Dispose()
306 {
307 var bwt = default(int);
308 var bio = default(int);
309 ThreadPool.GetAvailableThreads(out bwt, out bio);
310
311 var N = Environment.ProcessorCount * 2;
312
313 for (int i = 0; i < N; i++)
314 {
315 var e = new ManualResetEvent(false);
316 var f = new ManualResetEvent(false);
317 var d = ThreadPoolScheduler.Instance.Schedule(TimeSpan.FromMilliseconds(1), () => { e.Set(); f.WaitOne(); });
318 e.WaitOne();
319 d.Dispose();
320 f.Set();
321 }
322
323 var ewt = default(int);
324 var eio = default(int);
325 ThreadPool.GetAvailableThreads(out ewt, out eio);
326
327 Assert.IsFalse(bwt - ewt >= N);
328 }
329#endif
330 }
331}
332#endif