Skip to content

Cancelling asyncio native sendfile during setup leaves the transport paused #157025

Description

@lpyu001

Bug report

Bug description:

Summary

Cancelling loop.sendfile() while its native implementation is waiting for the transport's write buffer to become empty leaves the transport in a partially modified state. The transport remains paused and retains its internal empty-buffer waiter. The selector implementation additionally leaves the transport absent from the event loop's transport registry.

The cancellation window exists in both BaseSelectorEventLoop._sendfile_native() and BaseProactorEventLoop._sendfile_native().

Reproduction Code

import asyncio


loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)


class Transport:
    _sendfile_compatible = asyncio.constants._SendfileMode.TRY_NATIVE
    _sock = None

    def __init__(self):
        self.paused = False
        self.resumed = False
        self.empty_waiter = None

    def is_closing(self):
        return False

    def is_reading(self):
        return True

    def pause_reading(self):
        self.paused = True

    def resume_reading(self):
        self.resumed = True

    def _make_empty_waiter(self):
        self.empty_waiter = loop.create_future()
        return self.empty_waiter

    def _reset_empty_waiter(self):
        self.empty_waiter = None


async def main():
    transport = Transport()
    task = asyncio.create_task(loop.sendfile(transport, None))
    await asyncio.sleep(0)
    task.cancel()
    await asyncio.gather(task, return_exceptions=True)
    print(
        transport.paused,
        transport.resumed,
        transport.empty_waiter is not None,
    )


loop.run_until_complete(main())
loop.close()

Actual Behavior

The controlled transport is paused but never resumed, and its waiter is not reset:

True False True

On the selector implementation, the same cancellation point also leaves transport._sock_fd absent from loop._transports.

Expected Behavior

Cancellation should propagate without leaving the transport in a partially modified state. Reading should be restored when it was active before sendfile(), the empty-buffer waiter should be reset, and the selector implementation should restore the transport registry entry.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Windows

Linked PRs

Activity

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

    stdlibStandard Library Python modules in the Lib/ directorytopic-asynciotype-bugAn unexpected behavior, bug, or error

    Projects

    • Status
      Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions