1 // 2 // UnixSignalTest.cs - NUnit Test Cases for Mono.Unix.UnixSignal 3 // 4 // Authors: 5 // Jonathan Pryor <jonpryor@vt.edu> 6 // 7 // (C) 2008 Jonathan Pryor 8 // 9 10 using NUnit.Framework; 11 12 using System; 13 using System.Diagnostics; 14 using System.Text; 15 using System.Threading; 16 using Mono.Unix; 17 using Mono.Unix.Android; 18 using Mono.Unix.Native; 19 20 namespace MonoTests.Mono.Unix { 21 22 [TestFixture, Category ("NotOnWindows")] 23 public class UnixSignalTest { 24 25 // helper method to create a thread waiting on a UnixSignal CreateWaitSignalThread(UnixSignal signal, int timeout)26 static Thread CreateWaitSignalThread (UnixSignal signal, int timeout) 27 { 28 Thread t1 = new Thread(delegate() { 29 var sw = Stopwatch.StartNew (); 30 bool r = signal.WaitOne (timeout, false); 31 sw.Stop (); 32 Assert.AreEqual (signal.Count, 1); 33 Assert.AreEqual (r, true); 34 if (sw.Elapsed > new TimeSpan (0, 0, timeout/1000)) 35 throw new InvalidOperationException ("Signal slept too long"); 36 }); 37 return t1; 38 } 39 40 // helper method to create a two-thread test MultiThreadTest(UnixSignal signal, int timeout, ThreadStart tstart)41 static void MultiThreadTest (UnixSignal signal, int timeout, ThreadStart tstart) 42 { 43 Thread t1 = CreateWaitSignalThread (signal, timeout); 44 Thread t2 = new Thread (tstart); 45 t1.Start (); 46 t2.Start (); 47 t1.Join (); 48 t2.Join (); 49 } 50 51 [Test] TestNestedInvocation()52 public void TestNestedInvocation() 53 { 54 UnixSignal s = new UnixSignal(Signum.SIGINT); 55 Thread a = new Thread(delegate() { 56 bool r = s.WaitOne (1000, false); 57 }); 58 Thread b = new Thread(delegate() { 59 bool r = s.WaitOne (500, false); 60 }); 61 a.Start(); 62 b.Start(); 63 a.Join(); 64 b.Join(); 65 } 66 67 [Test] TestWaitAnyFailsWithMore64Signals()68 public void TestWaitAnyFailsWithMore64Signals() 69 { 70 UnixSignal s1 = new UnixSignal(Signum.SIGINT); 71 UnixSignal[] signals = new UnixSignal[65]; 72 for (int i=0; i<65; ++i) 73 signals[i] = s1; 74 75 Assert.That(UnixSignal.WaitAny(signals, new TimeSpan(0,0,1)), Is.EqualTo(-1)); 76 } 77 78 [Test] TestConcurrentWaitOne()79 public void TestConcurrentWaitOne() 80 { 81 UnixSignal s1 = new UnixSignal(Signum.SIGINT); 82 UnixSignal s2 = new UnixSignal(Signum.SIGINT); 83 Thread a = CreateWaitSignalThread(s1, 10000); 84 Thread b = CreateWaitSignalThread(s2, 5000); 85 Thread c = new Thread (delegate () { 86 Thread.Sleep (1000); 87 Stdlib.raise (Signum.SIGINT); 88 }); 89 a.Start(); 90 b.Start(); 91 c.Start(); 92 a.Join(); 93 b.Join(); 94 c.Join(); 95 Assert.That(s1.Count, Is.EqualTo(1), "Expected 1 signal raised"); 96 Assert.That(s2.Count, Is.EqualTo(1), "Expected 1 signal raised"); 97 } 98 99 [Test] TestConcurrentWaitOneSameInstance()100 public void TestConcurrentWaitOneSameInstance() 101 { 102 UnixSignal s1 = new UnixSignal(Signum.SIGINT); 103 Thread a = CreateWaitSignalThread(s1, 10000); 104 Thread b = CreateWaitSignalThread(s1, 10000); 105 Thread c = new Thread (delegate () { 106 Thread.Sleep (500); 107 Stdlib.raise (Signum.SIGINT); 108 }); 109 a.Start(); 110 b.Start(); 111 c.Start(); 112 a.Join(); 113 b.Join(); 114 c.Join(); 115 } 116 117 [Test] 118 [Category ("AndroidNotWorking")] // Crashes (silently) the runtime in similar fashion to real-time signals TestSignumProperty()119 public void TestSignumProperty () 120 { 121 UnixSignal signal1 = new UnixSignal (Signum.SIGSEGV); 122 Assert.That (signal1.Signum, Is.EqualTo (Signum.SIGSEGV)); 123 } 124 125 [Test] 126 [Category ("NotOnMac")] TestRealTimeCstor()127 public void TestRealTimeCstor () 128 { 129 if (!TestHelper.CanUseRealTimeSignals ()) 130 return; 131 RealTimeSignum rts = new RealTimeSignum (0); 132 using (UnixSignal s = new UnixSignal (rts)) 133 { 134 Assert.That(s.IsRealTimeSignal); 135 Assert.That(s.RealTimeSignum, Is.EqualTo (rts)); 136 } 137 } 138 139 [Test] 140 [Category ("NotOnMac")] TestSignumPropertyThrows()141 public void TestSignumPropertyThrows () 142 { 143 if (!TestHelper.CanUseRealTimeSignals ()) 144 return; 145 146 Assert.Throws<InvalidOperationException> (() => { 147 UnixSignal signal1 = new UnixSignal (new RealTimeSignum (0)); 148 Signum s = signal1.Signum; 149 }); 150 } 151 152 [Test] 153 [Category ("NotOnMac")] TestRealTimeSignumProperty()154 public void TestRealTimeSignumProperty () 155 { 156 if (!TestHelper.CanUseRealTimeSignals ()) 157 return; 158 RealTimeSignum rts = new RealTimeSignum (0); 159 UnixSignal signal1 = new UnixSignal (rts); 160 Assert.That (signal1.RealTimeSignum, Is.EqualTo (rts)); 161 } 162 163 [Test] 164 [Category ("NotOnMac")] TestRealTimePropertyThrows()165 public void TestRealTimePropertyThrows () 166 { 167 if (!TestHelper.CanUseRealTimeSignals ()) 168 return; 169 170 Assert.Throws<InvalidOperationException> (() => { 171 UnixSignal signal1 = new UnixSignal (Signum.SIGSEGV); 172 RealTimeSignum s = signal1.RealTimeSignum; 173 }); 174 } 175 176 [Test] 177 [Category ("NotOnMac")] TestRaiseRTMINSignal()178 public void TestRaiseRTMINSignal () 179 { 180 if (!TestHelper.CanUseRealTimeSignals ()) 181 return; 182 RealTimeSignum rts = new RealTimeSignum (0); 183 using (UnixSignal signal = new UnixSignal (rts)) 184 { 185 MultiThreadTest (signal, 5000, delegate() { 186 Thread.Sleep (1000); 187 Stdlib.raise (rts); 188 }); 189 } 190 } 191 192 [Test] 193 [Category ("NotOnMac")] TestRaiseRTMINPlusOneSignal()194 public void TestRaiseRTMINPlusOneSignal () 195 { 196 if (!TestHelper.CanUseRealTimeSignals ()) 197 return; 198 /*this number is a guestimate, but it's ok*/ 199 for (int i = 1; i < 10; ++i) { 200 RealTimeSignum rts = new RealTimeSignum (i); 201 UnixSignal signal; 202 try { 203 signal = new UnixSignal (rts); 204 } catch (ArgumentException) { /*skip the ones that are unavailable*/ 205 continue; 206 } 207 using (signal) 208 { 209 MultiThreadTest (signal, 5000, delegate() { 210 Thread.Sleep(1000); 211 Stdlib.raise(rts); 212 }); 213 } 214 return; 215 } 216 Assert.IsTrue (false, "#1 No available RT signal"); 217 } 218 219 [Test] 220 [Category ("NotOnMac")] TestCanRegisterRTSignalMultipleTimes()221 public void TestCanRegisterRTSignalMultipleTimes () 222 { 223 if (!TestHelper.CanUseRealTimeSignals ()) 224 return; 225 /*this number is a guestimate, but it's ok*/ 226 for (int i = 1; i < 10; ++i) { 227 RealTimeSignum rts = new RealTimeSignum (i); 228 UnixSignal signal; 229 try { 230 signal = new UnixSignal (rts); 231 } catch (ArgumentException) { /*skip the ones that are unavailable*/ 232 continue; 233 } 234 try { 235 using (UnixSignal signal2 = new UnixSignal (rts)) 236 { 237 //ok 238 return; 239 } 240 } catch (ArgumentException) { /*skip the ones that are unavailable*/ 241 Assert.IsTrue (false, "#1 Could not register second signal handler"); 242 } 243 } 244 Assert.IsTrue (false, "#2 No available RT signal"); 245 } 246 247 [Test] TestRaise()248 public void TestRaise () 249 { 250 Thread t1 = new Thread (delegate () { 251 using (UnixSignal a = new UnixSignal (Signum.SIGINT)) { 252 var sw = Stopwatch.StartNew (); 253 bool r = a.WaitOne (5000, false); 254 sw.Stop (); 255 Assert.AreEqual (a.Count, 1); 256 Assert.AreEqual (r, true); 257 if (sw.Elapsed > new TimeSpan (0, 0, 5)) 258 throw new InvalidOperationException ("Signal slept too long"); 259 } 260 }); 261 Thread t2 = new Thread (delegate () { 262 Thread.Sleep (1000); 263 Stdlib.raise (Signum.SIGINT); 264 }); 265 t1.Start (); 266 t2.Start (); 267 t1.Join (); 268 t2.Join (); 269 } 270 271 [Test] TestRaiseAny()272 public void TestRaiseAny () 273 { 274 Thread t1 = new Thread (delegate () { 275 using (UnixSignal a = new UnixSignal (Signum.SIGINT)) { 276 var sw = Stopwatch.StartNew (); 277 int idx = UnixSignal.WaitAny (new UnixSignal[]{a}, 5000); 278 sw.Stop (); 279 Assert.AreEqual (idx, 0); 280 Assert.AreEqual (a.Count, 1); 281 if (sw.Elapsed > new TimeSpan (0, 0, 5)) 282 throw new InvalidOperationException ("Signal slept too long"); 283 } 284 }); 285 Thread t2 = new Thread (delegate () { 286 Thread.Sleep (1000); 287 Stdlib.raise (Signum.SIGINT); 288 }); 289 t1.Start (); 290 t2.Start (); 291 t1.Join (); 292 t2.Join (); 293 } 294 295 [Test] TestSeparation()296 public void TestSeparation () 297 { 298 Thread t1 = new Thread (delegate () { 299 using (UnixSignal a = new UnixSignal (Signum.SIGINT)) 300 using (UnixSignal b = new UnixSignal (Signum.SIGTERM)) { 301 var sw = Stopwatch.StartNew (); 302 int idx = UnixSignal.WaitAny (new UnixSignal[]{a, b}, 5000); 303 sw.Stop (); 304 Assert.AreEqual (idx, 1); 305 Assert.AreEqual (a.Count, 0); 306 Assert.AreEqual (b.Count, 1); 307 if (sw.Elapsed > new TimeSpan (0, 0, 5)) 308 throw new InvalidOperationException ("Signal slept too long"); 309 } 310 }); 311 Thread t2 = new Thread (delegate () { 312 Thread.Sleep (1000); 313 Stdlib.raise (Signum.SIGTERM); 314 }); 315 t1.Start (); 316 t2.Start (); 317 t1.Join (); 318 t2.Join (); 319 } 320 321 [Test] TestNoEmit()322 public void TestNoEmit () 323 { 324 using (UnixSignal u = new UnixSignal (Signum.SIGINT)) { 325 var sw = Stopwatch.StartNew (); 326 bool r = u.WaitOne (5100, false); 327 Assert.AreEqual (r, false); 328 sw.Stop (); 329 if (sw.Elapsed < new TimeSpan (0, 0, 5)) 330 throw new InvalidOperationException ("Signal didn't block for 5s; blocked for " + sw.Elapsed.TotalSeconds.ToString()); 331 } 332 } 333 334 [Test] TestNoEmitAny()335 public void TestNoEmitAny () 336 { 337 using (UnixSignal u = new UnixSignal (Signum.SIGINT)) { 338 int idx = UnixSignal.WaitAny (new UnixSignal[]{u}, 5100); 339 Assert.AreEqual (idx, 5100); 340 } 341 } 342 343 [Test] TestDispose1()344 public void TestDispose1 () 345 { 346 UnixSignal a = new UnixSignal (Signum.SIGINT); 347 UnixSignal b = new UnixSignal (Signum.SIGINT); 348 349 Stdlib.raise (Signum.SIGINT); 350 SleepUntilSignaled (a); 351 352 Assert.AreEqual (a.Count, 1); 353 Assert.AreEqual (b.Count, 1); 354 355 a.Close (); 356 b.Reset (); 357 358 Stdlib.raise (Signum.SIGINT); 359 SleepUntilSignaled (b); 360 Assert.AreEqual (b.Count, 1); 361 362 b.Close (); 363 } 364 SleepUntilSignaled(UnixSignal s)365 static void SleepUntilSignaled (UnixSignal s) 366 { 367 for (int i = 0; i < 10; ++i) { 368 if (s.Count > 0) 369 break; 370 Thread.Sleep (100); 371 } 372 } 373 374 [Test] TestDispose2()375 public void TestDispose2 () 376 { 377 UnixSignal a = new UnixSignal (Signum.SIGINT); 378 UnixSignal b = new UnixSignal (Signum.SIGINT); 379 380 Stdlib.raise (Signum.SIGINT); 381 SleepUntilSignaled (a); 382 383 Assert.AreEqual (a.Count, 1); 384 Assert.AreEqual (b.Count, 1); 385 386 b.Close (); 387 a.Reset (); 388 389 Stdlib.raise (Signum.SIGINT); 390 SleepUntilSignaled (a); 391 Assert.AreEqual (a.Count, 1); 392 393 a.Close (); 394 } 395 396 [Test] 397 [Category ("AndroidNotWorking")] // Android 4.4.4 doesn't have signal(2) TestSignalActionInteraction()398 public void TestSignalActionInteraction () 399 { 400 using (UnixSignal a = new UnixSignal (Signum.SIGINT)) { 401 Stdlib.SetSignalAction (Signum.SIGINT, SignalAction.Ignore); 402 Stdlib.raise (Signum.SIGINT); 403 Assert.AreEqual (a.Count, 0); // never invoked 404 } 405 } 406 407 static readonly Signum[] signals = new Signum[] { 408 Signum.SIGHUP, Signum.SIGINT, Signum.SIGTERM, Signum.SIGCONT, 409 }; 410 411 const int StormCount = 100000; 412 413 [Test] TestRaiseStorm()414 public void TestRaiseStorm () 415 { 416 UnixSignal[] usignals = CreateSignals (signals); 417 Thread[] threads = new Thread[]{ 418 CreateRaiseStormThread (StormCount/4), 419 CreateRaiseStormThread (StormCount/4), 420 CreateRaiseStormThread (StormCount/4), 421 CreateRaiseStormThread (StormCount/4), 422 }; 423 foreach (Thread t in threads) 424 t.Start (); 425 foreach (Thread t in threads) 426 t.Join (); 427 AssertCountSet (usignals); 428 // signal delivery might take some time, wait a bit before closing 429 // the UnixSignal so we can ignore it and not terminate the process 430 // when a SIGHUP/SIGTERM arrives afterwards 431 Thread.Sleep (1000); 432 CloseSignals (usignals); 433 } 434 AssertCount(UnixSignal[] usignals)435 static void AssertCount (UnixSignal[] usignals) 436 { 437 int sum = 0; 438 foreach (UnixSignal s in usignals) 439 sum += s.Count; 440 Assert.AreEqual (sum, StormCount); 441 } 442 AssertCountSet(UnixSignal[] usignals)443 static void AssertCountSet (UnixSignal[] usignals) 444 { 445 foreach (UnixSignal s in usignals) { 446 Assert.IsTrue (s.Count > 0); 447 } 448 } 449 CreateSignals(Signum[] signals)450 static UnixSignal[] CreateSignals (Signum[] signals) 451 { 452 UnixSignal[] s = new UnixSignal [signals.Length]; 453 for (int i = 0; i < signals.Length; ++i) 454 s [i] = new UnixSignal (signals [i]); 455 return s; 456 } 457 CloseSignals(UnixSignal[] signals)458 static void CloseSignals (UnixSignal[] signals) 459 { 460 foreach (UnixSignal s in signals) 461 s.Close (); 462 } 463 464 // Create thread that issues many signals from a set of harmless signals CreateRaiseStormThread(int max)465 static Thread CreateRaiseStormThread (int max) 466 { 467 return new Thread (delegate () { 468 Random r = new Random (Environment.TickCount); 469 for (int i = 0; i < max; ++i) { 470 int n = r.Next (0, signals.Length); 471 Stdlib.raise (signals [n]); 472 } 473 }); 474 } 475 476 [Test] TestAddRemove()477 public void TestAddRemove () 478 { 479 UnixSignal[] usignals = CreateSignals (signals); 480 481 Thread[] threads = new Thread[]{ 482 CreateRaiseStormThread (StormCount), 483 CreateSignalCreatorThread (), 484 }; 485 486 foreach (Thread t in threads) 487 t.Start (); 488 foreach (Thread t in threads) 489 t.Join (); 490 491 AssertCountSet (usignals); 492 CloseSignals (usignals); 493 } 494 495 // Create thread that repeatedly registers then unregisters signal handlers CreateSignalCreatorThread()496 static Thread CreateSignalCreatorThread () 497 { 498 return new Thread (delegate () { 499 Random r = new Random (Environment.TickCount << 4); 500 for (int i = 0; i < StormCount; ++i) { 501 int n = r.Next (0, signals.Length); 502 using (new UnixSignal (signals [n])) 503 using (new UnixSignal (signals [(n+1)%signals.Length])) 504 using (new UnixSignal (signals [(n+2)%signals.Length])) 505 using (new UnixSignal (signals [(n+3)%signals.Length])) { 506 } 507 } 508 }); 509 } 510 511 [Test] TestWaitAny()512 public void TestWaitAny () 513 { 514 UnixSignal[] usignals = CreateSignals (signals); 515 516 Thread[] threads = new Thread[]{ 517 CreateRaiseStormThread (StormCount), 518 CreateSignalCreatorThread (), 519 CreateWaitAnyThread (usignals [0], usignals [2]), 520 CreateWaitAnyThread (usignals [1], usignals [3]), 521 CreateWaitAnyThread (usignals [1], usignals [2]), 522 }; 523 524 foreach (Thread t in threads) 525 t.Start (); 526 foreach (Thread t in threads) 527 t.Join (); 528 529 AssertCountSet (usignals); 530 CloseSignals (usignals); 531 } 532 533 // Create thread that blocks until at least one of the given signals is received CreateWaitAnyThread(params UnixSignal[] usignals)534 static Thread CreateWaitAnyThread (params UnixSignal[] usignals) 535 { 536 return new Thread (delegate () { 537 int idx = UnixSignal.WaitAny (usignals, 30000); 538 Assert.AreEqual (idx >= 0 && idx < usignals.Length, true); 539 }); 540 } 541 } 542 } 543