On Tue, Feb 18, 2025 at 07:20:12PM +0100, Kevin Wolf wrote: > This adds helper functions that allow running Rust futures to completion > using QEMU's event loops.
This commit is a cliff-hanger. I'm intrigued to find out how timer, fd, etc event loop integration will work :). > > Signed-off-by: Kevin Wolf <kw...@redhat.com> > --- > include/qemu/coroutine-rust.h | 24 +++++++++++ > rust/wrapper.h | 1 + > util/qemu-co-rust-async.c | 55 +++++++++++++++++++++++++ > rust/qemu-api/meson.build | 1 + > rust/qemu-api/src/futures.rs | 77 +++++++++++++++++++++++++++++++++++ > rust/qemu-api/src/lib.rs | 1 + > util/meson.build | 3 ++ > 7 files changed, 162 insertions(+) > create mode 100644 include/qemu/coroutine-rust.h > create mode 100644 util/qemu-co-rust-async.c > create mode 100644 rust/qemu-api/src/futures.rs > > diff --git a/include/qemu/coroutine-rust.h b/include/qemu/coroutine-rust.h > new file mode 100644 > index 0000000000..0c5cf42a6b > --- /dev/null > +++ b/include/qemu/coroutine-rust.h > @@ -0,0 +1,24 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Helpers to run Rust futures using QEMU coroutines > + * > + * Copyright Red Hat > + * > + * Author: > + * Kevin Wolf <kw...@redhat.com> > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or > later. > + * See the COPYING.LIB file in the top-level directory. > + */ > + > +#ifndef QEMU_COROUTINE_RUST_H > +#define QEMU_COROUTINE_RUST_H > + > +typedef struct RustBoxedFuture RustBoxedFuture; > +typedef void coroutine_fn RunFuture(RustBoxedFuture *future, void *opaque); > + > +void no_coroutine_fn rust_run_future(RustBoxedFuture *future, > + RunFuture *entry, > + void *opaque); This adds a blocking (aio_poll()-style) API. The more blocking APIs we add, the more points are created where QEMU hangs when the async operation doesn't complete in a reasonable amount of time. It would be best to avoid introducing new blocking APIs, but sometimes it is unavoidable. rust_run_future() is very generic and I think the downsides should be pointed out to discourage people from using it when not absolutely necessary. Can you document when it's appropriate to use this API? > + > +#endif > diff --git a/rust/wrapper.h b/rust/wrapper.h > index 303d7bba7f..3dc385e256 100644 > --- a/rust/wrapper.h > +++ b/rust/wrapper.h > @@ -58,3 +58,4 @@ typedef enum memory_order { > #include "block/block_int.h" > #include "block/qdict.h" > #include "qapi/qapi-visit-block-core.h" > +#include "qemu/coroutine-rust.h" > diff --git a/util/qemu-co-rust-async.c b/util/qemu-co-rust-async.c > new file mode 100644 > index 0000000000..d893dfb7bd > --- /dev/null > +++ b/util/qemu-co-rust-async.c > @@ -0,0 +1,55 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Helpers to run Rust futures using QEMU coroutines > + * > + * Copyright Red Hat > + * > + * Author: > + * Kevin Wolf <kw...@redhat.com> > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or > later. > + * See the COPYING.LIB file in the top-level directory. > + */ > + > +#include "qemu/osdep.h" > + > +#include "block/aio-wait.h" > +#include "qemu/coroutine.h" > +#include "qemu/coroutine-rust.h" > +#include "qemu/main-loop.h" > + > +typedef struct FutureCo { > + RustBoxedFuture *future; > + RunFuture *entry; > + void *opaque; > + bool done; > +} FutureCo; > + > +static void coroutine_fn rust_co_run_future_entry(void *opaque) > +{ > + FutureCo *data = opaque; > + > + data->entry(data->future, data->opaque); > + data->done = true; > + aio_wait_kick(); > +} > + > +void no_coroutine_fn rust_run_future(RustBoxedFuture *future, > + RunFuture *entry, > + void *opaque) > +{ > + AioContext *ctx = qemu_get_current_aio_context(); > + Coroutine *co; > + FutureCo data = { > + .future = future, > + .entry = entry, > + .opaque = opaque, > + .done = false, > + }; > + > + GLOBAL_STATE_CODE(); > + > + co = qemu_coroutine_create(rust_co_run_future_entry, &data); > + aio_co_enter(ctx, co); > + AIO_WAIT_WHILE(ctx, !data.done); > +} > diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build > index e0a3052c79..44fd34e193 100644 > --- a/rust/qemu-api/meson.build > +++ b/rust/qemu-api/meson.build > @@ -22,6 +22,7 @@ sources_core = [ > 'src/chardev.rs', > 'src/c_str.rs', > 'src/errno.rs', > + 'src/futures.rs', > 'src/module.rs', > 'src/offset_of.rs', > 'src/prelude.rs', > diff --git a/rust/qemu-api/src/futures.rs b/rust/qemu-api/src/futures.rs > new file mode 100644 > index 0000000000..cd307a1d62 > --- /dev/null > +++ b/rust/qemu-api/src/futures.rs > @@ -0,0 +1,77 @@ > +use crate::bindings; > +use std::ffi::c_void; > +use std::future::Future; > +use std::mem::MaybeUninit; > +use std::sync::Arc; > +use std::task::{Context, Poll, Wake, Waker}; > + > +struct RunFutureWaker { > + co: *mut bindings::Coroutine, > +} > +unsafe impl Send for RunFutureWaker {} > +unsafe impl Sync for RunFutureWaker {} > + > +impl Wake for RunFutureWaker { > + fn wake(self: Arc<Self>) { > + unsafe { > + bindings::aio_co_wake(self.co); > + } > + } > +} > + > +/// Use QEMU's event loops to run a Rust [`Future`] to completion and return > its result. > +/// > +/// This function must be called in coroutine context. If the future isn't > ready yet, it yields. > +pub fn qemu_co_run_future<F: Future>(future: F) -> F::Output { > + let waker = Waker::from(Arc::new(RunFutureWaker { > + co: unsafe { bindings::qemu_coroutine_self() }, > + })); > + let mut cx = Context::from_waker(&waker); > + > + let mut pinned_future = std::pin::pin!(future); > + loop { > + match pinned_future.as_mut().poll(&mut cx) { > + Poll::Ready(res) => return res, > + Poll::Pending => unsafe { > + bindings::qemu_coroutine_yield(); > + }, > + } > + } > +} > + > +/// Wrapper around [`qemu_co_run_future`] that can be called from C. > +/// > +/// # Safety > +/// > +/// `future` must be a valid pointer to an owned `F` (it will be freed in > this function). `output` > +/// must be a valid pointer representing a mutable reference to an > `F::Output` where the result can > +/// be stored. > +unsafe extern "C" fn rust_co_run_future<F: Future>( > + future: *mut bindings::RustBoxedFuture, > + output: *mut c_void, > +) { > + let future = unsafe { Box::from_raw(future.cast::<F>()) }; > + let output = output.cast::<F::Output>(); > + let ret = qemu_co_run_future(*future); > + unsafe { > + output.write(ret); > + } > +} > + > +/// Use QEMU's event loops to run a Rust [`Future`] to completion and return > its result. > +/// > +/// This function must be called outside of coroutine context to avoid > deadlocks. It blocks and rust_run_future() has GLOBAL_STATE_CODE() so qemu_run_future() needs to run not just outside coroutine context, but also under the BQL. Should this be mentioned? > +/// runs a nested even loop until the future is ready and returns a result. > +pub fn qemu_run_future<F: Future>(future: F) -> F::Output { > + let future_ptr = Box::into_raw(Box::new(future)); > + let mut output = MaybeUninit::<F::Output>::uninit(); > + unsafe { > + bindings::rust_run_future( > + future_ptr.cast::<bindings::RustBoxedFuture>(), > + #[allow(clippy::as_underscore)] > + Some(rust_co_run_future::<F> as _), This line is hard to follow. I think it's casting to the C equivalent type: void coroutine_fn (*)(RustBoxedFuture *future, void *opaque) I wonder if there's a clearer way of writing this. Maybe being explicit rather than implicit here would be helpful. If not, it's not a big deal, but I spent some time trying to figure out what this does and others might too. > + output.as_mut_ptr().cast::<c_void>(), > + ); > + output.assume_init() > + } > +} > diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs > index 825443abde..84928905f1 100644 > --- a/rust/qemu-api/src/lib.rs > +++ b/rust/qemu-api/src/lib.rs > @@ -20,6 +20,7 @@ > pub mod cell; > pub mod chardev; > pub mod errno; > +pub mod futures; > #[cfg(feature = "system")] > pub mod irq; > #[cfg(feature = "system")] > diff --git a/util/meson.build b/util/meson.build > index 780b5977a8..14a2ae17fd 100644 > --- a/util/meson.build > +++ b/util/meson.build > @@ -101,6 +101,9 @@ if have_block > util_ss.add(files('qemu-coroutine-sleep.c')) > util_ss.add(files('qemu-co-shared-resource.c')) > util_ss.add(files('qemu-co-timeout.c')) > + if have_rust > + util_ss.add(files('qemu-co-rust-async.c')) > + endif > util_ss.add(files('readline.c')) > util_ss.add(files('throttle.c')) > util_ss.add(files('timed-average.c')) > -- > 2.48.1 > >
signature.asc
Description: PGP signature