blob: 5414a61960ff5721d880a4b261300f6ceb2b09fa [file] [log] [blame]
Thomas Woutersed03b412007-08-28 21:37:11 +00001import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00002from test import support
Raymond Hettinger722d8c32009-06-18 22:21:03 +00003from contextlib import closing
Christian Heimescc47b052008-03-25 14:56:36 +00004import gc
Christian Heimes4fbc72b2008-03-22 00:47:35 +00005import pickle
6import select
Guido van Rossum4f17e3e1995-03-16 15:07:38 +00007import signal
Christian Heimes4fbc72b2008-03-22 00:47:35 +00008import subprocess
9import traceback
Christian Heimesc06950e2008-02-28 21:17:00 +000010import sys, os, time, errno
11
12if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos':
Benjamin Petersone549ead2009-03-28 21:42:05 +000013 raise unittest.SkipTest("Can't test signal on %s" % \
Christian Heimesc06950e2008-02-28 21:17:00 +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
Christian Heimes4fbc72b2008-03-22 00:47:35 +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
Benjamin Petersonad9d48d2008-04-02 21:49:44 +000031def ignoring_eintr(__func, *args, **kwargs):
32 try:
33 return __func(*args, **kwargs)
Neal Norwitzf5c7c2e2008-04-05 04:47:45 +000034 except EnvironmentError as e:
35 if e.errno != errno.EINTR:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +000036 raise
37 return None
38
39
Thomas Woutersed03b412007-08-28 21:37:11 +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
Christian Heimescc47b052008-03-25 14:56:36 +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
Christian Heimes5e696852008-04-09 08:37:03 +000051 def format_frame(self, frame, limit=None):
52 return ''.join(traceback.format_stack(frame, limit=limit))
53
54 def handlerA(self, signum, frame):
Thomas Woutersed03b412007-08-28 21:37:11 +000055 self.a_called = True
Benjamin Petersonee8712c2008-05-20 21:35:26 +000056 if support.verbose:
Christian Heimes5e696852008-04-09 08:37:03 +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
Christian Heimes5e696852008-04-09 08:37:03 +000060 def handlerB(self, signum, frame):
Thomas Woutersed03b412007-08-28 21:37:11 +000061 self.b_called = True
Benjamin Petersonee8712c2008-05-20 21:35:26 +000062 if support.verbose:
Christian Heimes5e696852008-04-09 08:37:03 +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
Christian Heimes4fbc72b2008-03-22 00:47:35 +000067 def wait(self, child):
68 """Wait for child to finish, ignoring EINTR."""
69 while True:
70 try:
71 child.wait()
72 return
73 except OSError as e:
74 if e.errno != errno.EINTR:
75 raise
Fred Drake004d5e62000-10-23 17:22:08 +000076
Christian Heimes4fbc72b2008-03-22 00:47:35 +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
Christian Heimes4fbc72b2008-03-22 00:47:35 +000085 # Variables the signals will modify:
86 self.a_called = False
87 self.b_called = False
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000088
Christian Heimes4fbc72b2008-03-22 00:47:35 +000089 # Let the sub-processes know who to send signals to.
90 pid = os.getpid()
Benjamin Petersonee8712c2008-05-20 21:35:26 +000091 if support.verbose:
Thomas Woutersed03b412007-08-28 21:37:11 +000092 print("test runner's pid is", pid)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000093
Benjamin Petersonad9d48d2008-04-02 21:49: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.
Christian Heimes4fbc72b2008-03-22 00:47:35 +000099 self.assertTrue(self.a_called)
100 self.assertFalse(self.b_called)
101 self.a_called = False
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000102
Christian Heimes5e696852008-04-09 08:37:03 +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
Thomas Woutersed03b412007-08-28 21:37:11 +0000107 try:
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000108 child = subprocess.Popen(['kill', '-USR1', str(pid)])
109 # This wait should be interrupted by the signal's exception.
110 self.wait(child)
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000111 time.sleep(1) # Give the signal time to be delivered.
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000112 self.fail('HandlerBCalled exception not thrown')
113 except HandlerBCalled:
114 self.assertTrue(self.b_called)
115 self.assertFalse(self.a_called)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000116 if support.verbose:
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000117 print("HandlerBCalled exception caught")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000118
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000119 child = ignoring_eintr(subprocess.Popen, ['kill', '-USR2', str(pid)])
120 if child:
121 self.wait(child) # Nothing should happen.
Christian Heimes4fbc72b2008-03-22 00:47:35 +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()
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000129 # But if another signal arrives before the alarm, pause
130 # may return early.
131 time.sleep(1)
Thomas Woutersed03b412007-08-28 21:37:11 +0000132 except KeyboardInterrupt:
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000133 if support.verbose:
Thomas Woutersed03b412007-08-28 21:37:11 +0000134 print("KeyboardInterrupt (the alarm() went off)")
Thomas Woutersed03b412007-08-28 21:37:11 +0000135 except:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000136 self.fail("Some other exception woke us from pause: %s" %
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000137 traceback.format_exc())
138 else:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000139 self.fail("pause returned of its own accord, and the signal"
140 " didn't arrive after another second.")
Thomas Woutersed03b412007-08-28 21:37:11 +0000141
R. David Murrayedcfeba2010-04-21 01:51:57 +0000142 # Issue 3864, unknown if this affects earlier versions of freebsd also
143 @unittest.skipIf(sys.platform=='freebsd6',
144 'inter process signals not reliable (do not mix well with threading) '
145 'on freebsd6')
Christian Heimes4fbc72b2008-03-22 00:47:35 +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()
Raymond Hettinger686057b2009-06-04 00:11:54 +0000153 with closing(os.fdopen(os_done_r, 'rb')) as done_r, \
154 closing(os.fdopen(os_done_w, 'wb')) as done_w:
Christian Heimes4fbc72b2008-03-22 00:47:35 +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)
Thomas Woutersed03b412007-08-28 21:37:11 +0000187
188
189class BasicSignalTests(unittest.TestCase):
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000190 def trivial_signal_handler(self, *args):
191 pass
192
Thomas Woutersed03b412007-08-28 21:37:11 +0000193 def test_out_of_range_signal_number_raises_error(self):
194 self.assertRaises(ValueError, signal.getsignal, 4242)
195
Thomas Woutersed03b412007-08-28 21:37:11 +0000196 self.assertRaises(ValueError, signal.signal, 4242,
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000197 self.trivial_signal_handler)
Thomas Woutersed03b412007-08-28 21:37:11 +0000198
199 def test_setting_signal_handler_to_none_raises_error(self):
200 self.assertRaises(TypeError, signal.signal,
201 signal.SIGUSR1, None)
202
Christian Heimes4fbc72b2008-03-22 00:47:35 +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
Christian Heimes5fb7c2a2007-12-24 08:52:31 +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 Petersonc9c0f202009-06-30 23:06:06 +0000224 self.assertTrue(mid_time - before_time < self.TIMEOUT_HALF)
Christian Heimes5fb7c2a2007-12-24 08:52:31 +0000225 select.select([self.read], [], [], self.TIMEOUT_FULL)
226 after_time = time.time()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000227 self.assertTrue(after_time - mid_time < self.TIMEOUT_HALF)
Christian Heimes5fb7c2a2007-12-24 08:52:31 +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 Petersonc9c0f202009-06-30 23:06:06 +0000238 self.assertTrue(after_time - before_time < self.TIMEOUT_HALF)
Christian Heimes5fb7c2a2007-12-24 08:52:31 +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
Christian Heimes8640e742008-02-23 16:23:06 +0000256class SiginterruptTest(unittest.TestCase):
257 signum = signal.SIGUSR1
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000258
259 def setUp(self):
260 """Install a no-op signal handler that can be set to allow
261 interrupts or not, and arrange for the original signal handler to be
262 re-installed when the test is finished.
263 """
264 oldhandler = signal.signal(self.signum, lambda x,y: None)
265 self.addCleanup(signal.signal, self.signum, oldhandler)
266
267 def readpipe_interrupted(self):
268 """Perform a read during which a signal will arrive. Return True if the
269 read is interrupted by the signal and raises an exception. Return False
270 if it returns normally.
271 """
272 # Create a pipe that can be used for the read. Also clean it up
273 # when the test is over, since nothing else will (but see below for
274 # the write end).
Christian Heimes8640e742008-02-23 16:23:06 +0000275 r, w = os.pipe()
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000276 self.addCleanup(os.close, r)
277
278 # Create another process which can send a signal to this one to try
279 # to interrupt the read.
Christian Heimes8640e742008-02-23 16:23:06 +0000280 ppid = os.getpid()
281 pid = os.fork()
282
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000283 if pid == 0:
284 # Child code: sleep to give the parent enough time to enter the
285 # read() call (there's a race here, but it's really tricky to
286 # eliminate it); then signal the parent process. Also, sleep
287 # again to make it likely that the signal is delivered to the
288 # parent process before the child exits. If the child exits
289 # first, the write end of the pipe will be closed and the test
290 # is invalid.
Christian Heimes8640e742008-02-23 16:23:06 +0000291 try:
292 time.sleep(0.2)
293 os.kill(ppid, self.signum)
294 time.sleep(0.2)
295 finally:
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000296 # No matter what, just exit as fast as possible now.
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000297 exit_subprocess()
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000298 else:
299 # Parent code.
300 # Make sure the child is eventually reaped, else it'll be a
301 # zombie for the rest of the test suite run.
302 self.addCleanup(os.waitpid, pid, 0)
Christian Heimes8640e742008-02-23 16:23:06 +0000303
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000304 # Close the write end of the pipe. The child has a copy, so
305 # it's not really closed until the child exits. We need it to
306 # close when the child exits so that in the non-interrupt case
307 # the read eventually completes, otherwise we could just close
308 # it *after* the test.
Christian Heimes8640e742008-02-23 16:23:06 +0000309 os.close(w)
310
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000311 # Try the read and report whether it is interrupted or not to
312 # the caller.
Christian Heimes8640e742008-02-23 16:23:06 +0000313 try:
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000314 d = os.read(r, 1)
Christian Heimes8640e742008-02-23 16:23:06 +0000315 return False
316 except OSError as err:
317 if err.errno != errno.EINTR:
318 raise
319 return True
Christian Heimes8640e742008-02-23 16:23:06 +0000320
321 def test_without_siginterrupt(self):
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000322 """If a signal handler is installed and siginterrupt is not called
323 at all, when that signal arrives, it interrupts a syscall that's in
324 progress.
325 """
326 i = self.readpipe_interrupted()
327 self.assertTrue(i)
328 # Arrival of the signal shouldn't have changed anything.
329 i = self.readpipe_interrupted()
330 self.assertTrue(i)
Christian Heimes8640e742008-02-23 16:23:06 +0000331
332 def test_siginterrupt_on(self):
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000333 """If a signal handler is installed and siginterrupt is called with
334 a true value for the second argument, when that signal arrives, it
335 interrupts a syscall that's in progress.
336 """
337 signal.siginterrupt(self.signum, 1)
338 i = self.readpipe_interrupted()
339 self.assertTrue(i)
340 # Arrival of the signal shouldn't have changed anything.
341 i = self.readpipe_interrupted()
342 self.assertTrue(i)
Christian Heimes8640e742008-02-23 16:23:06 +0000343
344 def test_siginterrupt_off(self):
Jean-Paul Calderone6f137ca2010-05-09 03:18:57 +0000345 """If a signal handler is installed and siginterrupt is called with
346 a false value for the second argument, when that signal arrives, it
347 does not interrupt a syscall that's in progress.
348 """
349 signal.siginterrupt(self.signum, 0)
350 i = self.readpipe_interrupted()
351 self.assertFalse(i)
352 # Arrival of the signal shouldn't have changed anything.
353 i = self.readpipe_interrupted()
354 self.assertFalse(i)
355
356
Christian Heimes5fb7c2a2007-12-24 08:52:31 +0000357
Martin v. Löwis823725e2008-03-24 13:39:54 +0000358class ItimerTest(unittest.TestCase):
359 def setUp(self):
360 self.hndl_called = False
361 self.hndl_count = 0
362 self.itimer = None
Christian Heimescc47b052008-03-25 14:56:36 +0000363 self.old_alarm = signal.signal(signal.SIGALRM, self.sig_alrm)
Martin v. Löwis823725e2008-03-24 13:39:54 +0000364
365 def tearDown(self):
Christian Heimescc47b052008-03-25 14:56:36 +0000366 signal.signal(signal.SIGALRM, self.old_alarm)
Martin v. Löwis823725e2008-03-24 13:39:54 +0000367 if self.itimer is not None: # test_itimer_exc doesn't change this attr
368 # just ensure that itimer is stopped
369 signal.setitimer(self.itimer, 0)
370
371 def sig_alrm(self, *args):
372 self.hndl_called = True
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000373 if support.verbose:
Martin v. Löwis823725e2008-03-24 13:39:54 +0000374 print("SIGALRM handler invoked", args)
375
376 def sig_vtalrm(self, *args):
377 self.hndl_called = True
378
379 if self.hndl_count > 3:
380 # it shouldn't be here, because it should have been disabled.
381 raise signal.ItimerError("setitimer didn't disable ITIMER_VIRTUAL "
382 "timer.")
383 elif self.hndl_count == 3:
384 # disable ITIMER_VIRTUAL, this function shouldn't be called anymore
385 signal.setitimer(signal.ITIMER_VIRTUAL, 0)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000386 if support.verbose:
Martin v. Löwis823725e2008-03-24 13:39:54 +0000387 print("last SIGVTALRM handler call")
388
389 self.hndl_count += 1
390
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000391 if support.verbose:
Martin v. Löwis823725e2008-03-24 13:39:54 +0000392 print("SIGVTALRM handler invoked", args)
393
394 def sig_prof(self, *args):
395 self.hndl_called = True
396 signal.setitimer(signal.ITIMER_PROF, 0)
397
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000398 if support.verbose:
Martin v. Löwis823725e2008-03-24 13:39:54 +0000399 print("SIGPROF handler invoked", args)
400
401 def test_itimer_exc(self):
402 # XXX I'm assuming -1 is an invalid itimer, but maybe some platform
403 # defines it ?
404 self.assertRaises(signal.ItimerError, signal.setitimer, -1, 0)
Christian Heimescc47b052008-03-25 14:56:36 +0000405 # Negative times are treated as zero on some platforms.
406 if 0:
407 self.assertRaises(signal.ItimerError,
408 signal.setitimer, signal.ITIMER_REAL, -1)
Martin v. Löwis823725e2008-03-24 13:39:54 +0000409
410 def test_itimer_real(self):
411 self.itimer = signal.ITIMER_REAL
Martin v. Löwis823725e2008-03-24 13:39:54 +0000412 signal.setitimer(self.itimer, 1.0)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000413 if support.verbose:
Martin v. Löwis823725e2008-03-24 13:39:54 +0000414 print("\ncall pause()...")
415 signal.pause()
416
417 self.assertEqual(self.hndl_called, True)
418
R. David Murrayedcfeba2010-04-21 01:51:57 +0000419 # Issue 3864, unknown if this affects earlier versions of freebsd also
420 @unittest.skipIf(sys.platform=='freebsd6',
421 'itimer not reliable (does not mix well with threading) on freebsd6')
Martin v. Löwis823725e2008-03-24 13:39:54 +0000422 def test_itimer_virtual(self):
423 self.itimer = signal.ITIMER_VIRTUAL
424 signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
425 signal.setitimer(self.itimer, 0.3, 0.2)
426
Mark Dickinsona14c2ed2009-10-31 10:38:43 +0000427 start_time = time.time()
Stefan Krah0d2fa4a2010-04-20 08:13:03 +0000428 while time.time() - start_time < 60.0:
Mark Dickinson2e304402009-10-04 18:43:54 +0000429 # use up some virtual time by doing real work
430 _ = pow(12345, 67890, 10000019)
Martin v. Löwis823725e2008-03-24 13:39:54 +0000431 if signal.getitimer(self.itimer) == (0.0, 0.0):
432 break # sig_vtalrm handler stopped this itimer
Stefan Krah0d2fa4a2010-04-20 08:13:03 +0000433 else: # Issue 8424
Benjamin Peterson558cd642010-05-15 17:52:12 +0000434 self.skipTest("timeout: likely cause: machine too slow or load too "
435 "high")
Martin v. Löwis823725e2008-03-24 13:39:54 +0000436
437 # virtual itimer should be (0.0, 0.0) now
438 self.assertEquals(signal.getitimer(self.itimer), (0.0, 0.0))
439 # and the handler should have been called
440 self.assertEquals(self.hndl_called, True)
441
R. David Murrayedcfeba2010-04-21 01:51:57 +0000442 # Issue 3864, unknown if this affects earlier versions of freebsd also
443 @unittest.skipIf(sys.platform=='freebsd6',
444 'itimer not reliable (does not mix well with threading) on freebsd6')
Martin v. Löwis823725e2008-03-24 13:39:54 +0000445 def test_itimer_prof(self):
446 self.itimer = signal.ITIMER_PROF
447 signal.signal(signal.SIGPROF, self.sig_prof)
Neal Norwitzf5c7c2e2008-04-05 04:47:45 +0000448 signal.setitimer(self.itimer, 0.2, 0.2)
Martin v. Löwis823725e2008-03-24 13:39:54 +0000449
Mark Dickinsona14c2ed2009-10-31 10:38:43 +0000450 start_time = time.time()
Stefan Krah0d2fa4a2010-04-20 08:13:03 +0000451 while time.time() - start_time < 60.0:
Mark Dickinsona14c2ed2009-10-31 10:38:43 +0000452 # do some work
453 _ = pow(12345, 67890, 10000019)
Martin v. Löwis823725e2008-03-24 13:39:54 +0000454 if signal.getitimer(self.itimer) == (0.0, 0.0):
455 break # sig_prof handler stopped this itimer
Stefan Krah0d2fa4a2010-04-20 08:13:03 +0000456 else: # Issue 8424
Benjamin Peterson558cd642010-05-15 17:52:12 +0000457 self.skipTest("timeout: likely cause: machine too slow or load too "
458 "high")
Martin v. Löwis823725e2008-03-24 13:39:54 +0000459
Neal Norwitzf5c7c2e2008-04-05 04:47:45 +0000460 # profiling itimer should be (0.0, 0.0) now
461 self.assertEquals(signal.getitimer(self.itimer), (0.0, 0.0))
462 # and the handler should have been called
Martin v. Löwis823725e2008-03-24 13:39:54 +0000463 self.assertEqual(self.hndl_called, True)
464
Jean-Paul Calderone867c4352010-06-19 19:54:48 +0000465
466
467class SomeException(Exception):
468 """
469 A unique exception class to be raised by a signal handler to verify that the
470 signal handler was invoked.
471 """
472
473
474
475def raiser(*args):
476 """A signal handler which raises SomeException."""
477 raise SomeException()
478
479
480
481class SigprocmaskTests(unittest.TestCase):
482 """Tests for sigprocmask."""
483 def _handle_sigusr1(self):
484 old_handler = signal.signal(signal.SIGUSR1, raiser)
485 self.addCleanup(signal.signal, signal.SIGUSR1, old_handler)
486
487
488 def test_signature(self):
489 """When invoked with other than two arguments, sigprocmask raises
490 TypeError.
491 """
492 self.assertRaises(TypeError, signal.sigprocmask)
493 self.assertRaises(TypeError, signal.sigprocmask, 1)
494 self.assertRaises(TypeError, signal.sigprocmask, 1, 2, 3)
495
496
497 def test_invalid_how(self):
498 """If a value other than SIG_BLOCK, SIG_UNBLOCK, or SIG_SETMASK is
499 passed for the how argument to sigprocmask, ValueError is raised.
500 """
501 message = "value specified for how \(1700\) invalid"
502 with self.assertRaisesRegexp(ValueError, message):
503 signal.sigprocmask(1700, [])
504
505
506 def test_invalid_signal_iterable(self):
507 """If iterating over the value passed for the signals parameter to
508 sigprocmask raises an exception, sigprocmask raises that exception.
509 """
510 class BrokenIter(object):
511 def __iter__(self):
512 raise RuntimeError("my __iter__ is broken")
513 with self.assertRaisesRegexp(RuntimeError, "my __iter__ is broken"):
514 signal.sigprocmask(signal.SIG_BLOCK, BrokenIter())
515
516
517 def test_invalid_signal(self):
518 """If an object in the iterable passed for the signals parameter to
519 sigprocmask isn't an integer, TypeError is raised."""
520 with self.assertRaisesRegexp(TypeError, "an integer is required"):
521 signal.sigprocmask(signal.SIG_BLOCK, [object()])
522
523
524 def test_return_previous_mask(self):
525 """sigprocmask returns a list of the signals previously masked.
526 """
527 previous = signal.sigprocmask(signal.SIG_BLOCK, [1, 3, 5])
528 result = signal.sigprocmask(signal.SIG_BLOCK, previous)
529 self.assertEquals(result, [1, 3, 5])
530
531
532 def test_block(self):
533 """When invoked with SIG_BLOCK, sigprocmask blocks the signals in the
534 sigmask list.
535 """
536 self._handle_sigusr1()
537 previous = signal.sigprocmask(signal.SIG_BLOCK, [signal.SIGUSR1])
538 os.kill(os.getpid(), signal.SIGUSR1)
539 with self.assertRaises(SomeException):
540 # Expect to receive SIGUSR1 after unblocking it.
541 signal.sigprocmask(signal.SIG_SETMASK, previous)
542
543
544 def test_unblock(self):
545 """When invoked with SIG_UNBLOCK, sigprocmask unblocks the signals in
546 the sigmask list.
547 """
548 self._handle_sigusr1()
549 previous = signal.sigprocmask(signal.SIG_BLOCK, [signal.SIGUSR1])
550 self.addCleanup(signal.sigprocmask, signal.SIG_SETMASK, previous)
551 signal.sigprocmask(signal.SIG_UNBLOCK, [signal.SIGUSR1])
552
553 with self.assertRaises(SomeException):
554 os.kill(os.getpid(), signal.SIGUSR1)
555
556
557 def test_long_signals(self):
558 """sigprocmask accepts signal numbers as instances of long."""
559 previous = signal.sigprocmask(
560 signal.SIG_SETMASK, [long(signal.SIGUSR1), long(signal.SIGUSR2)])
561 masked = signal.sigprocmask(signal.SIG_SETMASK, previous)
562 self.assertEquals(masked, [signal.SIGUSR1, signal.SIGUSR2])
563
564
565
566class SignalfdTests(unittest.TestCase):
567 """
568 Tests for signal.signalfd.
569 """
570 def test_signature(self):
571 """When invoked with fewer than two arguments or more than three,
572 signalfd raises TypeError.
573 """
574 self.assertRaises(TypeError, signal.signalfd)
575 self.assertRaises(TypeError, signal.signalfd, 1)
576 self.assertRaises(TypeError, signal.signalfd, 1, 2, 3, 4)
577
578
579 def test_create_signalfd(self):
580 """When invoked with a file descriptor of -1, signalfd allocates a new
581 file descriptor for signal information delivery and returns it.
582 """
583 fd = signal.signalfd(-1, [])
584 self.assertTrue(isinstance(fd, int))
585 os.close(fd)
586
587
588 def test_non_iterable_signals(self):
589 """If an object which is not iterable is passed for the sigmask list
590 argument to signalfd, the exception raised by trying to iterate over
591 that object is raised.
592 """
593 self.assertRaises(TypeError, signal.signalfd, -1, object())
594
595
596 def test_non_integer_signals(self):
597 """If any non-integer values are included in the sigmask list argument
598 to signalfd, the exception raised by the attempt to convert them to an
599 integer is raised.
600 """
601 self.assertRaises(TypeError, signal.signalfd, -1, [object()])
602
603
604 def test_out_of_range_signal(self):
605 """If a signal number that is out of the valid range is included in the
606 sigmask list argument to signalfd, ValueError is raised.
607 """
608 message = "signal number -2 out of range"
609 with self.assertRaisesRegexp(ValueError, message):
610 signal.signalfd(-1, [-2])
611
612
613 def test_handle_signals(self):
614 """After signalfd is called, if a signal is received which was in the
615 sigmask list passed to that call, information about the signal can be
616 read from the fd returned by that call.
617 """
618 fd = signal.signalfd(-1, [signal.SIGUSR2])
619 self.addCleanup(os.close, fd)
620 previous = signal.sigprocmask(signal.SIG_BLOCK, [signal.SIGUSR2])
621 self.addCleanup(signal.sigprocmask, signal.SIG_SETMASK, previous)
622 os.kill(os.getpid(), signal.SIGUSR2)
623 bytes = os.read(fd, 128)
624 self.assertTrue(bytes)
625
626
627 def test_close_on_exec(self):
628 """If the bit mask passed as the 3rd argument to signalfd includes
629 SFD_CLOEXEC, the returned file descriptor has FD_CLOEXEC set on it.
630 """
631 import fcntl
632 fd = signal.signalfd(-1, [], signal.SFD_CLOEXEC)
633 self.addCleanup(os.close, fd)
634 flags = fcntl.fcntl(fd, fcntl.F_GETFD)
635 self.assertTrue(flags & fcntl.FD_CLOEXEC)
636
637
638 def test_nonblocking(self):
639 """If the bit mask passed as the 3rd argument to signalfd includes
640 SFD_NOBLOCK, the file description referenced by the returned file
641 descriptor has O_NONBLOCK set on it.
642 """
643 import fcntl
644 fd = signal.signalfd(-1, [], signal.SFD_NONBLOCK)
645 self.addCleanup(os.close, fd)
646 flags = fcntl.fcntl(fd, fcntl.F_GETFL)
647 self.assertTrue(flags & os.O_NONBLOCK)
648
649
650 def test_default_flags(self):
651 """If an empty bit mask is passed as the 3rd argument to signalfd,
652 neither FD_CLOEXEC nor O_NONBLOCK is set on the resulting file
653 descriptor/description.
654 """
655 import fcntl
656 fd = signal.signalfd(-1, [])
657 self.addCleanup(os.close, fd)
658 flags = fcntl.fcntl(fd, fcntl.F_GETFD)
659 self.assertFalse(flags & fcntl.FD_CLOEXEC)
660 flags = fcntl.fcntl(fd, fcntl.F_GETFL)
661 self.assertFalse(flags & os.O_NONBLOCK)
662
663
Thomas Woutersed03b412007-08-28 21:37:11 +0000664def test_main():
Jean-Paul Calderone867c4352010-06-19 19:54:48 +0000665 support.run_unittest(
666 BasicSignalTests, InterProcessSignalTests,
667 WakeupSignalTests, SiginterruptTest, ItimerTest, SignalfdTests,
668 SigprocmaskTests)
Thomas Woutersed03b412007-08-28 21:37:11 +0000669
670
671if __name__ == "__main__":
672 test_main()