Skip to content

Commit 0e18c93

Browse files
implement cancellation semantics suggestions from code review
1 parent 9f4e79e commit 0e18c93

File tree

2 files changed

+15
-8
lines changed

2 files changed

+15
-8
lines changed

trio/_tests/test_threads.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,7 @@ def sync_check():
879879
try:
880880
from_thread_run_sync(bool)
881881
except _core.Cancelled:
882+
# pragma: no cover, sync functions don't raise Cancelled
882883
queue.put(True)
883884
else:
884885
queue.put(False)
@@ -893,7 +894,7 @@ def sync_check():
893894
await to_thread_run_sync(sync_check, cancellable=True)
894895

895896
assert cancel_scope.cancelled_caught
896-
assert await to_thread_run_sync(partial(queue.get, timeout=1))
897+
assert not await to_thread_run_sync(partial(queue.get, timeout=1))
897898

898899
async def no_checkpoint():
899900
return True
@@ -917,7 +918,7 @@ def async_check():
917918
await to_thread_run_sync(async_check, cancellable=True)
918919

919920
assert cancel_scope.cancelled_caught
920-
assert await to_thread_run_sync(partial(queue.get, timeout=1))
921+
assert not await to_thread_run_sync(partial(queue.get, timeout=1))
921922

922923
async def async_time_bomb():
923924
cancel_scope.cancel()

trio/_threads.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from trio._core._traps import RaiseCancelT
1818

1919
from ._core import (
20+
CancelScope,
2021
RunVar,
2122
TrioToken,
2223
disable_ki_protection,
@@ -86,6 +87,7 @@ class Run(Generic[RetT]):
8687
queue: stdlib_queue.SimpleQueue[outcome.Outcome[RetT]] = attr.ib(
8788
init=False, factory=stdlib_queue.SimpleQueue
8889
)
90+
scope: CancelScope = attr.ib(init=False, factory=CancelScope)
8991

9092
@disable_ki_protection
9193
async def unprotected_afn(self) -> RetT:
@@ -106,7 +108,12 @@ async def run(self) -> None:
106108
await trio.lowlevel.cancel_shielded_checkpoint()
107109

108110
async def run_system(self) -> None:
109-
result = await outcome.acapture(self.unprotected_afn)
111+
# NOTE: There is potential here to only conditionally enter a CancelScope
112+
# when we need it, sparing some computation. But doing so adds substantial
113+
# complexity, so we'll leave it until real need is demonstrated.
114+
with self.scope:
115+
result = await outcome.acapture(self.unprotected_afn)
116+
assert not self.scope.cancelled_caught, "any Cancelled should go to our parent"
110117
self.queue.put_nowait(result)
111118

112119

@@ -403,13 +410,14 @@ def _send_message_to_host_task(
403410
message: Run[RetT] | RunSync[RetT], trio_token: TrioToken
404411
) -> None:
405412
task_register = PARENT_TASK_DATA.task_register
406-
cancel_register = PARENT_TASK_DATA.cancel_register
407413

408414
def in_trio_thread() -> None:
409415
task = task_register[0]
410416
if task is None:
411-
raise_cancel = cancel_register[0]
412-
message.queue.put_nowait(outcome.capture(raise_cancel))
417+
# Our parent task is gone! Punt to a system task.
418+
if isinstance(message, Run):
419+
message.scope.cancel()
420+
_send_message_to_system_task(message, trio_token)
413421
else:
414422
trio.lowlevel.reschedule(task, outcome.Value(message))
415423

@@ -509,8 +517,6 @@ def from_thread_run_sync(
509517
Raises:
510518
RunFinishedError: if the corresponding call to `trio.run` has
511519
already completed.
512-
Cancelled: if the corresponding `trio.to_thread.run_sync` task is
513-
cancellable and exits before this function is called.
514520
RuntimeError: if you try calling this from inside the Trio thread,
515521
which would otherwise cause a deadlock or if no ``trio_token`` was
516522
provided, and we can't infer one from context.

0 commit comments

Comments
 (0)