blob: 202dc5383a591fcb749f93ba06883fe3ebce6ef2 [file] [log] [blame]
Georg Brandl9f2b93e2007-08-24 18:07:52 +00001import unittest
2from test import test_support
Georg Brandlc29863e2009-06-18 22:24:26 +00003from contextlib import closing
Neal Norwitzbb89e682008-03-25 07:00:39 +00004import gc
Jeffrey Yasskincf26f542008-03-21 05:02:44 +00005import pickle
6import select
Guido van Rossum4f17e3e1995-03-16 15:07:38 +00007import signal
Jeffrey Yasskin413f5882008-03-21 18:25:06 +00008import subprocess
Jeffrey Yasskincf26f542008-03-21 05:02:44 +00009import traceback
Christian Heimesacfd8ed2008-02-28 21:00:45 +000010import sys, os, time, errno
11
12if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos':
Benjamin Peterson888a39b2009-03-26 20:48:25 +000013 raise unittest.SkipTest("Can't test signal on %s" % \
Christian Heimesacfd8ed2008-02-28 21:00:45 +000014 sys.platform)
15
Guido van Rossumcc5a91d1997-04-16 00:29:15 +000016
Armin Rigo8b2cbfd2004-08-07 21:27:43 +000017class HandlerBCalled(Exception):
18 pass
Guido van Rossum4f17e3e1995-03-16 15:07:38 +000019
Jeffrey Yasskincf26f542008-03-21 05:02:44 +000020
21def exit_subprocess():
22 """Use os._exit(0) to exit the current subprocess.
23
24 Otherwise, the test catches the SystemExit and continues executing
25 in parallel with the original test, so you wind up with an
26 exponential number of tests running concurrently.
27 """
28 os._exit(0)
29
30
Jeffrey Yasskinab561312008-04-02 04:07:44 +000031def ignoring_eintr(__func, *args, **kwargs):
32 try:
33 return __func(*args, **kwargs)
Jeffrey Yasskin2b860db2008-04-04 04:51:19 +000034 except EnvironmentError as e:
Jeffrey Yasskine71d8122008-04-04 16:48:19 +000035 if e.errno != errno.EINTR:
Jeffrey Yasskinab561312008-04-02 04:07:44 +000036 raise
37 return None
38
39
Georg Brandl9f2b93e2007-08-24 18:07:52 +000040class InterProcessSignalTests(unittest.TestCase):
41 MAX_DURATION = 20 # Entire test should last at most 20 sec.
Guido van Rossum4f17e3e1995-03-16 15:07:38 +000042
Neal Norwitzbb89e682008-03-25 07:00:39 +000043 def setUp(self):
44 self.using_gc = gc.isenabled()
45 gc.disable()
46
47 def tearDown(self):
48 if self.using_gc:
49 gc.enable()
50
Jeffrey Yasskinee767772008-04-06 23:04:28 +000051 def format_frame(self, frame, limit=None):
52 return ''.join(traceback.format_stack(frame, limit=limit))
53
54 def handlerA(self, signum, frame):
Georg Brandl9f2b93e2007-08-24 18:07:52 +000055 self.a_called = True
56 if test_support.verbose:
Jeffrey Yasskinee767772008-04-06 23:04:28 +000057 print "handlerA invoked from signal %s at:\n%s" % (
58 signum, self.format_frame(frame, limit=1))
Guido van Rossum4f17e3e1995-03-16 15:07:38 +000059
Jeffrey Yasskinee767772008-04-06 23:04:28 +000060 def handlerB(self, signum, frame):
Georg Brandl9f2b93e2007-08-24 18:07:52 +000061 self.b_called = True
62 if test_support.verbose:
Jeffrey Yasskinee767772008-04-06 23:04:28 +000063 print "handlerB invoked from signal %s at:\n%s" % (
64 signum, self.format_frame(frame, limit=1))
65 raise HandlerBCalled(signum, self.format_frame(frame))
Neal Norwitz9730bcb2006-01-23 07:50:06 +000066
Jeffrey Yasskin413f5882008-03-21 18:25:06 +000067 def wait(self, child):
68 """Wait for child to finish, ignoring EINTR."""
Jeffrey Yasskincf26f542008-03-21 05:02:44 +000069 while True:
70 try:
Jeffrey Yasskin413f5882008-03-21 18:25:06 +000071 child.wait()
Jeffrey Yasskin6cda88e2008-03-21 05:51:37 +000072 return
Jeffrey Yasskincf26f542008-03-21 05:02:44 +000073 except OSError as e:
74 if e.errno != errno.EINTR:
75 raise
Fred Drake004d5e62000-10-23 17:22:08 +000076
Jeffrey Yasskincf26f542008-03-21 05:02:44 +000077 def run_test(self):
78 # Install handlers. This function runs in a sub-process, so we
79 # don't worry about re-setting the default handlers.
80 signal.signal(signal.SIGHUP, self.handlerA)
81 signal.signal(signal.SIGUSR1, self.handlerB)
82 signal.signal(signal.SIGUSR2, signal.SIG_IGN)
83 signal.signal(signal.SIGALRM, signal.default_int_handler)
Michael W. Hudson5c26e862004-06-11 18:09:28 +000084
Jeffrey Yasskincf26f542008-03-21 05:02:44 +000085 # Variables the signals will modify:
86 self.a_called = False
87 self.b_called = False
Tim Peters1742f332006-08-12 04:42:47 +000088
Jeffrey Yasskincf26f542008-03-21 05:02:44 +000089 # Let the sub-processes know who to send signals to.
90 pid = os.getpid()
Georg Brandl9f2b93e2007-08-24 18:07:52 +000091 if test_support.verbose:
92 print "test runner's pid is", pid
Tim Peters1742f332006-08-12 04:42:47 +000093
Jeffrey Yasskinab561312008-04-02 04:07:44 +000094 child = ignoring_eintr(subprocess.Popen, ['kill', '-HUP', str(pid)])
95 if child:
96 self.wait(child)
97 if not self.a_called:
98 time.sleep(1) # Give the signal time to be delivered.
Jeffrey Yasskincf26f542008-03-21 05:02:44 +000099 self.assertTrue(self.a_called)
100 self.assertFalse(self.b_called)
101 self.a_called = False
Tim Peters1742f332006-08-12 04:42:47 +0000102
Jeffrey Yasskinee767772008-04-06 23:04:28 +0000103 # Make sure the signal isn't delivered while the previous
104 # Popen object is being destroyed, because __del__ swallows
105 # exceptions.
106 del child
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000107 try:
Jeffrey Yasskin413f5882008-03-21 18:25:06 +0000108 child = subprocess.Popen(['kill', '-USR1', str(pid)])
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000109 # This wait should be interrupted by the signal's exception.
110 self.wait(child)
Jeffrey Yasskinab561312008-04-02 04:07:44 +0000111 time.sleep(1) # Give the signal time to be delivered.
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000112 self.fail('HandlerBCalled exception not thrown')
113 except HandlerBCalled:
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000114 self.assertTrue(self.b_called)
115 self.assertFalse(self.a_called)
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000116 if test_support.verbose:
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000117 print "HandlerBCalled exception caught"
Neal Norwitzec3c5e32006-07-30 19:18:38 +0000118
Jeffrey Yasskinab561312008-04-02 04:07:44 +0000119 child = ignoring_eintr(subprocess.Popen, ['kill', '-USR2', str(pid)])
120 if child:
121 self.wait(child) # Nothing should happen.
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000122
123 try:
124 signal.alarm(1)
125 # The race condition in pause doesn't matter in this case,
126 # since alarm is going to raise a KeyboardException, which
127 # will skip the call.
128 signal.pause()
Jeffrey Yasskinab561312008-04-02 04:07:44 +0000129 # But if another signal arrives before the alarm, pause
130 # may return early.
131 time.sleep(1)
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000132 except KeyboardInterrupt:
133 if test_support.verbose:
134 print "KeyboardInterrupt (the alarm() went off)"
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000135 except:
Jeffrey Yasskinab561312008-04-02 04:07:44 +0000136 self.fail("Some other exception woke us from pause: %s" %
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000137 traceback.format_exc())
138 else:
Jeffrey Yasskinab561312008-04-02 04:07:44 +0000139 self.fail("pause returned of its own accord, and the signal"
140 " didn't arrive after another second.")
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000141
Stefan Krah68b4e012010-04-20 07:59:10 +0000142 # Issue 3864. Unknown if this affects earlier versions of freebsd also.
R. David Murraye0e8a872010-04-17 05:26:26 +0000143 @unittest.skipIf(sys.platform=='freebsd6',
144 'inter process signals not reliable (do not mix well with threading) '
145 'on freebsd6')
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000146 def test_main(self):
147 # This function spawns a child process to insulate the main
148 # test-running process from all the signals. It then
149 # communicates with that child process over a pipe and
150 # re-raises information about any exceptions the child
151 # throws. The real work happens in self.run_test().
152 os_done_r, os_done_w = os.pipe()
Georg Brandlc29863e2009-06-18 22:24:26 +0000153 with closing(os.fdopen(os_done_r)) as done_r, \
154 closing(os.fdopen(os_done_w, 'w')) as done_w:
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000155 child = os.fork()
156 if child == 0:
157 # In the child process; run the test and report results
158 # through the pipe.
159 try:
160 done_r.close()
161 # Have to close done_w again here because
162 # exit_subprocess() will skip the enclosing with block.
163 with closing(done_w):
164 try:
165 self.run_test()
166 except:
167 pickle.dump(traceback.format_exc(), done_w)
168 else:
169 pickle.dump(None, done_w)
170 except:
171 print 'Uh oh, raised from pickle.'
172 traceback.print_exc()
173 finally:
174 exit_subprocess()
175
176 done_w.close()
177 # Block for up to MAX_DURATION seconds for the test to finish.
178 r, w, x = select.select([done_r], [], [], self.MAX_DURATION)
179 if done_r in r:
180 tb = pickle.load(done_r)
181 if tb:
182 self.fail(tb)
183 else:
184 os.kill(child, signal.SIGKILL)
185 self.fail('Test deadlocked after %d seconds.' %
186 self.MAX_DURATION)
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000187
188
189class BasicSignalTests(unittest.TestCase):
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000190 def trivial_signal_handler(self, *args):
191 pass
192
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000193 def test_out_of_range_signal_number_raises_error(self):
194 self.assertRaises(ValueError, signal.getsignal, 4242)
195
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000196 self.assertRaises(ValueError, signal.signal, 4242,
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000197 self.trivial_signal_handler)
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000198
199 def test_setting_signal_handler_to_none_raises_error(self):
200 self.assertRaises(TypeError, signal.signal,
201 signal.SIGUSR1, None)
202
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000203 def test_getsignal(self):
204 hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler)
205 self.assertEquals(signal.getsignal(signal.SIGHUP),
206 self.trivial_signal_handler)
207 signal.signal(signal.SIGHUP, hup)
208 self.assertEquals(signal.getsignal(signal.SIGHUP), hup)
209
210
Guido van Rossum02de8972007-12-19 19:41:06 +0000211class WakeupSignalTests(unittest.TestCase):
212 TIMEOUT_FULL = 10
213 TIMEOUT_HALF = 5
214
215 def test_wakeup_fd_early(self):
216 import select
217
218 signal.alarm(1)
219 before_time = time.time()
220 # We attempt to get a signal during the sleep,
221 # before select is called
222 time.sleep(self.TIMEOUT_FULL)
223 mid_time = time.time()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000224 self.assertTrue(mid_time - before_time < self.TIMEOUT_HALF)
Guido van Rossum02de8972007-12-19 19:41:06 +0000225 select.select([self.read], [], [], self.TIMEOUT_FULL)
226 after_time = time.time()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000227 self.assertTrue(after_time - mid_time < self.TIMEOUT_HALF)
Guido van Rossum02de8972007-12-19 19:41:06 +0000228
229 def test_wakeup_fd_during(self):
230 import select
231
232 signal.alarm(1)
233 before_time = time.time()
234 # We attempt to get a signal during the select call
235 self.assertRaises(select.error, select.select,
236 [self.read], [], [], self.TIMEOUT_FULL)
237 after_time = time.time()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000238 self.assertTrue(after_time - before_time < self.TIMEOUT_HALF)
Guido van Rossum02de8972007-12-19 19:41:06 +0000239
240 def setUp(self):
241 import fcntl
242
243 self.alrm = signal.signal(signal.SIGALRM, lambda x,y:None)
244 self.read, self.write = os.pipe()
245 flags = fcntl.fcntl(self.write, fcntl.F_GETFL, 0)
246 flags = flags | os.O_NONBLOCK
247 fcntl.fcntl(self.write, fcntl.F_SETFL, flags)
248 self.old_wakeup = signal.set_wakeup_fd(self.write)
249
250 def tearDown(self):
251 signal.set_wakeup_fd(self.old_wakeup)
252 os.close(self.read)
253 os.close(self.write)
254 signal.signal(signal.SIGALRM, self.alrm)
255
Facundo Batista7e251e82008-02-23 15:07:35 +0000256class SiginterruptTest(unittest.TestCase):
257 signum = signal.SIGUSR1
258 def readpipe_interrupted(self, cb):
259 r, w = os.pipe()
260 ppid = os.getpid()
261 pid = os.fork()
262
263 oldhandler = signal.signal(self.signum, lambda x,y: None)
264 cb()
265 if pid==0:
266 # child code: sleep, kill, sleep. and then exit,
267 # which closes the pipe from which the parent process reads
268 try:
269 time.sleep(0.2)
270 os.kill(ppid, self.signum)
271 time.sleep(0.2)
272 finally:
Jeffrey Yasskincf26f542008-03-21 05:02:44 +0000273 exit_subprocess()
Facundo Batista7e251e82008-02-23 15:07:35 +0000274
275 try:
276 os.close(w)
277
278 try:
279 d=os.read(r, 1)
280 return False
281 except OSError, err:
282 if err.errno != errno.EINTR:
283 raise
284 return True
285 finally:
286 signal.signal(self.signum, oldhandler)
287 os.waitpid(pid, 0)
288
289 def test_without_siginterrupt(self):
290 i=self.readpipe_interrupted(lambda: None)
291 self.assertEquals(i, True)
292
293 def test_siginterrupt_on(self):
294 i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 1))
295 self.assertEquals(i, True)
296
297 def test_siginterrupt_off(self):
298 i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 0))
299 self.assertEquals(i, False)
Guido van Rossum02de8972007-12-19 19:41:06 +0000300
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000301class ItimerTest(unittest.TestCase):
302 def setUp(self):
303 self.hndl_called = False
304 self.hndl_count = 0
305 self.itimer = None
Neal Norwitzbb89e682008-03-25 07:00:39 +0000306 self.old_alarm = signal.signal(signal.SIGALRM, self.sig_alrm)
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000307
308 def tearDown(self):
Neal Norwitzbb89e682008-03-25 07:00:39 +0000309 signal.signal(signal.SIGALRM, self.old_alarm)
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000310 if self.itimer is not None: # test_itimer_exc doesn't change this attr
311 # just ensure that itimer is stopped
312 signal.setitimer(self.itimer, 0)
313
314 def sig_alrm(self, *args):
315 self.hndl_called = True
316 if test_support.verbose:
317 print("SIGALRM handler invoked", args)
318
319 def sig_vtalrm(self, *args):
320 self.hndl_called = True
321
322 if self.hndl_count > 3:
323 # it shouldn't be here, because it should have been disabled.
324 raise signal.ItimerError("setitimer didn't disable ITIMER_VIRTUAL "
325 "timer.")
326 elif self.hndl_count == 3:
327 # disable ITIMER_VIRTUAL, this function shouldn't be called anymore
328 signal.setitimer(signal.ITIMER_VIRTUAL, 0)
329 if test_support.verbose:
330 print("last SIGVTALRM handler call")
331
332 self.hndl_count += 1
333
334 if test_support.verbose:
335 print("SIGVTALRM handler invoked", args)
336
337 def sig_prof(self, *args):
338 self.hndl_called = True
339 signal.setitimer(signal.ITIMER_PROF, 0)
340
341 if test_support.verbose:
342 print("SIGPROF handler invoked", args)
343
344 def test_itimer_exc(self):
345 # XXX I'm assuming -1 is an invalid itimer, but maybe some platform
346 # defines it ?
347 self.assertRaises(signal.ItimerError, signal.setitimer, -1, 0)
Neal Norwitzbb89e682008-03-25 07:00:39 +0000348 # Negative times are treated as zero on some platforms.
349 if 0:
350 self.assertRaises(signal.ItimerError,
351 signal.setitimer, signal.ITIMER_REAL, -1)
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000352
353 def test_itimer_real(self):
354 self.itimer = signal.ITIMER_REAL
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000355 signal.setitimer(self.itimer, 1.0)
356 if test_support.verbose:
357 print("\ncall pause()...")
358 signal.pause()
359
360 self.assertEqual(self.hndl_called, True)
361
Stefan Krah68b4e012010-04-20 07:59:10 +0000362 # Issue 3864. Unknown if this affects earlier versions of freebsd also.
R. David Murraye0e8a872010-04-17 05:26:26 +0000363 @unittest.skipIf(sys.platform=='freebsd6',
364 'itimer not reliable (does not mix well with threading) on freebsd6')
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000365 def test_itimer_virtual(self):
366 self.itimer = signal.ITIMER_VIRTUAL
367 signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
368 signal.setitimer(self.itimer, 0.3, 0.2)
369
Mark Dickinson4b841d92009-10-31 10:36:06 +0000370 start_time = time.time()
Stefan Krah68b4e012010-04-20 07:59:10 +0000371 while time.time() - start_time < 60.0:
Mark Dickinson245d9152009-10-04 18:38:39 +0000372 # use up some virtual time by doing real work
373 _ = pow(12345, 67890, 10000019)
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000374 if signal.getitimer(self.itimer) == (0.0, 0.0):
375 break # sig_vtalrm handler stopped this itimer
Stefan Krah68b4e012010-04-20 07:59:10 +0000376 else: # Issue 8424
377 sys.stdout.write("test_itimer_virtual: timeout: likely cause: "
378 "machine too slow or load too high.\n")
379 return
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000380
381 # virtual itimer should be (0.0, 0.0) now
382 self.assertEquals(signal.getitimer(self.itimer), (0.0, 0.0))
383 # and the handler should have been called
384 self.assertEquals(self.hndl_called, True)
385
Stefan Krah68b4e012010-04-20 07:59:10 +0000386 # Issue 3864. Unknown if this affects earlier versions of freebsd also.
R. David Murraye0e8a872010-04-17 05:26:26 +0000387 @unittest.skipIf(sys.platform=='freebsd6',
388 'itimer not reliable (does not mix well with threading) on freebsd6')
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000389 def test_itimer_prof(self):
390 self.itimer = signal.ITIMER_PROF
391 signal.signal(signal.SIGPROF, self.sig_prof)
Jeffrey Yasskin2b860db2008-04-04 04:51:19 +0000392 signal.setitimer(self.itimer, 0.2, 0.2)
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000393
Mark Dickinson4b841d92009-10-31 10:36:06 +0000394 start_time = time.time()
Stefan Krah68b4e012010-04-20 07:59:10 +0000395 while time.time() - start_time < 60.0:
Mark Dickinson4b841d92009-10-31 10:36:06 +0000396 # do some work
397 _ = pow(12345, 67890, 10000019)
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000398 if signal.getitimer(self.itimer) == (0.0, 0.0):
399 break # sig_prof handler stopped this itimer
Stefan Krah68b4e012010-04-20 07:59:10 +0000400 else: # Issue 8424
401 sys.stdout.write("test_itimer_prof: timeout: likely cause: "
402 "machine too slow or load too high.\n")
403 return
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000404
Jeffrey Yasskin2b860db2008-04-04 04:51:19 +0000405 # profiling itimer should be (0.0, 0.0) now
406 self.assertEquals(signal.getitimer(self.itimer), (0.0, 0.0))
407 # and the handler should have been called
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000408 self.assertEqual(self.hndl_called, True)
409
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000410def test_main():
Guido van Rossum02de8972007-12-19 19:41:06 +0000411 test_support.run_unittest(BasicSignalTests, InterProcessSignalTests,
Martin v. Löwisaef18b12008-03-24 13:31:16 +0000412 WakeupSignalTests, SiginterruptTest, ItimerTest)
Georg Brandl9f2b93e2007-08-24 18:07:52 +0000413
414
415if __name__ == "__main__":
416 test_main()