python-trio / python-trio/trio

Should send_all automatically do wait_send_all_might_not_block after sending the data?

Open
#371 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

design discussion potential API breaker
Dominant language
Python
Stars
7.3k
Forks
431
Avg merge
2d 17h
Merged PRs (30d)
6

Description

Currently send_all blocks until the kernel accepts responsibility for the data, and then immediately returns. I'm wondering if it should instead block until the kernel has not only accepted responsibility, but also until the the kernel send buffer has some space in it (i.e., the socket becomes writable again).

Pros:

  • It would make TCP_NOTSENT_LOWAT automatically work on MacOS. Currently it only works if you explicitly call wait_send_all_might_not_block, because the TCP_NOTSENT_LOWAT is only applied to select/kqueue type writability checks, not to send writability (discussion here: #76). This would also let us normalize TCP_NOTSENT_LOWAT semantics across MacOS and Linux (which does apply TCP_NOTSENT_LOWAT limits to send) -- in both cases send_all would wait to return until after the low water mark was reached. It would also open the door to a potential optimization on Linux: when a large buffer is passed to send_all, we could temporarily disable TCP_NOTSENT_LOWAT (so that the kernel accepts it in one big chunk instead of making us dribble it into the send buffer over multiple calls), and then re-enable it, and then wait for writability.

  • It would allow us to remove wait_send_all_might_not_block, which adds significant surface area to the Stream API (it would go from 4 methods → 3 methods, 25% smaller, and testing this method in particular adds significant complications).

  • It's generally consistent with the idea that send_all blocks until the data is sent -- not only do we hand it off to the kernel, we wait for the kernel to make some progress. (I guess this isn't so much a pro, as a counter to the possible "con" that send_all waiting is surprising.)

  • It automatically gives better results for latency-sensitive applications, in particular those where you get better results when waiting as late as possible before committing to what you want to send (e.g. screen-sharing). These apps are why wait_send_all_might_not_block exists, but currently it's not clear they'll actually benefit because this is a bit obscure and you have to explicitly set up your code to use it.

Cons:

  • Maybe it will kill throughput on super high bandwidth applications, or something? (Stuff like an echo server benchmark, but possibly also stuff like Antoine's worrying about here. Would want to check this. OTOH maybe it's fine because we'd end up replacing a loop like:

    while True:
        await wait_writable(socket)
        socket.send(...)
    

    with a loop like:

    while True:
        socket.send(...)
        await wait_writable(socket)
    

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the Stream API's send_all and wait_send_all_might_not_block behavior, along with the TCP_NOTSENT_LOWAT discussion in issue #76. Compare the proposed blocking semantics and throughput trade-offs on macOS and Linux; done means the API behavior is decided and validated against the stated use cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.