Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions docs/source/reference-lowlevel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,18 +231,19 @@ All environments provide the following functions:
yourself afterwards. Also, you want to be careful to make sure no
new tasks start waiting on the object in between when you call this
and when it's actually closed. So to close something properly, you
usually want to do these steps in order:
should do these steps in order:

1. Explicitly mark the object as closed, so that any new attempts
to use it will abort before they start.
2. Call `notify_closing` to wake up any already-existing users.
3. Actually close the object.

It's also possible to do them in a different order if that's more
convenient, *but only if* you make sure not to have any checkpoints in
between the steps. This way they all happen in a single atomic
step, so other tasks won't be able to tell what order they happened
in anyway.
Do not have any checkpoints between these steps, so that other tasks
cannot start waiting on the object before it is closed. You may change
the order of the first two steps if needed, but `notify_closing` must
always be called before actually closing the object. Calling it after
closing is not guaranteed to work, even without intervening checkpoints:
it may raise an error or leave stale I/O registrations behind.


Unix-specific API
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3520.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Clarify that `trio.lowlevel.notify_closing` must be called before actually closing a file descriptor or socket, even when there are no checkpoints between the steps.
13 changes: 7 additions & 6 deletions src/trio/_core/_generated_io_epoll.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,19 @@ def notify_closing(fd: int | _HasFileNo) -> None:
yourself afterwards. Also, you want to be careful to make sure no
new tasks start waiting on the object in between when you call this
and when it's actually closed. So to close something properly, you
usually want to do these steps in order:
should do these steps in order:

1. Explicitly mark the object as closed, so that any new attempts
to use it will abort before they start.
2. Call `notify_closing` to wake up any already-existing users.
3. Actually close the object.

It's also possible to do them in a different order if that's more
convenient, *but only if* you make sure not to have any checkpoints in
between the steps. This way they all happen in a single atomic
step, so other tasks won't be able to tell what order they happened
in anyway.
Do not have any checkpoints between these steps, so that other tasks
cannot start waiting on the object before it is closed. You may change
the order of the first two steps if needed, but `notify_closing` must
always be called before actually closing the object. Calling it after
closing is not guaranteed to work, even without intervening checkpoints:
it may raise an error or leave stale I/O registrations behind.
"""
try:
return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd)
Expand Down
13 changes: 7 additions & 6 deletions src/trio/_core/_generated_io_kqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,18 +134,19 @@ def notify_closing(fd: int | _HasFileNo) -> None:
yourself afterwards. Also, you want to be careful to make sure no
new tasks start waiting on the object in between when you call this
and when it's actually closed. So to close something properly, you
usually want to do these steps in order:
should do these steps in order:

1. Explicitly mark the object as closed, so that any new attempts
to use it will abort before they start.
2. Call `notify_closing` to wake up any already-existing users.
3. Actually close the object.

It's also possible to do them in a different order if that's more
convenient, *but only if* you make sure not to have any checkpoints in
between the steps. This way they all happen in a single atomic
step, so other tasks won't be able to tell what order they happened
in anyway.
Do not have any checkpoints between these steps, so that other tasks
cannot start waiting on the object before it is closed. You may change
the order of the first two steps if needed, but `notify_closing` must
always be called before actually closing the object. Calling it after
closing is not guaranteed to work, even without intervening checkpoints:
it may raise an error or leave stale I/O registrations behind.
"""
try:
return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd)
Expand Down
13 changes: 7 additions & 6 deletions src/trio/_core/_generated_io_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,19 @@ def notify_closing(handle: Handle | int | _HasFileNo) -> None:
yourself afterwards. Also, you want to be careful to make sure no
new tasks start waiting on the object in between when you call this
and when it's actually closed. So to close something properly, you
usually want to do these steps in order:
should do these steps in order:

1. Explicitly mark the object as closed, so that any new attempts
to use it will abort before they start.
2. Call `notify_closing` to wake up any already-existing users.
3. Actually close the object.

It's also possible to do them in a different order if that's more
convenient, *but only if* you make sure not to have any checkpoints in
between the steps. This way they all happen in a single atomic
step, so other tasks won't be able to tell what order they happened
in anyway.
Do not have any checkpoints between these steps, so that other tasks
cannot start waiting on the object before it is closed. You may change
the order of the first two steps if needed, but `notify_closing` must
always be called before actually closing the object. Calling it after
closing is not guaranteed to work, even without intervening checkpoints:
it may raise an error or leave stale I/O registrations behind.
"""
try:
return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(handle)
Expand Down
13 changes: 7 additions & 6 deletions src/trio/_core/_io_epoll.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,18 +361,19 @@ def notify_closing(self, fd: int | _HasFileNo) -> None:
yourself afterwards. Also, you want to be careful to make sure no
new tasks start waiting on the object in between when you call this
and when it's actually closed. So to close something properly, you
usually want to do these steps in order:
should do these steps in order:

1. Explicitly mark the object as closed, so that any new attempts
to use it will abort before they start.
2. Call `notify_closing` to wake up any already-existing users.
3. Actually close the object.

It's also possible to do them in a different order if that's more
convenient, *but only if* you make sure not to have any checkpoints in
between the steps. This way they all happen in a single atomic
step, so other tasks won't be able to tell what order they happened
in anyway.
Do not have any checkpoints between these steps, so that other tasks
cannot start waiting on the object before it is closed. You may change
the order of the first two steps if needed, but `notify_closing` must
always be called before actually closing the object. Calling it after
closing is not guaranteed to work, even without intervening checkpoints:
it may raise an error or leave stale I/O registrations behind.
"""
if not isinstance(fd, int):
fd = fd.fileno()
Expand Down
13 changes: 7 additions & 6 deletions src/trio/_core/_io_kqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,18 +255,19 @@ def notify_closing(self, fd: int | _HasFileNo) -> None:
yourself afterwards. Also, you want to be careful to make sure no
new tasks start waiting on the object in between when you call this
and when it's actually closed. So to close something properly, you
usually want to do these steps in order:
should do these steps in order:

1. Explicitly mark the object as closed, so that any new attempts
to use it will abort before they start.
2. Call `notify_closing` to wake up any already-existing users.
3. Actually close the object.

It's also possible to do them in a different order if that's more
convenient, *but only if* you make sure not to have any checkpoints in
between the steps. This way they all happen in a single atomic
step, so other tasks won't be able to tell what order they happened
in anyway.
Do not have any checkpoints between these steps, so that other tasks
cannot start waiting on the object before it is closed. You may change
the order of the first two steps if needed, but `notify_closing` must
always be called before actually closing the object. Calling it after
closing is not guaranteed to work, even without intervening checkpoints:
it may raise an error or leave stale I/O registrations behind.
"""
if not isinstance(fd, int):
fd = fd.fileno()
Expand Down
13 changes: 7 additions & 6 deletions src/trio/_core/_io_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,18 +806,19 @@ def notify_closing(self, handle: Handle | int | _HasFileNo) -> None:
yourself afterwards. Also, you want to be careful to make sure no
new tasks start waiting on the object in between when you call this
and when it's actually closed. So to close something properly, you
usually want to do these steps in order:
should do these steps in order:

1. Explicitly mark the object as closed, so that any new attempts
to use it will abort before they start.
2. Call `notify_closing` to wake up any already-existing users.
3. Actually close the object.

It's also possible to do them in a different order if that's more
convenient, *but only if* you make sure not to have any checkpoints in
between the steps. This way they all happen in a single atomic
step, so other tasks won't be able to tell what order they happened
in anyway.
Do not have any checkpoints between these steps, so that other tasks
cannot start waiting on the object before it is closed. You may change
the order of the first two steps if needed, but `notify_closing` must
always be called before actually closing the object. Calling it after
closing is not guaranteed to work, even without intervening checkpoints:
it may raise an error or leave stale I/O registrations behind.
"""
handle = _get_base_socket(handle)
waiters = self._afd_waiters.get(handle)
Expand Down
Loading