Hello, I tracked the issue: in http1connection.py, the write method does:
self._pending_write = self.stream.write(fchunk) self._pending_write.add_done_callback(self._on_write_complete) The Future[1] documentation says: Callbacks registered with add_done_callback() are always called via the event loop’s call_soon_threadsafe(). So although self._pending_write in that case is already in a 'done' state, self._on_write_complete is only called the next time we'll reenter the event loop. Meanwhile, before returning to the event loop, the flow of the code continues and RequestHandler.finish calls: self.request.connection.finish() It detects correctly that there is still a pending write, and calls: future_add_done_callback(self._pending_write, self._finish_request) However, future_add_done_callback does not schedule self._finish_request for the next ioloop iteration like Future.add_done_callback does: # concurrent.py: def future_add_done_callback(future, callback): # … if future.done(): callback(future) else: future.add_done_callback(callback) Compare with: # In concurrent.py, class Future: def add_done_callback(self, fn): # … if self._done: from tornado.ioloop import IOLoop IOLoop.current().add_callback(fn, self) else: self._callbacks.append(fn) So self._finish_request is called before self._on_write_complete, _clear_callbacks() is called, which resets self._write_callbacks. When control returns to the event loop, _on_write_complete is finally, called, which finds self._write_future set to None, and won't trigger the future that will permit RequestHandler.flush() to continue. If there is still data to write, self._pending_write is not done when future_add_done_callback is called, and RequestHandler.flush() won't get stuck. Enrico [1] https://www.tornadoweb.org/en/stable/concurrent.html#tornado.concurrent.Future.add_done_callback -- GPG key: 4096R/634F4BD1E7AD5568 2009-05-08 Enrico Zini <enr...@enricozini.org>
signature.asc
Description: PGP signature