Skip to content

Commit 5ad9543

Browse files
committed
Simplify signals code; use siginterrupt
* new code is a bit easier to reason about; * remove_signal_handler() will now shutdown signals listening machinery when the last signal is removed; * add_signal_handler() uses signal.siginterrupt() to auto-restart syscalls when a signal is received (generating less EINTRs.)
1 parent ddceca8 commit 5ad9543

File tree

3 files changed

+50
-15
lines changed

3 files changed

+50
-15
lines changed

uvloop/includes/stdlib.pxi

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import gc
99
import inspect
1010
import itertools
1111
import os
12-
import signal as std_signal
12+
import signal
1313
import socket
1414
import subprocess
1515
import ssl
@@ -123,11 +123,12 @@ cdef int subprocess_STDOUT = subprocess.STDOUT
123123
cdef int subprocess_DEVNULL = subprocess.DEVNULL
124124
cdef subprocess_SubprocessError = subprocess.SubprocessError
125125

126-
cdef int signal_NSIG = std_signal.NSIG
127-
cdef signal_signal = std_signal.signal
128-
cdef signal_set_wakeup_fd = std_signal.set_wakeup_fd
129-
cdef signal_default_int_handler = std_signal.default_int_handler
130-
cdef signal_SIG_DFL = std_signal.SIG_DFL
126+
cdef int signal_NSIG = signal.NSIG
127+
cdef signal_signal = signal.signal
128+
cdef signal_siginterrupt = signal.siginterrupt
129+
cdef signal_set_wakeup_fd = signal.set_wakeup_fd
130+
cdef signal_default_int_handler = signal.default_int_handler
131+
cdef signal_SIG_DFL = signal.SIG_DFL
131132

132133
cdef time_sleep = time.sleep
133134
cdef time_monotonic = time.monotonic
@@ -148,5 +149,5 @@ cdef py_inf = float('inf')
148149
# so we delete refs to all modules manually (except sys)
149150
del asyncio, concurrent, collections, errno
150151
del functools, inspect, itertools, socket, os, threading
151-
del std_signal, subprocess, ssl
152+
del signal, subprocess, ssl
152153
del time, traceback, warnings, weakref

uvloop/loop.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ cdef class Loop:
6161
dict _signal_handlers
6262
object _ssock
6363
object _csock
64+
bint _listening_signals
6465

6566
set _timers
6667
dict _polls

uvloop/loop.pyx

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ cdef class Loop:
149149
<method_t>self._exec_queued_writes, self))
150150

151151
self._ssock = self._csock = None
152-
self._signal_handlers = None
152+
self._signal_handlers = {}
153+
self._listening_signals = False
153154

154155
self._coroutine_wrapper_set = False
155156

@@ -225,19 +226,22 @@ cdef class Loop:
225226
self._debug_exception_handler_cnt = 0
226227

227228
cdef _setup_signals(self):
229+
if self._listening_signals:
230+
return
231+
228232
self._ssock, self._csock = socket_socketpair()
229233
self._ssock.setblocking(False)
230234
self._csock.setblocking(False)
231235
try:
232236
signal_set_wakeup_fd(self._csock.fileno())
233-
except ValueError:
237+
except (OSError, ValueError):
234238
# Not the main thread
235239
self._ssock.close()
236240
self._csock.close()
237241
self._ssock = self._csock = None
238242
return
239243

240-
self._signal_handlers = {}
244+
self._listening_signals = True
241245

242246
cdef _recv_signals_start(self):
243247
if self._ssock is None:
@@ -261,14 +265,29 @@ cdef class Loop:
261265
self._remove_reader(self._ssock)
262266

263267
cdef _shutdown_signals(self):
264-
if self._signal_handlers is None:
268+
if not self._listening_signals:
265269
return
270+
266271
for sig in list(self._signal_handlers):
267272
self.remove_signal_handler(sig)
268-
signal_set_wakeup_fd(-1)
273+
274+
if not self._listening_signals:
275+
# `remove_signal_handler` will call `_shutdown_signals` when
276+
# removing last signal handler.
277+
return
278+
279+
try:
280+
signal_set_wakeup_fd(-1)
281+
except (ValueError, OSError) as exc:
282+
aio_logger.info('set_wakeup_fd(-1) failed: %s', exc)
283+
269284
self._remove_reader(self._ssock)
270285
self._ssock.close()
271286
self._csock.close()
287+
self._ssock = None
288+
self._csock = None
289+
290+
self._listening_signals = False
272291

273292
cdef _read_from_self(self):
274293
while True:
@@ -2415,9 +2434,9 @@ cdef class Loop:
24152434
cdef:
24162435
Handle h
24172436

2418-
if self._signal_handlers is None:
2437+
if not self._listening_signals:
24192438
self._setup_signals()
2420-
if self._signal_handlers is None:
2439+
if not self._listening_signals:
24212440
raise ValueError('set_wakeup_fd only works in main thread')
24222441

24232442
if (aio_iscoroutine(callback)
@@ -2441,9 +2460,20 @@ cdef class Loop:
24412460
self._signal_handlers[sig] = h
24422461

24432462
try:
2463+
# Register a dummy signal handler to ask Python to write the signal
2464+
# number in the wakeup file descriptor.
24442465
signal_signal(sig, _sighandler_noop)
2466+
2467+
# Set SA_RESTART to limit EINTR occurrences.
2468+
signal_siginterrupt(sig, False)
24452469
except OSError as exc:
24462470
del self._signal_handlers[sig]
2471+
if not self._signal_handlers:
2472+
try:
2473+
signal_set_wakeup_fd(-1)
2474+
except (ValueError, OSError) as nexc:
2475+
aio_logger.info('set_wakeup_fd(-1) failed: %s', nexc)
2476+
24472477
if exc.errno == errno_EINVAL:
24482478
raise RuntimeError('sig {} cannot be caught'.format(sig))
24492479
else:
@@ -2456,7 +2486,7 @@ cdef class Loop:
24562486
"""
24572487
self._check_signal(sig)
24582488

2459-
if self._signal_handlers is None:
2489+
if not self._listening_signals:
24602490
return False
24612491

24622492
try:
@@ -2477,6 +2507,9 @@ cdef class Loop:
24772507
else:
24782508
raise
24792509

2510+
if not self._signal_handlers:
2511+
self._shutdown_signals()
2512+
24802513
return True
24812514

24822515
async def create_datagram_endpoint(self, protocol_factory,

0 commit comments

Comments
 (0)