@@ -167,6 +167,7 @@ cdef class Loop:
167
167
self ._ssock = self ._csock = None
168
168
self ._signal_handlers = {}
169
169
self ._listening_signals = False
170
+ self ._old_signal_wakeup_id = - 1
170
171
171
172
self ._coroutine_debug_set = False
172
173
@@ -183,6 +184,9 @@ cdef class Loop:
183
184
184
185
self ._servers = set ()
185
186
187
+ cdef inline _is_main_thread(self ):
188
+ return MAIN_THREAD_ID == PyThread_get_thread_ident()
189
+
186
190
def __init__ (self ):
187
191
self .set_debug((not sys_ignore_environment
188
192
and bool (os_environ.get(' PYTHONASYNCIODEBUG' ))))
@@ -241,34 +245,40 @@ cdef class Loop:
241
245
242
246
self ._debug_exception_handler_cnt = 0
243
247
244
- cdef _setup_signals(self ):
245
- cdef int old_wakeup_fd
248
+ cdef _setup_or_resume_signals(self ):
249
+ if not self ._is_main_thread():
250
+ return
246
251
247
252
if self ._listening_signals:
248
- return
253
+ raise RuntimeError (' signals handling has been already setup' )
254
+
255
+ if self ._ssock is not None :
256
+ raise RuntimeError (' self-pipe exists before loop run' )
257
+
258
+ # Create a self-pipe and call set_signal_wakeup_fd() with one
259
+ # of its ends. This is needed so that libuv knows that it needs
260
+ # to wakeup on ^C (no matter if the SIGINT handler is still the
261
+ # standard Python's one or or user set their own.)
249
262
250
263
self ._ssock, self ._csock = socket_socketpair()
251
- self ._ssock.setblocking(False )
252
- self ._csock.setblocking(False )
253
264
try :
254
- old_wakeup_fd = _set_signal_wakeup_fd(self ._csock.fileno())
255
- except (OSError , ValueError ):
256
- # Not the main thread
265
+ self ._ssock.setblocking(False )
266
+ self ._csock.setblocking(False )
267
+
268
+ fileno = self ._csock.fileno()
269
+
270
+ self ._old_signal_wakeup_id = _set_signal_wakeup_fd(fileno)
271
+ except Exception :
272
+ # Out of all statements in the try block, only the
273
+ # "_set_signal_wakeup_fd()" call can fail, but it shouldn't,
274
+ # as we ensure that the current thread is the main thread.
275
+ # Still, if something goes horribly wrong we want to clean up
276
+ # the socket pair.
257
277
self ._ssock.close()
258
278
self ._csock.close()
259
- self ._ssock = self ._csock = None
260
- return
261
-
262
- self ._listening_signals = True
263
- return old_wakeup_fd
264
-
265
- cdef _recv_signals_start(self ):
266
- cdef object old_wakeup_fd = None
267
- if self ._ssock is None :
268
- old_wakeup_fd = self ._setup_signals()
269
- if self ._ssock is None :
270
- # Not the main thread.
271
- return
279
+ self ._ssock = None
280
+ self ._csock = None
281
+ raise
272
282
273
283
self ._add_reader(
274
284
self ._ssock,
@@ -277,38 +287,49 @@ cdef class Loop:
277
287
" Loop._read_from_self" ,
278
288
< method_t> self ._read_from_self,
279
289
self ))
280
- return old_wakeup_fd
281
290
282
- cdef _recv_signals_stop(self ):
283
- if self ._ssock is None :
284
- return
291
+ self ._listening_signals = True
285
292
286
- self ._remove_reader(self ._ssock)
293
+ cdef _pause_signals(self ):
294
+ if not self ._is_main_thread():
295
+ if self ._listening_signals:
296
+ raise RuntimeError (
297
+ ' cannot pause signals handling; no longer running in '
298
+ ' the main thread' )
299
+ else :
300
+ return
287
301
288
- cdef _shutdown_signals(self ):
289
302
if not self ._listening_signals:
290
- return
303
+ raise RuntimeError ( ' signals handling has not been setup ' )
291
304
292
- for sig in list (self ._signal_handlers):
293
- self .remove_signal_handler(sig)
294
-
295
- if not self ._listening_signals:
296
- # `remove_signal_handler` will call `_shutdown_signals` when
297
- # removing last signal handler.
298
- return
305
+ self ._listening_signals = False
299
306
300
- try :
301
- signal_set_wakeup_fd(- 1 )
302
- except (ValueError , OSError ) as exc:
303
- aio_logger.info(' set_wakeup_fd(-1) failed: %s ' , exc)
307
+ _set_signal_wakeup_fd(self ._old_signal_wakeup_id)
304
308
305
309
self ._remove_reader(self ._ssock)
306
310
self ._ssock.close()
307
311
self ._csock.close()
308
312
self ._ssock = None
309
313
self ._csock = None
310
314
311
- self ._listening_signals = False
315
+ cdef _shutdown_signals(self ):
316
+ if not self ._is_main_thread():
317
+ if self ._signal_handlers:
318
+ aio_logger.warning(
319
+ ' cannot cleanup signal handlers: closing the event loop '
320
+ ' in a non-main OS thread' )
321
+ return
322
+
323
+ if self ._listening_signals:
324
+ raise RuntimeError (
325
+ ' cannot shutdown signals handling as it has not been paused' )
326
+
327
+ if self ._ssock:
328
+ raise RuntimeError (
329
+ ' self-pipe was not cleaned up after loop was run' )
330
+
331
+ for sig in list (self ._signal_handlers):
332
+ self .remove_signal_handler(sig)
312
333
313
334
def __sighandler (self , signum , frame ):
314
335
self ._signals.add(signum)
@@ -451,7 +472,6 @@ cdef class Loop:
451
472
452
473
cdef _run(self , uv.uv_run_mode mode):
453
474
cdef int err
454
- cdef object old_wakeup_fd
455
475
456
476
if self ._closed == 1 :
457
477
raise RuntimeError (' unable to start the loop; it was closed' )
@@ -474,7 +494,7 @@ cdef class Loop:
474
494
self .handler_check__exec_writes.start()
475
495
self .handler_idle.start()
476
496
477
- old_wakeup_fd = self ._recv_signals_start ()
497
+ self ._setup_or_resume_signals ()
478
498
479
499
if aio_set_running_loop is not None :
480
500
aio_set_running_loop(self )
@@ -484,13 +504,11 @@ cdef class Loop:
484
504
if aio_set_running_loop is not None :
485
505
aio_set_running_loop(None )
486
506
487
- self ._recv_signals_stop()
488
- if old_wakeup_fd is not None :
489
- signal_set_wakeup_fd(old_wakeup_fd)
490
-
491
507
self .handler_check__exec_writes.stop()
492
508
self .handler_idle.stop()
493
509
510
+ self ._pause_signals()
511
+
494
512
self ._thread_is_main = 0
495
513
self ._thread_id = 0
496
514
self ._running = 0
@@ -2794,10 +2812,10 @@ cdef class Loop:
2794
2812
cdef:
2795
2813
Handle h
2796
2814
2797
- if not self ._listening_signals :
2798
- self ._setup_signals()
2799
- if not self ._listening_signals:
2800
- raise ValueError ( ' set_wakeup_fd only works in main thread' )
2815
+ if not self ._is_main_thread() :
2816
+ raise ValueError (
2817
+ ' add_signal_handler() can only be called from '
2818
+ ' the main thread' )
2801
2819
2802
2820
if (aio_iscoroutine(callback)
2803
2821
or aio_iscoroutinefunction(callback)):
@@ -2829,14 +2847,6 @@ cdef class Loop:
2829
2847
2830
2848
self ._check_signal(sig)
2831
2849
self ._check_closed()
2832
- try :
2833
- # set_wakeup_fd() raises ValueError if this is not the
2834
- # main thread. By calling it early we ensure that an
2835
- # event loop running in another thread cannot add a signal
2836
- # handler.
2837
- _set_signal_wakeup_fd(self ._csock.fileno())
2838
- except (ValueError , OSError ) as exc:
2839
- raise RuntimeError (str (exc))
2840
2850
2841
2851
h = new_Handle(self , callback, args or None , None )
2842
2852
self ._signal_handlers[sig] = h
@@ -2866,6 +2876,12 @@ cdef class Loop:
2866
2876
2867
2877
Return True if a signal handler was removed, False if not.
2868
2878
"""
2879
+
2880
+ if not self ._is_main_thread():
2881
+ raise ValueError (
2882
+ ' remove_signal_handler() can only be called from '
2883
+ ' the main thread' )
2884
+
2869
2885
self ._check_signal(sig)
2870
2886
2871
2887
if not self ._listening_signals:
@@ -2889,9 +2905,6 @@ cdef class Loop:
2889
2905
else :
2890
2906
raise
2891
2907
2892
- if not self ._signal_handlers:
2893
- self ._shutdown_signals()
2894
-
2895
2908
return True
2896
2909
2897
2910
@cython.iterable_coroutine
0 commit comments