Skip to content

Commit 3a89913

Browse files
committed
Issue #22018: On Windows, signal.set_wakeup_fd() now also supports sockets.
A side effect is that Python depends to the WinSock library.
1 parent cad511c commit 3a89913

6 files changed

Lines changed: 294 additions & 29 deletions

File tree

Doc/c-api/exceptions.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,13 +443,18 @@ in various ways. There is a separate error indicator for each thread.
443443
444444
.. c:function:: int PySignal_SetWakeupFd(int fd)
445445
446-
This utility function specifies a file descriptor to which a ``'\0'`` byte will
447-
be written whenever a signal is received. It returns the previous such file
448-
descriptor. The value ``-1`` disables the feature; this is the initial state.
446+
This utility function specifies a file descriptor to which the signal number
447+
is written as a single byte whenever a signal is received. *fd* must be
448+
non-blocking. It returns the previous such file descriptor.
449+
450+
The value ``-1`` disables the feature; this is the initial state.
449451
This is equivalent to :func:`signal.set_wakeup_fd` in Python, but without any
450452
error checking. *fd* should be a valid file descriptor. The function should
451453
only be called from the main thread.
452454
455+
.. versionchanged:: 3.5
456+
On Windows, the function now also supports socket handles.
457+
453458
454459
.. c:function:: PyObject* PyErr_NewException(char *name, PyObject *base, PyObject *dict)
455460

Doc/library/signal.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ The :mod:`signal` module defines the following functions:
318318
attempting to call it from other threads will cause a :exc:`ValueError`
319319
exception to be raised.
320320

321+
.. versionchanged:: 3.5
322+
On Windows, the function now also supports socket handles.
323+
321324

322325
.. function:: siginterrupt(signalnum, flag)
323326

Lib/test/test_signal.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pickle
77
import select
88
import signal
9+
import socket
910
import struct
1011
import subprocess
1112
import traceback
@@ -255,6 +256,13 @@ def test_invalid_fd(self):
255256
self.assertRaises((ValueError, OSError),
256257
signal.set_wakeup_fd, fd)
257258

259+
def test_invalid_socket(self):
260+
sock = socket.socket()
261+
fd = sock.fileno()
262+
sock.close()
263+
self.assertRaises((ValueError, OSError),
264+
signal.set_wakeup_fd, fd)
265+
258266
def test_set_wakeup_fd_result(self):
259267
r1, w1 = os.pipe()
260268
self.addCleanup(os.close, r1)
@@ -268,6 +276,20 @@ def test_set_wakeup_fd_result(self):
268276
self.assertEqual(signal.set_wakeup_fd(-1), w2)
269277
self.assertEqual(signal.set_wakeup_fd(-1), -1)
270278

279+
def test_set_wakeup_fd_socket_result(self):
280+
sock1 = socket.socket()
281+
self.addCleanup(sock1.close)
282+
fd1 = sock1.fileno()
283+
284+
sock2 = socket.socket()
285+
self.addCleanup(sock2.close)
286+
fd2 = sock2.fileno()
287+
288+
signal.set_wakeup_fd(fd1)
289+
self.assertIs(signal.set_wakeup_fd(fd2), fd1)
290+
self.assertIs(signal.set_wakeup_fd(-1), fd2)
291+
self.assertIs(signal.set_wakeup_fd(-1), -1)
292+
271293

272294
@unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
273295
class WakeupSignalTests(unittest.TestCase):
@@ -435,6 +457,90 @@ def test_pending(self):
435457
""", signal.SIGUSR1, signal.SIGUSR2, ordered=False)
436458

437459

460+
@unittest.skipUnless(hasattr(socket, 'socketpair'), 'need socket.socketpair')
461+
class WakeupSocketSignalTests(unittest.TestCase):
462+
463+
@unittest.skipIf(_testcapi is None, 'need _testcapi')
464+
def test_socket(self):
465+
# use a subprocess to have only one thread
466+
code = """if 1:
467+
import signal
468+
import socket
469+
import struct
470+
import _testcapi
471+
472+
signum = signal.SIGINT
473+
signals = (signum,)
474+
475+
def handler(signum, frame):
476+
pass
477+
478+
signal.signal(signum, handler)
479+
480+
read, write = socket.socketpair()
481+
read.setblocking(False)
482+
write.setblocking(False)
483+
signal.set_wakeup_fd(write.fileno())
484+
485+
_testcapi.raise_signal(signum)
486+
487+
data = read.recv(1)
488+
if not data:
489+
raise Exception("no signum written")
490+
raised = struct.unpack('B', data)
491+
if raised != signals:
492+
raise Exception("%r != %r" % (raised, signals))
493+
494+
read.close()
495+
write.close()
496+
"""
497+
498+
assert_python_ok('-c', code)
499+
500+
@unittest.skipIf(_testcapi is None, 'need _testcapi')
501+
def test_send_error(self):
502+
# Use a subprocess to have only one thread.
503+
if os.name == 'nt':
504+
action = 'send'
505+
else:
506+
action = 'write'
507+
code = """if 1:
508+
import errno
509+
import signal
510+
import socket
511+
import sys
512+
import time
513+
import _testcapi
514+
from test.support import captured_stderr
515+
516+
signum = signal.SIGINT
517+
518+
def handler(signum, frame):
519+
pass
520+
521+
signal.signal(signum, handler)
522+
523+
read, write = socket.socketpair()
524+
read.setblocking(False)
525+
write.setblocking(False)
526+
527+
signal.set_wakeup_fd(write.fileno())
528+
529+
# Close sockets: send() will fail
530+
read.close()
531+
write.close()
532+
533+
with captured_stderr() as err:
534+
_testcapi.raise_signal(signum)
535+
536+
err = err.getvalue()
537+
if ('Exception ignored when trying to {action} to the signal wakeup fd'
538+
not in err):
539+
raise AssertionError(err)
540+
""".format(action=action)
541+
assert_python_ok('-c', code)
542+
543+
438544
@unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
439545
class SiginterruptTest(unittest.TestCase):
440546

@@ -984,6 +1090,7 @@ def test_main():
9841090
try:
9851091
support.run_unittest(GenericTests, PosixTests, InterProcessSignalTests,
9861092
WakeupFDTests, WakeupSignalTests,
1093+
WakeupSocketSignalTests,
9871094
SiginterruptTest, ItimerTest, WindowsSignalTests,
9881095
PendingSignalsTests)
9891096
finally:

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ Core and Builtins
113113
Library
114114
-------
115115

116+
- Issue #22018: On Windows, signal.set_wakeup_fd() now also supports sockets.
117+
A side effect is that Python depends to the WinSock library.
118+
116119
- Issue #22054: Add os.get_blocking() and os.set_blocking() functions to get
117120
and set the blocking mode of a file descriptor (False if the O_NONBLOCK flag
118121
is set, True otherwise). These functions are not available on Windows.

0 commit comments

Comments
 (0)