| 248 | removed = [False] |
| 249 | |
| 250 | def accept_handler(fd: socket.socket, events: int) -> None: |
| 251 | # More connections may come in while we're handling callbacks; |
| 252 | # to prevent starvation of other tasks we must limit the number |
| 253 | # of connections we accept at a time. Ideally we would accept |
| 254 | # up to the number of connections that were waiting when we |
| 255 | # entered this method, but this information is not available |
| 256 | # (and rearranging this method to call accept() as many times |
| 257 | # as possible before running any callbacks would have adverse |
| 258 | # effects on load balancing in multiprocess configurations). |
| 259 | # Instead, we use the (default) listen backlog as a rough |
| 260 | # heuristic for the number of connections we can reasonably |
| 261 | # accept at once. |
| 262 | for i in range(_DEFAULT_BACKLOG): |
| 263 | if removed[0]: |
| 264 | # The socket was probably closed |
| 265 | return |
| 266 | try: |
| 267 | connection, address = sock.accept() |
| 268 | except BlockingIOError: |
| 269 | # EWOULDBLOCK indicates we have accepted every |
| 270 | # connection that is available. |
| 271 | return |
| 272 | except ConnectionAbortedError: |
| 273 | # ECONNABORTED indicates that there was a connection |
| 274 | # but it was closed while still in the accept queue. |
| 275 | # (observed on FreeBSD). |
| 276 | continue |
| 277 | callback(connection, address) |
| 278 | |
| 279 | def remove_handler() -> None: |
| 280 | io_loop.remove_handler(sock) |