Set `._cancel_msg` to RPC `{cmd: 'self._cancel_task', ..}` msg

Like how we set `Context._cancel_msg` in `._deliver_msg()` (in
which case normally it's an `{'error': ..}` msg), do the same when any
RPC task is remotely cancelled via `Actor._cancel_task` where that task
doesn't yet have a cancel msg set yet.

This makes is much easier to distinguish between ctx cancellations due
to some remote error vs. Explicit remote requests via any of
`Actor.cancel()`, `Portal.cancel_actor()` or `Context.cancel()`.
modden_spawn_from_client_req
Tyler Goodlet 2024-03-07 18:24:00 -05:00
parent 7ae9b5319b
commit 364ea91983
1 changed files with 45 additions and 29 deletions

View File

@ -302,7 +302,7 @@ async def _errors_relayed_via_ipc(
)
)
):
# await pause()
# await _debug.pause()
# XXX QUESTION XXX: is there any case where we'll
# want to debug IPC disconnects as a default?
# => I can't think of a reason that inspecting this
@ -322,6 +322,12 @@ async def _errors_relayed_via_ipc(
cid=ctx.cid,
)
# NOTE: the src actor should always be packed into the
# error.. but how should we verify this?
# assert err_msg['src_actor_uid']
# if not err_msg['error'].get('src_actor_uid'):
# import pdbp; pdbp.set_trace()
if is_rpc:
try:
await chan.send(err_msg)
@ -566,6 +572,7 @@ async def _invoke(
# inside ._context._drain_to_final_msg()`..
# # TODO: remove this ^ right?
if ctx._scope.cancelled_caught:
our_uid: tuple = actor.uid
# first check for and raise any remote error
# before raising any context cancelled case
@ -575,8 +582,9 @@ async def _invoke(
ctx._maybe_raise_remote_err(re)
cs: CancelScope = ctx._scope
if cs.cancel_called:
our_uid: tuple = actor.uid
canceller: tuple = ctx.canceller
msg: str = (
'actor was cancelled by '
@ -632,15 +640,6 @@ async def _invoke(
# f' |_{ctx}'
)
# TODO: does this ever get set any more or can
# we remove it?
if ctx._cancel_msg:
msg += (
# '------ - ------\n'
# 'IPC msg:\n'
f'\n\n{ctx._cancel_msg}'
)
# task-contex was either cancelled by request using
# ``Portal.cancel_actor()`` or ``Context.cancel()``
# on the far end, or it was cancelled by the local
@ -1753,7 +1752,9 @@ class Actor:
self,
cid: str,
parent_chan: Channel,
requesting_uid: tuple[str, str] | None = None,
requesting_uid: tuple[str, str]|None = None,
ipc_msg: dict|None|bool = False,
) -> bool:
'''
@ -1764,16 +1765,13 @@ class Actor:
in the signature (for now).
'''
# this ctx based lookup ensures the requested task to
# be cancelled was indeed spawned by a request from
# this channel
# this ctx based lookup ensures the requested task to be
# cancelled was indeed spawned by a request from its
# parent (or some grandparent's) channel
ctx: Context
func: Callable
is_complete: trio.Event
# NOTE: right now this is only implicitly called by
# streaming IPC but it should be called
# to cancel any remotely spawned task
try:
(
ctx,
@ -1801,20 +1799,23 @@ class Actor:
log.cancel(
'Cancel request for RPC task\n\n'
f'<= ._cancel_task(): {requesting_uid}\n'
f' |_ @{ctx.dmaddr}\n\n'
f'<= Actor.cancel_task(): {requesting_uid}\n\n'
f'=> {ctx._task}\n'
f' |_ >> {ctx.repr_rpc}\n'
# f' >> Actor._cancel_task() => {ctx._task}\n'
# f' |_ {ctx._task}\n\n'
# TODO: better ascii repr for "supervisor" like
# a nursery or context scope?
# f'=> {parent_chan}\n'
f'=> {ctx._task}\n'
# f' |_{ctx._task}\n'
# TODO: simplified `Context.__repr__()` fields output
# shows only application state-related stuff like,
# - ._stream
# - .closed
# - .started_called
# - .. etc.
f' >> {ctx.repr_rpc}\n'
# f' >> {ctx.repr_rpc}\n'
# f' |_ctx: {cid}\n'
# f' >> {ctx._nsf}()\n'
)
@ -1824,6 +1825,16 @@ class Actor:
):
ctx._canceller: tuple = requesting_uid
# TODO: pack the RPC `{'cmd': <blah>}` msg into a ctxc and
# then raise and pack it here?
if (
ipc_msg
and ctx._cancel_msg is None
):
# assign RPC msg directly from the loop which usually
# the case with `ctx.cancel()` on the other side.
ctx._cancel_msg = ipc_msg
# don't allow cancelling this function mid-execution
# (is this necessary?)
if func is self._cancel_task:
@ -1904,10 +1915,15 @@ class Actor:
else
"IPC channel's "
)
rent_chan_repr: str = (
f'|_{parent_chan}'
if parent_chan
else ''
)
log.cancel(
f'Cancelling {descr} {len(tasks)} rpc tasks\n\n'
f'<= .cancel_rpc_tasks(): {req_uid}\n'
f'<= `Actor.cancel_rpc_tasks()`: {req_uid}\n'
f' {rent_chan_repr}\n'
# f'{self}\n'
# f'{tasks_str}'
)
@ -1927,9 +1943,6 @@ class Actor:
):
continue
# if func == self._cancel_task:
# continue
# TODO: this maybe block on the task cancellation
# and so should really done in a nursery batch?
await self._cancel_task(
@ -2339,6 +2352,8 @@ async def process_messages(
await actor._cancel_task(
cid,
channel,
ipc_msg=msg,
)
break
@ -2449,6 +2464,7 @@ async def process_messages(
# cancel it!
'parent_chan': chan,
'requesting_uid': chan.uid,
'ipc_msg': msg,
}
# TODO: remove? already have emit in meth.
# log.runtime(
@ -2737,7 +2753,7 @@ class Arbiter(Actor):
sockaddr: tuple[str, int]
for (aname, _), sockaddr in self._registry.items():
log.info(
log.runtime(
f'Actor mailbox info:\n'
f'aname: {aname}\n'
f'sockaddr: {sockaddr}\n'