spawn_system_task() doesn't work immediately after start_guest_run()
Here's a fun one:
In [1]: import trio
In [2]: thunks = []
In [3]: trio.lowlevel.start_guest_run(trio.sleep_forever, run_sync_soon_threadsafe=thunks.append, done_callback=print)
In [4]: trio.lowlevel.spawn_system_task(trio.sleep_forever)
Out[4]: <Task 'trio.sleep_forever' at 0x7f0a6d379340>
In [5]: thunks.pop(0)()
In [6]: thunks.pop(0)()
Error(TrioInternalError('internal error in Trio - please file a bug!'))
What's going on: the system nursery doesn't exist yet at the time of spawn_system_task(), so the new task is started as though it's the init task, with a None parent_nursery and in particular a None cancel_status. The first time it yields, Trio tries to access an attribute of the None cancel status, and stuff blows up.
We could either teach spawn_system_task to recognize the case of a None system_nursery and issue a useful error, or could make start_guest_run() execute the first two guest ticks synchronously, so that the system nursery is available by the time control returns to user code. Thoughts on which to prefer?
This code looks like it should work, so I'd prefer the latter solution.
Ha, that is a fun one.
We could either teach spawn_system_task to recognize the case of a None system_nursery and issue a useful error, or could make start_guest_run() execute the first two guest ticks synchronously, so that the system nursery is available by the time control returns to user code. Thoughts on which to prefer?
I'd be OK with either, but agreed with @smurfix that making it work seems nice if we can do it.
The implementation should probably be something like: while not runner.startup_complete: tick(), rather than executing a fixed number of ticks.