diff options
Diffstat (limited to 'Lib/test/test_signal.py')
| -rw-r--r-- | Lib/test/test_signal.py | 72 |
1 files changed, 71 insertions, 1 deletions
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index f64bd4c..0a43fa7 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -483,11 +483,81 @@ class ItimerTest(unittest.TestCase): # and the handler should have been called self.assertEqual(self.hndl_called, True) + +@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), + 'need signal.pthread_sigmask()') +class PthreadSigmaskTests(unittest.TestCase): + def test_arguments(self): + self.assertRaises(TypeError, signal.pthread_sigmask) + self.assertRaises(TypeError, signal.pthread_sigmask, 1) + self.assertRaises(TypeError, signal.pthread_sigmask, 1, 2, 3) + self.assertRaises(RuntimeError, signal.pthread_sigmask, 1700, []) + + def test_block_unlock(self): + import faulthandler + pid = os.getpid() + signum = signal.SIGUSR1 + + def handler(signum, frame): + 1/0 + + def read_sigmask(): + return signal.pthread_sigmask(signal.SIG_BLOCK, []) + + # The fault handler timeout thread masks all signals. If the main + # thread masks also SIGUSR1, all threads mask this signal. In this + # case, if we send SIGUSR1 to the process, the signal is pending in the + # main or the faulthandler timeout thread. Unblock SIGUSR1 in the main + # thread calls the signal handler only if the signal is pending for the + # main thread. + # + # Stop the faulthandler timeout thread to workaround this problem. + # Another solution would be to send the signal directly to the main + # thread using pthread_kill(), but Python doesn't expose this + # function. + faulthandler.cancel_dump_tracebacks_later() + + # Install our signal handler + old_handler = signal.signal(signum, handler) + self.addCleanup(signal.signal, signum, old_handler) + + # Unblock SIGUSR1 (and copy the old mask) to test our signal handler + old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) + self.addCleanup(signal.pthread_sigmask, signal.SIG_SETMASK, old_mask) + with self.assertRaises(ZeroDivisionError): + os.kill(pid, signum) + + # Block and then raise SIGUSR1. The signal is blocked: the signal + # handler is not called, and the signal is now pending + signal.pthread_sigmask(signal.SIG_BLOCK, [signum]) + os.kill(pid, signum) + + # Check the new mask + blocked = read_sigmask() + self.assertIn(signum, blocked) + self.assertEqual(set(old_mask) ^ set(blocked), {signum}) + + # Unblock SIGUSR1 + with self.assertRaises(ZeroDivisionError): + # unblock the pending signal calls immediatly the signal handler + signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) + with self.assertRaises(ZeroDivisionError): + os.kill(pid, signum) + + # Check the new mask + unblocked = read_sigmask() + self.assertNotIn(signum, unblocked) + self.assertEqual(set(blocked) ^ set(unblocked), {signum}) + self.assertSequenceEqual(old_mask, unblocked) + # Finally, restore the previous signal handler and the signal mask + + def test_main(): try: support.run_unittest(BasicSignalTests, InterProcessSignalTests, WakeupSignalTests, SiginterruptTest, - ItimerTest, WindowsSignalTests) + ItimerTest, WindowsSignalTests, + PthreadSigmaskTests) finally: support.reap_children() |
