@@ -162,43 +162,50 @@ def _write_to_self(self):
162162 exc_info = True )
163163
164164 def _start_serving (self , protocol_factory , sock ,
165- sslcontext = None , server = None ):
165+ sslcontext = None , server = None , backlog = 100 ):
166166 self .add_reader (sock .fileno (), self ._accept_connection ,
167- protocol_factory , sock , sslcontext , server )
167+ protocol_factory , sock , sslcontext , server , backlog )
168168
169169 def _accept_connection (self , protocol_factory , sock ,
170- sslcontext = None , server = None ):
171- try :
172- conn , addr = sock .accept ()
173- if self ._debug :
174- logger .debug ("%r got a new connection from %r: %r" ,
175- server , addr , conn )
176- conn .setblocking (False )
177- except (BlockingIOError , InterruptedError , ConnectionAbortedError ):
178- pass # False alarm.
179- except OSError as exc :
180- # There's nowhere to send the error, so just log it.
181- if exc .errno in (errno .EMFILE , errno .ENFILE ,
182- errno .ENOBUFS , errno .ENOMEM ):
183- # Some platforms (e.g. Linux keep reporting the FD as
184- # ready, so we remove the read handler temporarily.
185- # We'll try again in a while.
186- self .call_exception_handler ({
187- 'message' : 'socket.accept() out of system resource' ,
188- 'exception' : exc ,
189- 'socket' : sock ,
190- })
191- self .remove_reader (sock .fileno ())
192- self .call_later (constants .ACCEPT_RETRY_DELAY ,
193- self ._start_serving ,
194- protocol_factory , sock , sslcontext , server )
170+ sslcontext = None , server = None , backlog = 100 ):
171+ # This method is only called once for each event loop tick where the
172+ # listening socket has triggered an EVENT_READ. There may be multiple
173+ # connections waiting for an .accept() so it is called in a loop.
174+ # See https://bugs.python.org/issue27906 for more details.
175+ for _ in range (backlog ):
176+ try :
177+ conn , addr = sock .accept ()
178+ if self ._debug :
179+ logger .debug ("%r got a new connection from %r: %r" ,
180+ server , addr , conn )
181+ conn .setblocking (False )
182+ except (BlockingIOError , InterruptedError , ConnectionAbortedError ):
183+ # Early exit because the socket accept buffer is empty.
184+ return None
185+ except OSError as exc :
186+ # There's nowhere to send the error, so just log it.
187+ if exc .errno in (errno .EMFILE , errno .ENFILE ,
188+ errno .ENOBUFS , errno .ENOMEM ):
189+ # Some platforms (e.g. Linux keep reporting the FD as
190+ # ready, so we remove the read handler temporarily.
191+ # We'll try again in a while.
192+ self .call_exception_handler ({
193+ 'message' : 'socket.accept() out of system resource' ,
194+ 'exception' : exc ,
195+ 'socket' : sock ,
196+ })
197+ self .remove_reader (sock .fileno ())
198+ self .call_later (constants .ACCEPT_RETRY_DELAY ,
199+ self ._start_serving ,
200+ protocol_factory , sock , sslcontext , server ,
201+ backlog )
202+ else :
203+ raise # The event loop will catch, log and ignore it.
195204 else :
196- raise # The event loop will catch, log and ignore it.
197- else :
198- extra = {'peername' : addr }
199- accept = self ._accept_connection2 (protocol_factory , conn , extra ,
200- sslcontext , server )
201- self .create_task (accept )
205+ extra = {'peername' : addr }
206+ accept = self ._accept_connection2 (protocol_factory , conn , extra ,
207+ sslcontext , server )
208+ self .create_task (accept )
202209
203210 @coroutine
204211 def _accept_connection2 (self , protocol_factory , conn , extra ,
0 commit comments