WaitHandleTest.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. //
  2. // WaitHandleTest.cs
  3. //
  4. // Author:
  5. // Sebastien Pouliot <[email protected]>
  6. //
  7. // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Diagnostics;
  30. using System.Collections.Generic;
  31. using System.Threading;
  32. using NUnit.Framework;
  33. namespace MonoTests.System.Threading {
  34. [TestFixture]
  35. public class WaitHandleTest {
  36. TimeSpan Infinite = new TimeSpan (-10000); // -10000 ticks == -1 ms
  37. TimeSpan SmallNegative = new TimeSpan (-2); // between 0 and -1.0 (infinite) ms
  38. TimeSpan Negative = new TimeSpan (-20000); // really negative
  39. WaitHandle [] TooLarge = new Mutex [65];
  40. WaitHandle [] Empty = new Mutex [1];
  41. WaitHandle [] Single = new Mutex [1] { new Mutex (true) };
  42. [Test]
  43. [ExpectedException (typeof (ArgumentNullException))]
  44. public void WaitAny_WaitHandle_Null ()
  45. {
  46. WaitHandle.WaitAny (null);
  47. }
  48. [Test]
  49. [ExpectedException (typeof (NotSupportedException))]
  50. public void WaitAny_WaitHandle_TooLarge ()
  51. {
  52. WaitHandle.WaitAny (TooLarge);
  53. }
  54. [Test]
  55. [ExpectedException (typeof (ArgumentNullException))]
  56. public void WaitAny_WaitHandle_Empty ()
  57. {
  58. WaitHandle.WaitAny (Empty);
  59. }
  60. [Test]
  61. public void WaitAny_WaitHandle ()
  62. {
  63. Assert.AreEqual (0, WaitHandle.WaitAny (Single), "WaitAny");
  64. }
  65. [Test]
  66. [ExpectedException (typeof (ArgumentNullException))]
  67. public void WaitAny_WaitHandleNull_Int ()
  68. {
  69. WaitHandle.WaitAny (null, -1);
  70. }
  71. [Test]
  72. [ExpectedException (typeof (NotSupportedException))]
  73. public void WaitAny_WaitHandle_TooLarge_Int ()
  74. {
  75. WaitHandle.WaitAny (TooLarge, -1);
  76. }
  77. [Test]
  78. [ExpectedException (typeof (ArgumentNullException))]
  79. public void WaitAny_WaitHandle_Empty_Int ()
  80. {
  81. WaitHandle.WaitAny (Empty, -1);
  82. }
  83. [Test]
  84. public void WaitAny_WaitHandle_Int ()
  85. {
  86. // -1 is infinite
  87. Assert.AreEqual (0, WaitHandle.WaitAny (Single, -1), "WaitAny");
  88. }
  89. [Test]
  90. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  91. public void WaitAny_WaitHandle_Int_Negative ()
  92. {
  93. Assert.AreEqual (0, WaitHandle.WaitAny (Single, -2), "WaitAny");
  94. }
  95. [Test]
  96. [ExpectedException (typeof (ArgumentNullException))]
  97. public void WaitAny_WaitHandleNull_TimeSpan ()
  98. {
  99. WaitHandle.WaitAny (null, Infinite);
  100. }
  101. [Test]
  102. [ExpectedException (typeof (NotSupportedException))]
  103. public void WaitAny_WaitHandle_TooLarge_TimeSpan ()
  104. {
  105. WaitHandle.WaitAny (TooLarge, Infinite);
  106. }
  107. [Test]
  108. [ExpectedException (typeof (ArgumentNullException))]
  109. public void WaitAny_WaitHandle_Empty_TimeSpan ()
  110. {
  111. WaitHandle.WaitAny (Empty, Infinite);
  112. }
  113. [Test]
  114. public void WaitAny_WaitHandle_TimeSpan ()
  115. {
  116. Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite");
  117. Assert.AreEqual (0, WaitHandle.WaitAny (Single, Infinite), "WaitAny-Infinite");
  118. Assert.AreEqual (0, WaitHandle.WaitAny (Single, SmallNegative), "WaitAny-SmallNegative");
  119. }
  120. [Test]
  121. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  122. public void WaitAny_WaitHandle_TimeSpan_Negative ()
  123. {
  124. Assert.AreEqual (0, WaitHandle.WaitAny (Single, Negative), "WaitAny");
  125. }
  126. [Test]
  127. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  128. public void WaitAny_WaitHandle_TimeSpan_MaxValue ()
  129. {
  130. Assert.AreEqual (0, WaitHandle.WaitAny (Single, TimeSpan.MaxValue), "WaitAny");
  131. }
  132. [Test]
  133. [ExpectedException (typeof (ArgumentNullException))]
  134. public void WaitAll_WaitHandle_Null ()
  135. {
  136. WaitHandle.WaitAll (null);
  137. }
  138. [Test]
  139. [ExpectedException (typeof (NotSupportedException))]
  140. public void WaitAll_WaitHandle_TooLarge ()
  141. {
  142. WaitHandle.WaitAll (TooLarge);
  143. }
  144. [Test]
  145. [ExpectedException (typeof (ArgumentNullException))]
  146. public void WaitAll_WaitHandle_Empty ()
  147. {
  148. WaitHandle.WaitAll (Empty);
  149. }
  150. [Test]
  151. public void WaitAll_WaitHandle ()
  152. {
  153. Assert.IsTrue (WaitHandle.WaitAll (Single), "WaitAll");
  154. }
  155. [Test]
  156. [ExpectedException (typeof (ArgumentNullException))]
  157. public void WaitAll_WaitHandleNull_Int ()
  158. {
  159. WaitHandle.WaitAll (null, -1);
  160. }
  161. [Test]
  162. [ExpectedException (typeof (NotSupportedException))]
  163. public void WaitAll_WaitHandle_TooLarge_Int ()
  164. {
  165. WaitHandle.WaitAll (TooLarge, -1);
  166. }
  167. [Test]
  168. [ExpectedException (typeof (ArgumentNullException))]
  169. public void WaitAll_WaitHandle_Empty_Int ()
  170. {
  171. WaitHandle.WaitAll (Empty, -1);
  172. }
  173. [Test]
  174. public void WaitAll_WaitHandle_Int ()
  175. {
  176. // -1 is infinite
  177. Assert.IsTrue (WaitHandle.WaitAll (Single, -1), "WaitAll");
  178. }
  179. [Test]
  180. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  181. public void WaitAll_WaitHandle_Int_Negative ()
  182. {
  183. Assert.IsTrue (WaitHandle.WaitAll (Single, -2), "WaitAll");
  184. }
  185. [Test]
  186. [ExpectedException (typeof (ArgumentNullException))]
  187. public void WaitAll_WaitHandleNull_TimeSpan ()
  188. {
  189. WaitHandle.WaitAll (null, Infinite);
  190. }
  191. [Test]
  192. [ExpectedException (typeof (NotSupportedException))]
  193. public void WaitAll_WaitHandle_TooLarge_TimeSpan ()
  194. {
  195. WaitHandle.WaitAll (TooLarge, Infinite);
  196. }
  197. [Test]
  198. [ExpectedException (typeof (ArgumentNullException))]
  199. public void WaitAll_WaitHandle_Empty_TimeSpan ()
  200. {
  201. WaitHandle.WaitAll (Empty, Infinite);
  202. }
  203. [Test]
  204. public void WaitAll_WaitHandle_TimeSpan ()
  205. {
  206. Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite");
  207. Assert.IsTrue (WaitHandle.WaitAll (Single, Infinite), "WaitAll-Infinite");
  208. Assert.IsTrue (WaitHandle.WaitAll (Single, SmallNegative), "WaitAll-SmallNegative");
  209. }
  210. [Test]
  211. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  212. public void WaitAll_WaitHandle_TimeSpan_Negative ()
  213. {
  214. Assert.IsTrue (WaitHandle.WaitAll (Single, Negative), "WaitAll");
  215. }
  216. [Test]
  217. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  218. public void WaitAll_WaitHandle_TimeSpan_MaxValue ()
  219. {
  220. Assert.IsTrue (WaitHandle.WaitAll (Single, TimeSpan.MaxValue), "WaitAll");
  221. }
  222. [Test]
  223. public void WaitOne ()
  224. {
  225. Assert.IsTrue (Single [0].WaitOne (), "WaitOne");
  226. }
  227. [Test]
  228. public void WaitOne_Int ()
  229. {
  230. // -1 is infinite
  231. Assert.IsTrue (Single [0].WaitOne (-1), "WaitOne");
  232. }
  233. [Test]
  234. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  235. public void WaitOne_Int_Negative ()
  236. {
  237. Assert.IsTrue (Single [0].WaitOne (-2), "WaitOne");
  238. }
  239. [Test]
  240. public void WaitOne_TimeSpan ()
  241. {
  242. Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite");
  243. Assert.IsTrue (Single [0].WaitOne (Infinite), "WaitOne-Infinite");
  244. Assert.IsTrue (Single [0].WaitOne (SmallNegative), "WaitOne-SmallNegative");
  245. }
  246. [Test]
  247. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  248. public void WaitOne_TimeSpan_Negative ()
  249. {
  250. Assert.IsTrue (Single [0].WaitOne (Negative), "WaitOne");
  251. }
  252. [Test]
  253. [ExpectedException (typeof (ArgumentOutOfRangeException))]
  254. public void WaitOne_TimeSpan_MaxValue ()
  255. {
  256. Assert.IsTrue (Single [0].WaitOne (TimeSpan.MaxValue), "WaitOne");
  257. }
  258. [Test]
  259. [ExpectedException (typeof (ArgumentNullException))]
  260. public void WaitAll_Empty ()
  261. {
  262. WaitHandle.WaitAll (new WaitHandle [0]);
  263. }
  264. [Test]
  265. [ExpectedException (typeof (ArgumentException))]
  266. public void WaitAny_Empty ()
  267. {
  268. WaitHandle.WaitAny (new WaitHandle [0]);
  269. }
  270. [Test]
  271. [Category ("MultiThreaded")]
  272. public void InterrupedWaitAny ()
  273. {
  274. using (var m1 = new Mutex (true)) {
  275. using (var m2 = new Mutex (true)) {
  276. using (var done = new ManualResetEvent (false)) {
  277. var thread = new Thread (() =>
  278. {
  279. try {
  280. WaitHandle.WaitAny (new WaitHandle [] { m1, m2 });
  281. } catch (ThreadInterruptedException) {
  282. done.Set ();
  283. }
  284. });
  285. thread.Start ();
  286. Thread.Sleep (100); // wait a bit so the thread can enter its wait
  287. thread.Interrupt ();
  288. Assert.IsTrue (thread.Join (1000), "Join");
  289. Assert.IsTrue (done.WaitOne (1000), "done");
  290. m1.ReleaseMutex ();
  291. m2.ReleaseMutex ();
  292. }
  293. }
  294. }
  295. }
  296. [Test]
  297. [Category ("MultiThreaded")]
  298. public void InterrupedWaitAll ()
  299. {
  300. using (var m1 = new Mutex (true)) {
  301. using (var m2 = new Mutex (true)) {
  302. using (var done = new ManualResetEvent (false)) {
  303. var thread = new Thread (() =>
  304. {
  305. try {
  306. WaitHandle.WaitAll (new WaitHandle [] { m1, m2 });
  307. } catch (ThreadInterruptedException) {
  308. done.Set ();
  309. }
  310. });
  311. thread.Start ();
  312. Thread.Sleep (100); // wait a bit so the thread can enter its wait
  313. thread.Interrupt ();
  314. Assert.IsTrue (thread.Join (1000), "Join");
  315. Assert.IsTrue (done.WaitOne (1000), "done");
  316. m1.ReleaseMutex ();
  317. m2.ReleaseMutex ();
  318. }
  319. }
  320. }
  321. }
  322. [Test]
  323. [Category ("MultiThreaded")]
  324. public void InterrupedWaitOne ()
  325. {
  326. using (var m1 = new Mutex (true)) {
  327. using (var done = new ManualResetEvent (false)) {
  328. var thread = new Thread (() =>
  329. {
  330. try {
  331. m1.WaitOne ();
  332. } catch (ThreadInterruptedException) {
  333. done.Set ();
  334. }
  335. });
  336. thread.Start ();
  337. Thread.Sleep (100); // wait a bit so the thread can enter its wait
  338. thread.Interrupt ();
  339. Assert.IsTrue (thread.Join (1000), "Join");
  340. Assert.IsTrue (done.WaitOne (1000), "done");
  341. m1.ReleaseMutex ();
  342. }
  343. }
  344. }
  345. [Test]
  346. [Category ("MultiThreaded")]
  347. public void WaitOneWithAbandonedMutex ()
  348. {
  349. using (var m = new Mutex (false)) {
  350. var thread1 = new Thread (() => {
  351. m.WaitOne ();
  352. });
  353. thread1.Start ();
  354. thread1.Join (1000);
  355. try {
  356. m.WaitOne ();
  357. Assert.Fail ("Expected AbandonedMutexException");
  358. } catch (AbandonedMutexException) {
  359. }
  360. // Current thread should own the Mutex now
  361. var signalled = false;
  362. var thread2 = new Thread (() => {
  363. signalled = m.WaitOne (100);
  364. });
  365. thread2.Start ();
  366. thread2.Join (1000);
  367. Assert.IsFalse (signalled);
  368. // Since this thread owns the Mutex releasing it shouldn't fail
  369. m.ReleaseMutex ();
  370. // The Mutex should now be unowned
  371. try {
  372. m.ReleaseMutex ();
  373. Assert.Fail ("Expected ApplicationException");
  374. } catch (ApplicationException) {
  375. }
  376. }
  377. }
  378. [Test]
  379. [Category ("MultiThreaded")]
  380. public void WaitOneWithAbandonedMutexAndMultipleThreads ()
  381. {
  382. using (var m = new Mutex (true)) {
  383. var nonAbandoned = 0;
  384. var abandoned = 0;
  385. var n = 0;
  386. var threads = new List<Thread> ();
  387. for (int i = 0; i < 50; i++) {
  388. var thread = new Thread (() => {
  389. try {
  390. m.WaitOne ();
  391. nonAbandoned++;
  392. } catch (AbandonedMutexException) {
  393. abandoned++;
  394. }
  395. if (((n++) % 5) != 0)
  396. m.ReleaseMutex ();
  397. });
  398. thread.Start ();
  399. threads.Add (thread);
  400. }
  401. m.ReleaseMutex ();
  402. foreach (var thread in threads) {
  403. if (!thread.Join (1000)) {
  404. Assert.Fail ("Timed out");
  405. }
  406. }
  407. Assert.AreEqual (40, nonAbandoned);
  408. Assert.AreEqual (10, abandoned);
  409. }
  410. }
  411. [Test]
  412. [Category ("MultiThreaded")]
  413. public void WaitAnyWithSecondMutexAbandoned ()
  414. {
  415. using (var m1 = new Mutex (false)) {
  416. using (var m2 = new Mutex (false)) {
  417. var mainProceed = false;
  418. var thread2Proceed = false;
  419. var thread1 = new Thread (() => {
  420. m2.WaitOne ();
  421. });
  422. var thread2 = new Thread (() => {
  423. m1.WaitOne ();
  424. mainProceed = true;
  425. while (!thread2Proceed) {
  426. Thread.Sleep (10);
  427. }
  428. m1.ReleaseMutex ();
  429. });
  430. thread1.Start ();
  431. thread1.Join (1000);
  432. thread2.Start ();
  433. while (!mainProceed) {
  434. Thread.Sleep (10);
  435. }
  436. try {
  437. WaitHandle.WaitAny (new WaitHandle [] { m1, m2 });
  438. Assert.Fail ("Expected AbandonedMutexException");
  439. } catch (AbandonedMutexException e) {
  440. Assert.AreEqual (1, e.MutexIndex);
  441. Assert.AreEqual (m2, e.Mutex);
  442. } finally {
  443. thread2Proceed = true;
  444. thread2.Join (1000);
  445. }
  446. // Current thread should own the second Mutex now
  447. var signalled = -1;
  448. var thread3 = new Thread (() => {
  449. signalled = WaitHandle.WaitAny (new WaitHandle [] { m1, m2 }, 0);
  450. });
  451. thread3.Start ();
  452. thread3.Join (1000);
  453. Assert.AreEqual (0, signalled);
  454. // Since this thread owns the second Mutex releasing it shouldn't fail
  455. m2.ReleaseMutex ();
  456. // Second Mutex should now be unowned
  457. try {
  458. m2.ReleaseMutex ();
  459. Assert.Fail ("Expected ApplicationException");
  460. } catch (ApplicationException) {
  461. }
  462. // .NET allows the first Mutex which is now abandoned to be released multiple times by this thread
  463. m1.ReleaseMutex ();
  464. m1.ReleaseMutex ();
  465. }
  466. }
  467. }
  468. [Test]
  469. [ExpectedException (typeof (AbandonedMutexException))]
  470. [Category ("MultiThreaded")]
  471. public void WaitAllWithOneAbandonedMutex ()
  472. {
  473. using (var m1 = new Mutex (false)) {
  474. using (var m2 = new Mutex (false)) {
  475. var thread = new Thread (() => {
  476. m1.WaitOne ();
  477. });
  478. thread.Start ();
  479. thread.Join (1000);
  480. WaitHandle.WaitAll (new WaitHandle [] { m1, m2 });
  481. }
  482. }
  483. }
  484. #if MONO_FEATURE_THREAD_SUSPEND_RESUME
  485. [Test]
  486. [Category ("MultiThreaded")]
  487. public void WaitOneWithTimeoutAndSpuriousWake ()
  488. {
  489. /* This is to test that WaitEvent.WaitOne is not going to wait largely
  490. * more than its timeout. In this test, it shouldn't wait more than
  491. * 1500 milliseconds, with its timeout being 1000ms */
  492. using (ManualResetEvent mre = new ManualResetEvent (false))
  493. using (ManualResetEvent ready = new ManualResetEvent (false)) {
  494. var thread = new Thread (() => {
  495. ready.Set ();
  496. mre.WaitOne (1000);
  497. });
  498. thread.Start ();
  499. ready.WaitOne ();
  500. Thread.Sleep (10); // wait a bit so we enter mre.WaitOne
  501. var sw = Stopwatch.StartNew ();
  502. while (sw.ElapsedMilliseconds <= 500) {
  503. thread.Suspend ();
  504. thread.Resume ();
  505. }
  506. Assert.IsTrue (thread.Join (1000), "#1");
  507. }
  508. }
  509. [Test]
  510. [Category ("MultiThreaded")]
  511. public void WaitAnyWithTimeoutAndSpuriousWake ()
  512. {
  513. /* This is to test that WaitEvent.WaitAny is not going to wait largely
  514. * more than its timeout. In this test, it shouldn't wait more than
  515. * 1500 milliseconds, with its timeout being 1000ms */
  516. using (ManualResetEvent mre1 = new ManualResetEvent (false))
  517. using (ManualResetEvent mre2 = new ManualResetEvent (false))
  518. using (ManualResetEvent ready = new ManualResetEvent (false)) {
  519. var thread = new Thread (() => {
  520. ready.Set ();
  521. WaitHandle.WaitAny (new [] { mre1, mre2 }, 1000);
  522. });
  523. thread.Start ();
  524. ready.WaitOne ();
  525. Thread.Sleep (10); // wait a bit so we enter WaitHandle.WaitAny ({mre1, mre2})
  526. var sw = Stopwatch.StartNew ();
  527. while (sw.ElapsedMilliseconds <= 500) {
  528. thread.Suspend ();
  529. thread.Resume ();
  530. }
  531. Assert.IsTrue (thread.Join (1000), "#1");
  532. }
  533. }
  534. [Test]
  535. [Category ("MultiThreaded")]
  536. public void WaitAllWithTimeoutAndSpuriousWake ()
  537. {
  538. /* This is to test that WaitEvent.WaitAll is not going to wait largely
  539. * more than its timeout. In this test, it shouldn't wait more than
  540. * 1500 milliseconds, with its timeout being 1000ms */
  541. using (ManualResetEvent mre1 = new ManualResetEvent (false))
  542. using (ManualResetEvent mre2 = new ManualResetEvent (false))
  543. using (ManualResetEvent ready = new ManualResetEvent (false)) {
  544. var thread = new Thread (() => {
  545. ready.Set ();
  546. WaitHandle.WaitAll (new [] { mre1, mre2 }, 1000);
  547. });
  548. thread.Start ();
  549. ready.WaitOne ();
  550. Thread.Sleep (10); // wait a bit so we enter WaitHandle.WaitAll ({mre1, mre2})
  551. var sw = Stopwatch.StartNew ();
  552. while (sw.ElapsedMilliseconds <= 500) {
  553. thread.Suspend ();
  554. thread.Resume ();
  555. }
  556. Assert.IsTrue (thread.Join (1000), "#1");
  557. }
  558. }
  559. #endif // MONO_FEATURE_THREAD_SUSPEND_RESUME
  560. [Test]
  561. public static void SignalAndWait()
  562. {
  563. using (var eventToSignal = new AutoResetEvent (false))
  564. using (var eventToWait = new AutoResetEvent (false))
  565. {
  566. eventToWait.Set ();
  567. Assert.IsTrue (WaitHandle.SignalAndWait (eventToSignal, eventToWait), "#1");
  568. Assert.IsTrue (eventToSignal.WaitOne (), "#2");
  569. }
  570. }
  571. }
  572. }