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