Skip to content

Fix SocketStream.aclose() abort() race on the asyncio backend - #1300

Closed
afonsojanu wants to merge 1 commit into
agronholm:masterfrom
afonsojanu:fix/socket-stream-aclose-connection-lost-race
Closed

Fix SocketStream.aclose() abort() race on the asyncio backend#1300
afonsojanu wants to merge 1 commit into
agronholm:masterfrom
afonsojanu:fix/socket-stream-aclose-connection-lost-race

Conversation

@afonsojanu

Copy link
Copy Markdown

NOTE Erasing or replacing the contents of this template will result in your pull
request being summarily closed without consideration!

Changes

Fixes #1250.

On the asyncio backend, SocketStream.aclose() closes the transport and then
checkpoints with await sleep(0) before calling transport.abort(). If the
transport's write buffer drains and connection_lost() fires during that
checkpoint, the transport has already detached itself from the event loop by
the time aclose() resumes, so the subsequent abort() call raises
AttributeError: 'NoneType' object has no attribute 'call_soon' instead of
being the harmless no-op it's meant to be in that interleaving.

StreamProtocol now records whether connection_lost() has already run, and
aclose() skips the abort() call when it has, since the connection is
already gone at that point.

Added a deterministic regression test that schedules connection_lost() to
fire during the checkpoint (via loop.call_soon) rather than relying on the
timing-dependent socket-buffer repro from the issue.

Checklist

  • You've added tests (in tests/) which would fail without your patch
  • You've updated the documentation (in docs/), in case of behavior changes or new
    features
  • You've added a new changelog entry (in docs/versionhistory.rst).

If connection_lost() fires while aclose() is suspended at the
checkpoint between transport.close() and transport.abort(), the
transport has already detached from the event loop by the time
control returns. Calling abort() on it then raises AttributeError
instead of being the no-op it's meant to be in that interleaving.

StreamProtocol now tracks whether connection_lost() has already run,
and aclose() skips the abort() call when it has.

Fixes agronholm#1250
@agronholm

Copy link
Copy Markdown
Owner

There are now tons of PRs on this same subject, and I'm drowning in them. Justify why you had to send yet another one.

@afonsojanu afonsojanu closed this Aug 30, 2026
@afonsojanu

Copy link
Copy Markdown
Author

Fair question. I went back and checked, and #1255 already covers this exact bug (same issue #1250, same fix approach: track connection-lost state on the protocol and skip abort() when it's already true). It's been open since July 30th, so it predates mine by a month.

I should have searched open PRs against issue #1250 before starting instead of just checking for an existing fix on master. Closing this one, sorry for adding to the pile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

asyncio backend: race in SocketStream.aclose() — transport.abort() raises AttributeError: 'NoneType' object has no attribute 'call_soon'

2 participants