@@ -2279,6 +2279,16 @@ def start_guest_run(
2279
2279
the host loop and then immediately starts the guest run, and then shuts
2280
2280
down the host when the guest run completes.
2281
2281
2282
+ Once :func:`start_guest_run` returns successfully, the guest run
2283
+ has been set up enough that you can invoke sync-colored Trio
2284
+ functions such as :func:`~trio.current_time`, :func:`spawn_system_task`,
2285
+ and :func:`current_trio_token`. If a `~trio.TrioInternalError` occurs
2286
+ during this early setup of the guest run, it will be raised out of
2287
+ :func:`start_guest_run`. All other errors, including all errors
2288
+ raised by the *async_fn*, will be delivered to your
2289
+ *done_callback* at some point after :func:`start_guest_run` returns
2290
+ successfully.
2291
+
2282
2292
Args:
2283
2293
2284
2294
run_sync_soon_threadsafe: An arbitrary callable, which will be passed a
@@ -2339,6 +2349,43 @@ def my_done_callback(run_outcome):
2339
2349
host_uses_signal_set_wakeup_fd = host_uses_signal_set_wakeup_fd ,
2340
2350
),
2341
2351
)
2352
+
2353
+ # Run a few ticks of the guest run synchronously, so that by the
2354
+ # time we return, the system nursery exists and callers can use
2355
+ # spawn_system_task. We don't actually run any user code during
2356
+ # this time, so it shouldn't be possible to get an exception here,
2357
+ # except for a TrioInternalError.
2358
+ next_send = None
2359
+ for tick in range (5 ): # expected need is 2 iterations + leave some wiggle room
2360
+ if runner .system_nursery is not None :
2361
+ # We're initialized enough to switch to async guest ticks
2362
+ break
2363
+ try :
2364
+ timeout = guest_state .unrolled_run_gen .send (next_send )
2365
+ except StopIteration : # pragma: no cover
2366
+ raise TrioInternalError (
2367
+ "Guest runner exited before system nursery was initialized"
2368
+ )
2369
+ if timeout != 0 : # pragma: no cover
2370
+ guest_state .unrolled_run_gen .throw (
2371
+ TrioInternalError (
2372
+ "Guest runner blocked before system nursery was initialized"
2373
+ )
2374
+ )
2375
+ # next_send should be the return value of
2376
+ # IOManager.get_events() if no I/O was waiting, which is
2377
+ # platform-dependent. We don't actually check for I/O during
2378
+ # this init phase because no one should be expecting any yet.
2379
+ next_send = 0 if sys .platform == "win32" else ()
2380
+ else : # pragma: no cover
2381
+ guest_state .unrolled_run_gen .throw (
2382
+ TrioInternalError (
2383
+ "Guest runner yielded too many times before "
2384
+ "system nursery was initialized"
2385
+ )
2386
+ )
2387
+
2388
+ guest_state .unrolled_run_next_send = Value (next_send )
2342
2389
run_sync_soon_not_threadsafe (guest_state .guest_tick )
2343
2390
2344
2391
0 commit comments