If the client is gone, and the session finished, no need to return. The async handler can use this information to avoid unnecessary work and exit earlier.
Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- include/qapi/qmp/dispatch.h | 8 ++++++++ qapi/qmp-dispatch.c | 10 ++++++++++ tests/test-qmp-cmds.c | 39 ++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 6aef0abc70..6673902e95 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -91,6 +91,14 @@ void qmp_return_free(QmpReturn *qret); void qmp_return(QmpReturn *qret, QObject *rsp); void qmp_return_error(QmpReturn *qret, Error *err); +/* + * @qmp_return_is_cancelled: + * + * Return true if the QmpReturn is cancelled, and free the QmpReturn + * in this case. + */ +bool qmp_return_is_cancelled(QmpReturn *qret); + void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); void qmp_register_async_command(QmpCommandList *cmds, const char *name, diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 85e99671a9..e5c1505b05 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -64,6 +64,16 @@ void qmp_return_free(QmpReturn *qret) } } +bool qmp_return_is_cancelled(QmpReturn *qret) +{ + if (!qret->session) { + qmp_return_free(qret); + return true; + } + + return false; +} + static void qmp_return_orderly(QmpReturn *qret) { QmpSession *session = qret->session; diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 75ef30f6ea..66ad8726fe 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -34,17 +34,29 @@ void qmp_cmd_success_response(Error **errp) { } +static GMainLoop *loop; + static gboolean cmd_async_idle(gpointer user_data) { QmpReturn *qret = user_data; - qmp_cmd_async_return(qret, g_new0(Empty2, 1)); + if (!qret->session) { + g_assert(qmp_return_is_cancelled(qret)); + g_main_loop_quit(loop); + g_main_loop_unref(loop); + loop = NULL; + } else { + qmp_cmd_async_return(qret, g_new0(Empty2, 1)); + } return G_SOURCE_REMOVE; } void qmp_cmd_async(const char *filename, QmpReturn *qret) { + if (g_str_equal(filename, "cancel")) { + qmp_session_destroy(qret->session); + } g_idle_add(cmd_async_idle, qret); } @@ -453,6 +465,30 @@ static void test_qmp_return_async(void) qobject_unref(req); } +static void test_qmp_return_async_cancel(void) +{ + QmpReturnAsync a = { { 0, }, }; + QDict *args = qdict_new(); + QDict *req = qdict_new(); + + a.loop = g_main_loop_new(NULL, TRUE); + qmp_session_init(&a.session, &qmp_commands, + NULL, dispatch_return_async); + + qdict_put_str(args, "filename", "cancel"); + qdict_put_str(req, "execute", "cmd-async"); + qdict_put(req, "arguments", args); + qmp_dispatch(&a.session, QOBJECT(req), false); + g_assert(a.loop); + + loop = a.loop; + g_main_loop_run(loop); + g_assert(!loop); + + qmp_session_destroy(&a.session); + qobject_unref(req); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -467,6 +503,7 @@ int main(int argc, char **argv) g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial); g_test_add_func("/qmp/return_orderly", test_qmp_return_orderly); g_test_add_func("/qmp/return_async", test_qmp_return_async); + g_test_add_func("/qmp/return_async_cancel", test_qmp_return_async_cancel); test_qmp_init_marshal(&qmp_commands); g_test_run(); -- 2.24.0