EventLoopSchedulerTest.cs 14 KB

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