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