@@ -36,6 +36,7 @@ def __init__(self, *args, **kwargs):
3636 self .nr_conns = 0
3737 self .lifespan = None
3838 self .state = {} # Shared state for lifespan
39+ self ._quick_shutdown = False # True for SIGINT/SIGQUIT (immediate), False for SIGTERM (graceful)
3940
4041 @classmethod
4142 def check_config (cls , cfg , log ):
@@ -122,7 +123,11 @@ def init_signals(self):
122123 self .loop .add_signal_handler (signal .SIGABRT , self .handle_abort_signal )
123124
124125 def handle_quit_signal (self ):
125- """Handle SIGQUIT - immediate shutdown."""
126+ """Handle SIGQUIT/SIGINT - immediate shutdown."""
127+ self ._quick_shutdown = True
128+ if not self .alive :
129+ # Already shutting down (SIGTERM was sent) - wake up the loop
130+ return
126131 self .alive = False
127132 self .cfg .worker_int (self )
128133
@@ -221,23 +226,32 @@ async def _shutdown(self):
221226 for server in self .servers :
222227 server .close ()
223228
224- # Wait for servers to close
225- for server in self .servers :
226- await server .wait_closed ()
227-
228- # Wait for in-flight connections (with timeout)
229- graceful_timeout = self .cfg .graceful_timeout
230- if self .nr_conns > 0 :
229+ # Wait for servers to close (skip on quick shutdown)
230+ if not self ._quick_shutdown :
231+ for server in self .servers :
232+ if self ._quick_shutdown :
233+ break
234+ try :
235+ await asyncio .wait_for (server .wait_closed (), timeout = 0.5 )
236+ except asyncio .TimeoutError :
237+ pass # Check _quick_shutdown on next iteration
238+
239+ # Wait for in-flight connections (skip on quick shutdown)
240+ if self .nr_conns > 0 and not self ._quick_shutdown :
241+ graceful_timeout = self .cfg .graceful_timeout
231242 self .log .info ("Waiting for %d connections to finish..." , self .nr_conns )
232243 deadline = self .loop .time () + graceful_timeout
233244 while self .nr_conns > 0 and self .loop .time () < deadline :
245+ if self ._quick_shutdown :
246+ self .log .info ("Quick shutdown requested" )
247+ break
234248 await asyncio .sleep (0.1 )
235249
236250 if self .nr_conns > 0 :
237- self .log .warning ("Closing %d connections after timeout " , self .nr_conns )
251+ self .log .warning ("Forcing close of %d connections" , self .nr_conns )
238252
239- # Run lifespan shutdown
240- if self .lifespan :
253+ # Run lifespan shutdown (skip on quick shutdown)
254+ if self .lifespan and not self . _quick_shutdown :
241255 try :
242256 await self .lifespan .shutdown ()
243257 except Exception as e :
@@ -263,11 +277,19 @@ def _cleanup(self):
263277 for task in pending :
264278 task .cancel ()
265279
266- # Run loop until all tasks are cancelled
280+ # Run loop until all tasks are cancelled (with timeout on quick exit)
267281 if pending :
268- self .loop .run_until_complete (
269- asyncio .gather (* pending , return_exceptions = True )
270- )
282+ gather = asyncio .gather (* pending , return_exceptions = True )
283+ if self ._quick_shutdown :
284+ # Quick exit - don't wait long for tasks to cancel
285+ try :
286+ self .loop .run_until_complete (
287+ asyncio .wait_for (gather , timeout = 1.0 )
288+ )
289+ except asyncio .TimeoutError :
290+ self .log .debug ("Timeout waiting for tasks to cancel" )
291+ else :
292+ self .loop .run_until_complete (gather )
271293
272294 self .loop .close ()
273295 except Exception as e :
0 commit comments