Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bc80822

Browse files
committed
Issue #12303: Add sigwaitinfo() and sigtimedwait() to the signal module.
1 parent bb66972 commit bc80822

8 files changed

Lines changed: 295 additions & 23 deletions

File tree

Doc/library/signal.rst

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ The :mod:`signal` module defines the following functions:
179179
will then be called. Returns nothing. Not on Windows. (See the Unix man page
180180
:manpage:`signal(2)`.)
181181

182-
See also :func:`sigwait` and :func:`sigpending`.
182+
See also :func:`sigwait`, :func:`sigwaitinfo`, :func:`sigtimedwait` and
183+
:func:`sigpending`.
183184

184185

185186
.. function:: pthread_kill(thread_id, signum)
@@ -334,7 +335,47 @@ The :mod:`signal` module defines the following functions:
334335
Availability: Unix (see the man page :manpage:`sigwait(3)` for further
335336
information).
336337

337-
See also :func:`pause`, :func:`pthread_sigmask` and :func:`sigpending`.
338+
See also :func:`pause`, :func:`pthread_sigmask`, :func:`sigpending`,
339+
:func:`sigwaitinfo` and :func:`sigtimedwait`.
340+
341+
.. versionadded:: 3.3
342+
343+
344+
.. function:: sigwaitinfo(sigset)
345+
346+
Suspend execution of the calling thread until the delivery of one of the
347+
signals specified in the signal set *sigset*. The function accepts the
348+
signal and removes it from the pending list of signals. If one of the
349+
signals in *sigset* is already pending for the calling thread, the function
350+
will return immediately with information about that signal. The signal
351+
handler is not called for the delivered signal. The function raises an
352+
:exc:`OSError` with error number set to :const:`errno.EINTR` if it is
353+
interrupted by a signal that is not in *sigset*.
354+
355+
The return value is an object representing the data contained in the
356+
:c:type:`siginfo_t` structure, namely: :attr:`si_signo`, :attr:`si_code`,
357+
:attr:`si_errno`, :attr:`si_pid`, :attr:`si_uid`, :attr:`si_status`,
358+
:attr:`si_band`.
359+
360+
Availability: Unix (see the man page :manpage:`sigwaitinfo(2)` for further
361+
information).
362+
363+
See also :func:`pause`, :func:`sigwait` and :func:`sigtimedwait`.
364+
365+
.. versionadded:: 3.3
366+
367+
368+
.. function:: sigtimedwait(sigset, (timeout_sec, timeout_nsec))
369+
370+
Like :func:`sigtimedwait`, but takes a tuple of ``(seconds, nanoseconds)``
371+
as an additional argument specifying a timeout. If both *timeout_sec* and
372+
*timeout_nsec* are specified as :const:`0`, a poll is performed. Returns
373+
:const:`None` if a timeout occurs.
374+
375+
Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further
376+
information).
377+
378+
See also :func:`pause`, :func:`sigwait` and :func:`sigwaitinfo`.
338379

339380
.. versionadded:: 3.3
340381

Doc/whatsnew/3.3.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ signal
169169
* :func:`~signal.pthread_kill`: send a signal to a thread ;
170170
* :func:`~signal.sigpending`: examine pending functions ;
171171
* :func:`~signal.sigwait`: wait a signal.
172+
* :func:`~signal.sigwaitinfo`: wait for a signal, returning detailed
173+
information about it.
174+
* :func:`~signal.sigtimedwait`: like :func:`~signal.sigwaitinfo` but with a
175+
timeout.
172176

173177
* The signal handler writes the signal number as a single byte instead of
174178
a nul byte into the wakeup file descriptor. So it is possible to wait more

Lib/test/test_signal.py

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ class PendingSignalsTests(unittest.TestCase):
520520
functions.
521521
"""
522522
def setUp(self):
523+
self.hndl_called = False
523524
self.has_pthread_kill = hasattr(signal, 'pthread_kill')
524525

525526
def handler(self, signum, frame):
@@ -607,52 +608,118 @@ def test_pthread_kill(self):
607608
with self.assertRaises(ZeroDivisionError):
608609
signal.pthread_kill(current, signum)
609610

610-
@unittest.skipUnless(hasattr(signal, 'sigwait'),
611-
'need signal.sigwait()')
612611
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
613612
'need signal.pthread_sigmask()')
614613
@unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork()')
615-
def test_sigwait(self):
616-
def test(signum):
617-
signal.alarm(1)
618-
received = signal.sigwait([signum])
619-
if received != signum:
620-
print("sigwait() received %s, not %s"
621-
% (received, signum),
622-
file=sys.stderr)
623-
os._exit(1)
624-
614+
def wait_helper(self, test, handler, blocked=signal.SIGALRM):
625615
signum = signal.SIGALRM
626616

627-
# sigwait must be called with the signal blocked: since the current
617+
# sig*wait* must be called with the signal blocked: since the current
628618
# process might have several threads running, we fork() a child process
629619
# to have a single thread.
630620
pid = os.fork()
631621
if pid == 0:
632622
# child: block and wait the signal
633623
try:
634-
signal.signal(signum, self.handler)
635-
signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
624+
signal.signal(signum, handler)
625+
signal.pthread_sigmask(signal.SIG_BLOCK, [blocked])
636626

637627
# Do the tests
638628
test(signum)
639629

640630
# The handler must not be called on unblock
641631
try:
642-
signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])
632+
signal.pthread_sigmask(signal.SIG_UNBLOCK, [blocked])
643633
except ZeroDivisionError:
644634
print("the signal handler has been called",
645635
file=sys.stderr)
646636
os._exit(1)
647637
except BaseException as err:
648638
print("error: {}".format(err), file=sys.stderr)
639+
sys.stderr.flush()
649640
os._exit(1)
650641
else:
651642
os._exit(0)
652643
else:
653644
# parent: check that the child correcty received the signal
654645
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
655646

647+
@unittest.skipUnless(hasattr(signal, 'sigwait'),
648+
'need signal.sigwait()')
649+
def test_sigwait(self):
650+
def test(signum):
651+
signal.alarm(1)
652+
self.assertEqual(signum, signal.sigwait([signum]))
653+
654+
self.wait_helper(test, self.handler)
655+
656+
@unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
657+
'need signal.sigwaitinfo()')
658+
def test_sigwaitinfo(self):
659+
def test(signum):
660+
signal.alarm(1)
661+
info = signal.sigwaitinfo([signum])
662+
self.assertEqual(signum, info.si_signo)
663+
664+
self.wait_helper(test, self.handler)
665+
666+
@unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
667+
'need signal.sigtimedwait()')
668+
def test_sigtimedwait(self):
669+
def test(signum):
670+
signal.alarm(1)
671+
info = signal.sigtimedwait([signum], (10, 1000))
672+
self.assertEqual(signum, info.si_signo)
673+
674+
self.wait_helper(test, self.handler)
675+
676+
# check that polling with sigtimedwait works
677+
@unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
678+
'need signal.sigtimedwait()')
679+
def test_sigtimedwait_poll(self):
680+
def test(signum):
681+
self.kill(signum)
682+
info = signal.sigtimedwait([signum], (0, 0))
683+
self.assertEqual(signum, info.si_signo)
684+
685+
self.wait_helper(test, self.handler)
686+
687+
@unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
688+
'need signal.sigtimedwait()')
689+
def test_sigtimedwait_timeout(self):
690+
def test(signum):
691+
self.assertEqual(None, signal.sigtimedwait([signum], (1, 35500)))
692+
693+
self.wait_helper(test, self.handler)
694+
695+
@unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
696+
'need signal.sigtimedwait()')
697+
def test_sigtimedwait_negative_timeout(self):
698+
signum = signal.SIGALRM
699+
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, -1))
700+
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (0, -1))
701+
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, 0))
702+
703+
def alarm_handler(self, signum, frame):
704+
self.hndl_called = True
705+
706+
@unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
707+
'need signal.sigwaitinfo()')
708+
def test_sigwaitinfo_interrupted(self):
709+
def test(signum):
710+
signal.alarm(1)
711+
try:
712+
signal.sigwaitinfo([signal.SIGUSR1])
713+
except OSError as e:
714+
if e.errno == errno.EINTR:
715+
self.assertTrue(self.hndl_called)
716+
else:
717+
self.fail("Expected EINTR to be raised by sigwaitinfo")
718+
else:
719+
self.fail("Expected EINTR to be raised by sigwaitinfo")
720+
721+
self.wait_helper(test, self.alarm_handler, signal.SIGUSR1)
722+
656723
@unittest.skipUnless(hasattr(signal, 'sigwait'),
657724
'need signal.sigwait()')
658725
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ Core and Builtins
200200
Library
201201
-------
202202

203+
- Issue #12303: Add sigwaitinfo() and sigtimedwait() to the signal module.
204+
203205
- Issue #12404: Remove C89 incompatible code from mmap module. Patch by Akira
204206
Kitada.
205207

0 commit comments

Comments
 (0)