Skip to content

Connecting a socket in timeout mode to a server with full listen backlog raises BlockingIOError #117208

@wferi

Description

@wferi

Bug report

Bug description:

I'm working on a Debian bookworm system. Please consider this executable script ms:

#!/usr/bin/python3 -i

from pathlib import Path
import socket

SOCK = 'stream.sock'
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Path(SOCK).unlink(missing_ok=True)
s.bind(SOCK)
s.listen(0)

and this one mc:

#!/usr/bin/python3

import socket

SOCK = 'stream.sock'
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.settimeout(3)
s.connect(SOCK)

Let's start ./ms, and once it reaches the Python prompt, run ./mc twice in another terminal:

$ ./mc
$ ./mc
Traceback (most recent call last):
  File "/home/wferi/block/./mc", line 8, in <module>
    s.connect(SOCK)
BlockingIOError: [Errno 11] Resource temporarily unavailable

The first1 invocation runs to completion all right, but all succeeding ones immediately fail with BlockingIOError, as if the connecting socket was in non-blocking mode, not in timeout mode. I understand that the documentation2 notes

at the operating system level, sockets in timeout mode are internally set in non-blocking mode

so that must be where this comes from, but such an implementation detail shouldn't leak out of the abstraction, as the documentation also states that

A socket object can be in one of three modes: blocking, non-blocking, or timeout.

and

exception BlockingIOError: Raised when an operation would block on an object (e.g. socket) set for non-blocking operation. 3

CPython versions tested on:

3.11

Operating systems tested on:

Linux

Linked PRs

Footnotes

  1. In general the first backlog+1, but I set the listen backlog to 0 in the code of ms above.

  2. https://docs.python.org/3/library/socket.html#socket-timeouts

  3. https://docs.python.org/3/library/exceptions.html

Activity

added
type-bugAn unexpected behavior, bug, or error
on Mar 25, 2024
duaneg

duaneg commented on May 26, 2025

@duaneg
Contributor

I found this unreliable to reproduce initially, but after some tweaking managed to come up with a self-contained script that works on my system:

repro
from pathlib import Path
import socket
import sys
import threading

SOCK = 'stream.sock'

def open_socket():
    return socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

def server(listening):
    s = open_socket()
    Path(SOCK).unlink(missing_ok=True)
    s.bind(SOCK)
    s.listen(0)
    listening.set()

def client(listening):
    s = open_socket()
    s.settimeout(3)
    listening.wait()
    s.connect(SOCK)

def run():
    listening = threading.Event()

    threads = (threading.Thread(target=server, args=(listening, )),
               threading.Thread(target=client, args=(listening, )),
               threading.Thread(target=client, args=(listening, )))

    for t in threads:
        t.start()

    for t in threads:
        t.join()

stopping = threading.Event()

def check_exception(args):
    if args.exc_type != ConnectionRefusedError:
        stopping.set()
        sys.excepthook(args.exc_type, args.exc_value, args.exc_traceback)

threading.excepthook = check_exception
while not stopping.is_set():
    run()

The exception is coming from internal_connect, where it calls connect. If there is a timeout and connect fails with EINPROGRESS, we wait. However, man 2 connect on Linux tells us:

EINPROGRESS: The socket is nonblocking and the connection cannot be completed immediately. (UNIX domain sockets failed with EAGAIN instead.)

That is what is happening in this case. Since it is a UNIX domain socket errno is set to EAGAIN not EINPROGRESS, so it doesn't try to wait, and immediately raises the error.

added a commit that references this issue on May 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Connecting a socket in timeout mode to a server with full listen backlog raises BlockingIOError · Issue #117208 · python/cpython

      Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

      Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant