mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									b3e7219abf
								
							
						
					
					
						commit
						d49b1f14de
					
				
					 5 changed files with 35 additions and 5 deletions
				
			
		| 
						 | 
					@ -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
 | 
				
			||||||
  * :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
 | 
				
			||||||
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):
 | 
				
			||||||
    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):
 | 
				
			||||||
        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):
 | 
				
			||||||
            [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
 | 
				
			||||||
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)
 | 
				
			||||||
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)
 | 
				
			||||||
       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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue