Only `ctx.canccel()` when non-callee ctxc received

Since presumably if the ctxc *is* received from the callee side, there's
no point in also sending it a cancel request ;)

Also, try out shielding the `ctx.cancel()` call inside
`Portal.open_context()` since that seems to resolve testing assertions
where you'd expect `.cancel_called` to have been set in the case where
an inter-peer cancellation is raised by some local ctx-handle and that
local ctxc causes other surrounding `.open_context()` blocks to raise
and then implicitly call `ctx.cancel()`. However, there's a possible
race where the callee side might deliver a ctxc while all that is
happening, `._scope.cancel()` gets called and then by the time
`ctx.cancel()` is called it raises `trio.Cancelled` instead continuing
to set `._cancel_called`? This needs more testing and docs to get the
minute details correct .. yet again.
shielded_ctx_cancel
Tyler Goodlet 2024-03-06 13:32:04 -05:00
parent 9e3f41a5b1
commit 0e310971a3
1 changed files with 13 additions and 7 deletions

View File

@ -42,7 +42,7 @@ from async_generator import asynccontextmanager
from .trionics import maybe_open_nursery
from .devx import (
# acquire_debug_lock,
# pause,
pause,
maybe_wait_for_debugger,
)
from ._state import (
@ -556,7 +556,7 @@ class Portal:
# placeholder for any exception raised in the runtime
# or by user tasks which cause this context's closure.
scope_err: BaseException|None = None
ctxc_from_callee: ContextCancelled|None = None
ctxc_from_callee: ContextCancelled|bool = False
try:
async with trio.open_nursery() as nurse:
@ -673,8 +673,6 @@ class Portal:
# `Nursery.cancel_scope.cancel()`)
except ContextCancelled as ctxc:
scope_err = ctxc
ctxc_from_callee = ctxc
# XXX TODO XXX: FIX THIS debug_mode BUGGGG!!!
# using this code and then resuming the REPL will
# cause a SIGINT-ignoring HANG!
@ -686,6 +684,10 @@ class Portal:
#
# await pause()
# ctxc_from_callee: bool = (
# ctxc.src_actor_uid == self.chan.uid
# )
# CASE 2: context was cancelled by local task calling
# `.cancel()`, we don't raise and the exit block should
# exit silently.
@ -696,6 +698,7 @@ class Portal:
and
ctxc.canceller == self.actor.uid
):
ctxc_from_callee = True
log.cancel(
f'Context (cid=[{ctx.cid[-6:]}..] cancelled gracefully with:\n'
f'{ctxc}'
@ -755,6 +758,7 @@ class Portal:
f'{caller_err}\n'
)
# TODO: does this need to be shielded?
if debug_mode():
# async with acquire_debug_lock(self.actor.uid):
# pass
@ -771,12 +775,14 @@ class Portal:
'Calling `ctx.cancel()`!\n'
)
# we don't need to cancel the callee if it already
# told us it's cancelled ;p
if ctxc_from_callee is None:
if not ctxc_from_callee:
log.critical('SENDING CANCEL')
try:
await ctx.cancel()
# await pause(shield=True)
with trio.CancelScope(shield=True):
await ctx.cancel()
except (
trio.BrokenResourceError,
trio.ClosedResourceError,