On Thu, Jul 16, 2026 at 10:40:31PM +0800, Zhang Chen wrote:
> Based on monitor QOM path tracking iothread users with holder.
> Introduce the AioContext in the Monitor struct to avoid repeated calls to
> iothread_get_aio_context() and ensure symmetrical ref/unref during
> monitor lifecycle.
>
> Signed-off-by: Zhang Chen <[email protected]>
> ---
> monitor/monitor-internal.h | 3 +++
> monitor/monitor.c | 22 ++++++++++++++++++++--
> monitor/qmp.c | 5 +++--
> 3 files changed, 26 insertions(+), 4 deletions(-)
>
> diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
> index 23829f32f9..caecceec93 100644
> --- a/monitor/monitor-internal.h
> +++ b/monitor/monitor-internal.h
> @@ -153,6 +153,9 @@ struct Monitor {
> guint out_watch;
> int mux_out;
> int reset_seen;
> +
> + /* iothread context */
> + AioContext *ctx;
> };
>
> struct MonitorHMPClass {
> diff --git a/monitor/monitor.c b/monitor/monitor.c
> index ed195fd97b..688bb85c81 100644
> --- a/monitor/monitor.c
> +++ b/monitor/monitor.c
> @@ -573,7 +573,7 @@ void monitor_suspend(Monitor *mon)
> * Kick I/O thread to make sure this takes effect. It'll be
> * evaluated again in prepare() of the watch object.
> */
> - aio_notify(iothread_get_aio_context(mon_iothread));
> + aio_notify(mon->ctx);
> }
>
> trace_monitor_suspend(mon, 1);
> @@ -668,6 +668,17 @@ void monitor_cleanup(void)
> qemu_mutex_unlock(&monitor_lock);
> monitor_flush(mon);
> qemu_mutex_lock(&monitor_lock);
> +
> + if (mon_iothread) {
> + g_autofree char *path = object_get_canonical_path(OBJECT(mon));
> + const IOThreadHolder io_holder = {
> + .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> + .u.qom_object.qom_path = path,
> + };
> +
> + iothread_put_aio_context(mon_iothread, &io_holder);
> + mon->ctx = NULL;
> + }
> object_unparent(OBJECT(mon));
> }
> qemu_mutex_unlock(&monitor_lock);
> @@ -732,7 +743,14 @@ static void monitor_complete(UserCreatable *uc, Error
> **errp)
> mon_iothread = iothread_create("mon_iothread", &error_abort);
> }
>
> - ctx = iothread_get_aio_context(mon_iothread);
> + g_autofree char *path = object_get_canonical_path(OBJECT(mon));
> + const IOThreadHolder io_holder = {
> + .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> + .u.qom_object.qom_path = path,
> + };
> +
> + mon->ctx = iothread_ref_and_get_aio_context(mon_iothread,
> &io_holder);
> + ctx = mon->ctx;
> } else {
> ctx = qemu_get_aio_context();
> }
Even though QMP only takes the first branch, I'd be more
comfortable if we set mon->ctx in both branches of this
condition, so we can always assume that mon->ctx is
non-NULL.
> diff --git a/monitor/qmp.c b/monitor/qmp.c
> index 223e0643c2..b16bd77688 100644
> --- a/monitor/qmp.c
> +++ b/monitor/qmp.c
> @@ -732,7 +732,8 @@ static void monitor_qmp_complete(UserCreatable *uc, Error
> **errp)
> * thread. Schedule a bottom half.
> */
> mon->setup_pending = true;
> - aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
> +
> + aio_bh_schedule_oneshot(MONITOR(mon)->ctx,
> monitor_qmp_setup_handlers_bh, mon);
> /* The bottom half will add @mon to @mon_list */
> } else {
> @@ -787,7 +788,7 @@ static bool monitor_qmp_prepare_delete(UserCreatable *uc,
> Error **errp)
>
> /* Synchronize with in-flight iothread callbacks. */
> if (monitor_requires_iothread(mon)) {
> - aio_wait_bh_oneshot(iothread_get_aio_context(mon_iothread),
> + aio_wait_bh_oneshot(MONITOR(mon)->ctx,
> monitor_qmp_iothread_quiesce, NULL);
> }
...this unconditionally accesses mon->ctx, and so will surely hit
a NULL
>
> --
> 2.49.0
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|