Re: Can we make QMP commands in Rust always be coroutine safe?

Paolo Bonzini <[email protected]> Tue, 5 May 2026 12:51:00 +0200
Newsgroups org.nongnu.qemu-rust,org.nongnu.qemu-devel
Message-ID <[email protected]>
On 5/5/26 10:44, Markus Armbruster wrote:
>      Coroutine safety can be hard to prove, similar to thread safety.  Common
>      pitfalls are:
> 
>      - The BQL isn't held across ``qemu_coroutine_yield()``, so
>        operations that used to assume that they execute atomically may have
>        to be more careful to protect against changes in the global state.
> 
>      - Nested event loops (``AIO_WAIT_WHILE()`` etc.) are problematic in
>        coroutine context and can easily lead to deadlocks.  They should be
>        replaced by yielding and reentering the coroutine when the condition
>        becomes false.
> 
>      Since the command handler may assume coroutine context, any callers
>      other than the QMP dispatcher must also call it in coroutine context.
>      In particular, HMP commands calling such a QMP command handler must be
>      marked ``.coroutine = true`` in hmp-commands.hx.
> 
>      It is an error to specify both ``'coroutine': true`` and ``'allow-oob': true``
>      for a command.  We don't currently have a use case for both together and
>      without a use case, it's not entirely clear what the semantics should
>      be.
> 
> Can we make commands written in Rust always coroutine safe?

We won't *ever* have mixed coroutine/non-coroutine functions in Rust. 
Kevin's prototype used async functions (stackless coroutines) for Rust 
yielding functions, rather than qemu_coroutine_yield()[1].  Within sucg 
fubctuibs, yielding is much more explicit than in C (you have to write 
".await" explicitly at all levels of calling a yielding function).

But we have the BQL, and "coroutine: true" commands if I understand 
correctly are run outside it (they run in iothread context).  So any 
command that uses BQL-protected data cannot be coroutine safe, and that 
means it's likely that Rust would also have coroutine: true/false.

However, there are safeguards:

1) it will be impossible to write yielding code in a "coroutine: false" 
Rust command; it won't secretly start a nested event loop.

2) releasing the BQL while keeping a reference to a BqlRefCell is an 
instant panic;

This leaves nested event loops in "coroutine: true" commands as a 
potential pitfall.

Paolo

[1] suspension and resumption is represented respectively by futures (a 
discriminated union of a suspension state and a result after completion) 
and wakers.  To convert suspended async fns to qemu_coroutine_yield() 
calls, Kevin wrapped the async fns with a loop that continues until the 
future as a result, yielding across calls to the async fn; and to resume 
the suspended async fn, he wrote a waker that invokes 
qemu_coroutine_resume().