Skip to content

Fix LibevConnection close() race causing EBADF errors#700

Draft
dkropachev wants to merge 1 commit intomasterfrom
fix/libev-close-race-614
Draft

Fix LibevConnection close() race causing EBADF errors#700
dkropachev wants to merge 1 commit intomasterfrom
fix/libev-close-race-614

Conversation

@dkropachev
Copy link
Collaborator

Summary

Fixes the close() race condition in LibevConnection that causes [Errno 9] Bad file descriptor errors during node restarts.

  • close() closes the socket immediately but watchers are stopped asynchronously in _loop_will_run() on the next event loop iteration
  • If handle_read()/handle_write() is already running when the socket closes, they get EBADF and call defunct() unnecessarily
  • factory() can return a dead connection when close() is called during handshake without setting last_error

Changes

  • Add is_closed/is_defunct guards in handle_read() and handle_write() error paths
  • Set last_error in close() when connected_event is not yet set
  • Set last_error on server-initiated close (EOF) in handle_read()

Test plan

  • tests/unit/io/test_libevreactor.py passes

Refs #614

LibevConnection.close() closes the socket immediately while watchers
are stopped asynchronously in the next event loop iteration via
_loop_will_run(). This creates a race window where handle_read() or
handle_write() can operate on a closed socket fd, producing EBADF
errors that surface as ConnectionShutdown.

- Add is_closed/is_defunct guards in handle_read() and handle_write()
  error paths to silently exit during shutdown instead of calling
  defunct() with EBADF
- Set last_error in close() when connected_event is not yet set to
  prevent factory() from returning a dead connection
- Set last_error on server-initiated close (EOF) in handle_read()
  before calling close()
@dkropachev dkropachev marked this pull request as draft February 14, 2026 16:40
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.

1 participant