Commit d49b1f14 authored by Victor Stinner's avatar Victor Stinner

Issue #8407: The signal handler writes the signal number as a single byte

instead of a nul byte into the wakeup file descriptor. So it is possible to
wait more than one signal and know which signals were raised.
parent b3e7219a
...@@ -262,13 +262,17 @@ The :mod:`signal` module defines the following functions: ...@@ -262,13 +262,17 @@ The :mod:`signal` module defines the following functions:
.. function:: set_wakeup_fd(fd) .. function:: set_wakeup_fd(fd)
Set the wakeup fd to *fd*. When a signal is received, a ``'\0'`` byte is Set the wakeup file descriptor to *fd*. When a signal is received, the
written to the fd. This can be used by a library to wakeup a poll or select signal number is written as a single byte into the fd. This can be used by
call, allowing the signal to be fully processed. a library to wakeup a poll or select call, allowing the signal to be fully
processed.
The old wakeup fd is returned. *fd* must be non-blocking. It is up to the The old wakeup fd is returned. *fd* must be non-blocking. It is up to the
library to remove any bytes before calling poll or select again. library to remove any bytes before calling poll or select again.
Use for example ``struct.unpack('%uB' % len(data), data)`` to decode the
signal numbers list.
When threads are enabled, this function can only be called from the main thread; When threads are enabled, this function can only be called from the main thread;
attempting to call it from other threads will cause a :exc:`ValueError` attempting to call it from other threads will cause a :exc:`ValueError`
exception to be raised. exception to be raised.
......
...@@ -131,6 +131,10 @@ signal ...@@ -131,6 +131,10 @@ signal
* :func:`~signal.sigpending`: examine pending functions ; * :func:`~signal.sigpending`: examine pending functions ;
* :func:`~signal.sigwait`: wait a signal. * :func:`~signal.sigwait`: wait a signal.
* The signal handler writes the signal number as a single byte instead of
a nul byte into the wakeup file descriptor. So it is possible to wait more
than one signal and know which signals were raised.
Optimizations Optimizations
============= =============
......
...@@ -5,6 +5,7 @@ import gc ...@@ -5,6 +5,7 @@ import gc
import pickle import pickle
import select import select
import signal import signal
import struct
import subprocess import subprocess
import traceback import traceback
import sys, os, time, errno import sys, os, time, errno
...@@ -236,6 +237,11 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -236,6 +237,11 @@ class WakeupSignalTests(unittest.TestCase):
TIMEOUT_FULL = 10 TIMEOUT_FULL = 10
TIMEOUT_HALF = 5 TIMEOUT_HALF = 5
def check_signum(self, *signals):
data = os.read(self.read, len(signals)+1)
raised = struct.unpack('%uB' % len(data), data)
self.assertSequenceEqual(raised, signals)
def test_wakeup_fd_early(self): def test_wakeup_fd_early(self):
import select import select
...@@ -249,6 +255,7 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -249,6 +255,7 @@ class WakeupSignalTests(unittest.TestCase):
select.select([self.read], [], [], self.TIMEOUT_FULL) select.select([self.read], [], [], self.TIMEOUT_FULL)
after_time = time.time() after_time = time.time()
self.assertTrue(after_time - mid_time < self.TIMEOUT_HALF) self.assertTrue(after_time - mid_time < self.TIMEOUT_HALF)
self.check_signum(signal.SIGALRM)
def test_wakeup_fd_during(self): def test_wakeup_fd_during(self):
import select import select
...@@ -260,6 +267,14 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -260,6 +267,14 @@ class WakeupSignalTests(unittest.TestCase):
[self.read], [], [], self.TIMEOUT_FULL) [self.read], [], [], self.TIMEOUT_FULL)
after_time = time.time() after_time = time.time()
self.assertTrue(after_time - before_time < self.TIMEOUT_HALF) self.assertTrue(after_time - before_time < self.TIMEOUT_HALF)
self.check_signum(signal.SIGALRM)
def test_signum(self):
old_handler = signal.signal(signal.SIGUSR1, lambda x,y:None)
self.addCleanup(signal.signal, signal.SIGUSR1, old_handler)
os.kill(os.getpid(), signal.SIGUSR1)
os.kill(os.getpid(), signal.SIGALRM)
self.check_signum(signal.SIGUSR1, signal.SIGALRM)
def setUp(self): def setUp(self):
import fcntl import fcntl
......
...@@ -140,6 +140,10 @@ Core and Builtins ...@@ -140,6 +140,10 @@ Core and Builtins
Library Library
------- -------
- Issue #8407: The signal handler writes the signal number as a single byte
instead of a nul byte into the wakeup file descriptor. So it is possible to
wait more than one signal and know which signals were raised.
- Issue #8407: Add pthread_kill(), sigpending() and sigwait() functions to the - Issue #8407: Add pthread_kill(), sigpending() and sigwait() functions to the
signal module. signal module.
......
...@@ -176,6 +176,7 @@ checksignals_witharg(void * unused) ...@@ -176,6 +176,7 @@ checksignals_witharg(void * unused)
static void static void
trip_signal(int sig_num) trip_signal(int sig_num)
{ {
unsigned char byte;
Handlers[sig_num].tripped = 1; Handlers[sig_num].tripped = 1;
if (is_tripped) if (is_tripped)
return; return;
...@@ -183,8 +184,10 @@ trip_signal(int sig_num) ...@@ -183,8 +184,10 @@ trip_signal(int sig_num)
cleared in PyErr_CheckSignals() before .tripped. */ cleared in PyErr_CheckSignals() before .tripped. */
is_tripped = 1; is_tripped = 1;
Py_AddPendingCall(checksignals_witharg, NULL); Py_AddPendingCall(checksignals_witharg, NULL);
if (wakeup_fd != -1) if (wakeup_fd != -1) {
write(wakeup_fd, "\0", 1); byte = (unsigned char)sig_num;
write(wakeup_fd, &byte, 1);
}
} }
static void static void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment