EventLoopSchedulerTest.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Reactive.Concurrency;
  6. using System.Reactive.Disposables;
  7. using System.Threading;
  8. using Microsoft.Reactive.Testing;
  9. using Xunit;
  10. #if STRESS
  11. using ReactiveTests.Stress.Schedulers;
  12. #endif
  13. namespace ReactiveTests.Tests
  14. {
  15. public class EventLoopSchedulerTest
  16. {
  17. [Fact]
  18. public void EventLoop_ArgumentChecking()
  19. {
  20. var el = new EventLoopScheduler();
  21. #if !NO_THREAD
  22. ReactiveAssert.Throws<ArgumentNullException>(() => new EventLoopScheduler(null));
  23. #endif
  24. ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule<int>(42, default(Func<IScheduler, int, IDisposable>)));
  25. ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule<int>(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
  26. ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule<int>(42, TimeSpan.Zero, default(Func<IScheduler, int, IDisposable>)));
  27. ReactiveAssert.Throws<ArgumentNullException>(() => el.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
  28. ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => el.SchedulePeriodic(42, TimeSpan.FromSeconds(-1), _ => _));
  29. }
  30. [Fact]
  31. public void EventLoop_Now()
  32. {
  33. var res = new EventLoopScheduler().Now - DateTime.Now;
  34. Assert.True(res.Seconds < 1);
  35. }
  36. [Fact]
  37. public void EventLoop_ScheduleAction()
  38. {
  39. var ran = false;
  40. var gate = new Semaphore(0, 1);
  41. var el = new EventLoopScheduler();
  42. el.Schedule(() => { ran = true;
  43. gate.Release(); });
  44. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  45. Assert.True(ran);
  46. }
  47. #if !NO_THREAD
  48. [Fact]
  49. public void EventLoop_DifferentThread()
  50. {
  51. var id = default(int);
  52. var gate = new Semaphore(0, 1);
  53. var el = new EventLoopScheduler();
  54. el.Schedule(() =>
  55. {
  56. id = Thread.CurrentThread.ManagedThreadId;
  57. gate.Release();
  58. });
  59. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  60. Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, id);
  61. }
  62. #endif
  63. [Fact]
  64. public void EventLoop_ScheduleOrderedActions()
  65. {
  66. var results = new List<int>();
  67. var gate = new Semaphore(0, 1);
  68. var el = new EventLoopScheduler();
  69. el.Schedule(() => results.Add(0));
  70. el.Schedule(() =>
  71. {
  72. results.Add(1);
  73. gate.Release();
  74. });
  75. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  76. results.AssertEqual(0, 1);
  77. }
  78. [Fact]
  79. public void EventLoop_SchedulerDisposed()
  80. {
  81. var d = 0;
  82. var e = new ManualResetEvent(false);
  83. var f = new ManualResetEvent(false);
  84. var results = new List<int>();
  85. var el = new EventLoopScheduler();
  86. el.Schedule(() => results.Add(0));
  87. el.Schedule(() =>
  88. {
  89. el.Dispose();
  90. e.Set();
  91. results.Add(1);
  92. try
  93. {
  94. el.Schedule(() => { throw new Exception("Should be disposed!"); });
  95. f.Set();
  96. }
  97. catch (ObjectDisposedException)
  98. {
  99. // BREAKING CHANGE v2 > v1.x - New exception behavior.
  100. Interlocked.Increment(ref d);
  101. f.Set();
  102. }
  103. });
  104. e.WaitOne();
  105. try
  106. {
  107. el.Schedule(() => results.Add(2));
  108. }
  109. catch (ObjectDisposedException)
  110. {
  111. // BREAKING CHANGE v2 > v1.x - New exception behavior.
  112. Interlocked.Increment(ref d);
  113. }
  114. f.WaitOne();
  115. results.AssertEqual(0, 1);
  116. Assert.Equal(2, d);
  117. }
  118. [Fact]
  119. public void EventLoop_ScheduleTimeOrderedActions()
  120. {
  121. var results = new List<int>();
  122. var gate = new Semaphore(0, 1);
  123. var el = new EventLoopScheduler();
  124. el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1));
  125. el.Schedule(TimeSpan.FromMilliseconds(100), () =>
  126. {
  127. results.Add(0);
  128. gate.Release();
  129. });
  130. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  131. results.AssertEqual(1, 0);
  132. }
  133. [Fact]
  134. public void EventLoop_ScheduleOrderedAndTimedActions()
  135. {
  136. var results = new List<int>();
  137. var gate = new Semaphore(0, 1);
  138. var el = new EventLoopScheduler();
  139. el.Schedule(() => results.Add(1));
  140. el.Schedule(TimeSpan.FromMilliseconds(100), () =>
  141. {
  142. results.Add(0);
  143. gate.Release();
  144. });
  145. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  146. results.AssertEqual(1, 0);
  147. }
  148. [Fact]
  149. public void EventLoop_ScheduleTimeOrderedInFlightActions()
  150. {
  151. var results = new List<int>();
  152. var gate = new Semaphore(0, 1);
  153. var el = new EventLoopScheduler();
  154. el.Schedule(TimeSpan.FromMilliseconds(100), () =>
  155. {
  156. results.Add(0);
  157. el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1));
  158. el.Schedule(TimeSpan.FromMilliseconds(100), ()=>
  159. {
  160. results.Add(2);
  161. gate.Release();
  162. });
  163. });
  164. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  165. results.AssertEqual(0, 1, 2);
  166. }
  167. [Fact]
  168. public void EventLoop_ScheduleTimeAndOrderedInFlightActions()
  169. {
  170. var results = new List<int>();
  171. var gate = new Semaphore(0, 1);
  172. var el = new EventLoopScheduler();
  173. el.Schedule(TimeSpan.FromMilliseconds(100), () =>
  174. {
  175. results.Add(0);
  176. el.Schedule(() => results.Add(4));
  177. el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1));
  178. el.Schedule(TimeSpan.FromMilliseconds(100), () =>
  179. {
  180. results.Add(2);
  181. gate.Release();
  182. });
  183. });
  184. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  185. results.AssertEqual(0, 4, 1, 2);
  186. }
  187. [Fact]
  188. public void EventLoop_ScheduleActionNested()
  189. {
  190. var ran = false;
  191. var el = new EventLoopScheduler();
  192. var gate = new Semaphore(0, 1);
  193. el.Schedule(() => el.Schedule(() => { ran = true;
  194. gate.Release(); }));
  195. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  196. Assert.True(ran);
  197. }
  198. #if !SILVERLIGHT
  199. [Fact(Skip ="")]
  200. public void EventLoop_ScheduleActionDue()
  201. {
  202. var ran = false;
  203. var el = new EventLoopScheduler();
  204. var sw = new Stopwatch();
  205. var gate = new Semaphore(0, 1);
  206. sw.Start();
  207. el.Schedule(TimeSpan.FromSeconds(0.2), () => {
  208. ran = true;
  209. sw.Stop();
  210. gate.Release();
  211. });
  212. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  213. Assert.True(ran, "ran");
  214. Assert.True(sw.ElapsedMilliseconds > 180, "due " + sw.ElapsedMilliseconds);
  215. }
  216. [Fact(Skip = "")]
  217. public void EventLoop_ScheduleActionDueNested()
  218. {
  219. var ran = false;
  220. var el = new EventLoopScheduler();
  221. var gate = new Semaphore(0, 1);
  222. var sw = new Stopwatch();
  223. sw.Start();
  224. el.Schedule(TimeSpan.FromSeconds(0.2), () =>
  225. {
  226. sw.Stop();
  227. sw.Start();
  228. el.Schedule(TimeSpan.FromSeconds(0.2), () =>
  229. {
  230. sw.Stop();
  231. ran = true;
  232. gate.Release();
  233. });
  234. });
  235. Assert.True(gate.WaitOne(TimeSpan.FromSeconds(2)));
  236. Assert.True(ran, "ran");
  237. Assert.True(sw.ElapsedMilliseconds > 380, "due " + sw.ElapsedMilliseconds);
  238. }
  239. #endif
  240. #if !NO_PERF
  241. #if !NO_STOPWATCH
  242. [Fact]
  243. public void Stopwatch()
  244. {
  245. StopwatchTest.Run(new EventLoopScheduler());
  246. }
  247. #endif
  248. #endif
  249. #if !NO_CDS
  250. [Fact]
  251. public void EventLoop_Immediate()
  252. {
  253. var M = 1000;
  254. var N = 4;
  255. for (int i = 0; i < N; i++)
  256. {
  257. for (int j = 1; j <= M; j *= 10)
  258. {
  259. using (var e = new EventLoopScheduler())
  260. {
  261. using (var d = new CompositeDisposable())
  262. {
  263. var cd = new CountdownEvent(j);
  264. for (int k = 0; k < j; k++)
  265. d.Add(e.Schedule(() => cd.Signal()));
  266. if (!cd.Wait(10000))
  267. Assert.True(false, "j = " + j);
  268. }
  269. }
  270. }
  271. }
  272. }
  273. [Fact]
  274. public void EventLoop_TimeCollisions()
  275. {
  276. var M = 1000;
  277. var N = 4;
  278. for (int i = 0; i < N; i++)
  279. {
  280. for (int j = 1; j <= M; j *= 10)
  281. {
  282. using (var e = new EventLoopScheduler())
  283. {
  284. using (var d = new CompositeDisposable())
  285. {
  286. var cd = new CountdownEvent(j);
  287. for (int k = 0; k < j; k++)
  288. d.Add(e.Schedule(TimeSpan.FromMilliseconds(100), () => cd.Signal()));
  289. if (!cd.Wait(10000))
  290. Assert.True(false, "j = " + j);
  291. }
  292. }
  293. }
  294. }
  295. }
  296. [Fact]
  297. public void EventLoop_Spread()
  298. {
  299. var M = 1000;
  300. var N = 4;
  301. for (int i = 0; i < N; i++)
  302. {
  303. for (int j = 1; j <= M; j *= 10)
  304. {
  305. using (var e = new EventLoopScheduler())
  306. {
  307. using (var d = new CompositeDisposable())
  308. {
  309. var cd = new CountdownEvent(j);
  310. for (int k = 0; k < j; k++)
  311. d.Add(e.Schedule(TimeSpan.FromMilliseconds(k), () => cd.Signal()));
  312. if (!cd.Wait(10000))
  313. Assert.True(false, "j = " + j);
  314. }
  315. }
  316. }
  317. }
  318. }
  319. #endif
  320. [Fact]
  321. public void EventLoop_Periodic()
  322. {
  323. var n = 0;
  324. using (var s = new EventLoopScheduler())
  325. {
  326. var e = new ManualResetEvent(false);
  327. var d = s.SchedulePeriodic(TimeSpan.FromMilliseconds(25), () =>
  328. {
  329. if (Interlocked.Increment(ref n) == 10)
  330. e.Set();
  331. });
  332. if (!e.WaitOne(10000))
  333. Assert.True(false);
  334. d.Dispose();
  335. }
  336. }
  337. #if STRESS
  338. [Fact]
  339. public void EventLoop_Stress()
  340. {
  341. EventLoop.NoSemaphoreFullException();
  342. }
  343. #endif
  344. #if !NO_CDS && DESKTOPCLR
  345. [Fact]
  346. public void EventLoop_CorrectWorkStealing()
  347. {
  348. const int workItemCount = 100;
  349. var failureCount = 0;
  350. var countdown = new CountdownEvent(workItemCount);
  351. var dueTime = DateTimeOffset.Now + TimeSpan.FromSeconds(1);
  352. using (var d = new CompositeDisposable())
  353. {
  354. for (var i = 0; i < workItemCount; i++)
  355. {
  356. var scheduler = new EventLoopScheduler();
  357. scheduler.Schedule(() =>
  358. {
  359. var schedulerThread = Thread.CurrentThread;
  360. scheduler.Schedule(dueTime, () =>
  361. {
  362. if (Thread.CurrentThread != schedulerThread)
  363. {
  364. Interlocked.Increment(ref failureCount);
  365. }
  366. countdown.Signal();
  367. });
  368. });
  369. d.Add(scheduler);
  370. }
  371. countdown.Wait();
  372. }
  373. Assert.Equal(0, failureCount);
  374. }
  375. #endif
  376. }
  377. }