Skip to content

Commit b08763c

Browse files
committed
Add a debug-mode-breakpoint-causes-hang case!
Only found this by luck more or less (while working on something in a client project) and it turns out we can actually get to (yet another) hang state where SIGINT will be ignored by the root actor on teardown.. I've added all the necessary logic flags to reproduce. We obviously need a follow up bug issue and a test suite to replicate! It appears as though the following are required based on very light tinkering: - infected asyncio mode active - debug mode active - the `trio` context must breakpoint *before* `.started()`-ing - the `asyncio` must **not** error
1 parent cb04aa1 commit b08763c

File tree

1 file changed

+47
-9
lines changed

1 file changed

+47
-9
lines changed

examples/debugging/asyncio_bp.py

+47-9
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
import trio
44
import tractor
5+
from tractor import to_asyncio
6+
7+
8+
async def aio_sleep_forever():
9+
await asyncio.sleep(float('inf'))
510

611

712
async def bp_then_error(
813
to_trio: trio.MemorySendChannel,
914
from_trio: asyncio.Queue,
1015

16+
raise_after_bp: bool = True,
17+
1118
) -> None:
1219

1320
# sync with ``trio``-side (caller) task
@@ -18,40 +25,57 @@ async def bp_then_error(
1825
# we set `Lock.local_task_in_debug = 'sync'`, we probably want
1926
# some further, at least, meta-data about the task/actoq in debug
2027
# in terms of making it clear it's asyncio mucking about.
21-
2228
breakpoint()
2329

30+
# short checkpoint / delay
2431
await asyncio.sleep(0.5)
25-
raise ValueError('blah')
2632

33+
if raise_after_bp:
34+
raise ValueError('blah')
2735

28-
async def aio_sleep_forever():
29-
await asyncio.sleep(float('inf'))
36+
# TODO: test case with this so that it gets cancelled?
37+
else:
38+
# XXX NOTE: this is required in order to get the SIGINT-ignored
39+
# hang case documented in the module script section!
40+
await aio_sleep_forever()
3041

3142

3243
@tractor.context
3344
async def trio_ctx(
3445
ctx: tractor.Context,
46+
bp_before_started: bool = False,
3547
):
3648

3749
# this will block until the ``asyncio`` task sends a "first"
3850
# message, see first line in above func.
3951
async with (
40-
tractor.to_asyncio.open_channel_from(bp_then_error) as (first, chan),
52+
53+
to_asyncio.open_channel_from(
54+
bp_then_error,
55+
raise_after_bp=not bp_before_started,
56+
) as (first, chan),
57+
4158
trio.open_nursery() as n,
4259
):
4360

4461
assert first == 'start'
62+
63+
if bp_before_started:
64+
await tractor.breakpoint()
65+
4566
await ctx.started(first)
4667

4768
n.start_soon(
48-
tractor.to_asyncio.run_task,
69+
to_asyncio.run_task,
4970
aio_sleep_forever,
5071
)
5172
await trio.sleep_forever()
5273

5374

54-
async def main():
75+
async def main(
76+
bps_all_over: bool = False,
77+
78+
) -> None:
5579

5680
async with tractor.open_nursery() as n:
5781

@@ -63,11 +87,18 @@ async def main():
6387
loglevel='cancel',
6488
)
6589

66-
async with p.open_context(trio_ctx) as (ctx, first):
90+
async with p.open_context(
91+
trio_ctx,
92+
bp_before_started=bps_all_over,
93+
) as (ctx, first):
6794

6895
assert first == 'start'
69-
await trio.sleep_forever()
7096

97+
if bps_all_over:
98+
await tractor.breakpoint()
99+
100+
# await trio.sleep_forever()
101+
await ctx.cancel()
71102
assert 0
72103

73104
# TODO: case where we cancel from trio-side while asyncio task
@@ -76,4 +107,11 @@ async def main():
76107

77108

78109
if __name__ == '__main__':
110+
111+
# works fine B)
79112
trio.run(main)
113+
114+
# will hang and ignores SIGINT !!
115+
# NOTE: you'll need to send a SIGQUIT (via ctl-\) to kill it
116+
# manually..
117+
# trio.run(main, True)

0 commit comments

Comments
 (0)