Add a new type of command, QmpCommandFuncAsync: those commands can return later thanks to QmpReturn. This commit introduces the new type and register function and teach qmp_dipatch() to call it without qmp_return(). The async_fn callback will be responsible for calling qmp_return(), either synchronously or asynchronously.
Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- include/qapi/qmp/dispatch.h | 10 +++++++++- qapi/qmp-dispatch.c | 27 ++++++++++++++++----------- qapi/qmp-registry.c | 27 ++++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 92d6fd1afb..6aef0abc70 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -21,6 +21,7 @@ typedef struct QmpReturn QmpReturn; typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); +typedef void (QmpCommandAsyncFunc)(QDict *, QmpReturn *); typedef enum QmpCommandOptions { @@ -28,12 +29,16 @@ typedef enum QmpCommandOptions QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), QCO_ALLOW_PRECONFIG = (1U << 2), + QCO_ASYNC = (1U << 3), } QmpCommandOptions; typedef struct QmpCommand { const char *name; - QmpCommandFunc *fn; + union { + QmpCommandFunc *fn; + QmpCommandAsyncFunc *async_fn; + }; QmpCommandOptions options; QTAILQ_ENTRY(QmpCommand) node; bool enabled; @@ -88,6 +93,9 @@ void qmp_return_error(QmpReturn *qret, Error *err); void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); +void qmp_register_async_command(QmpCommandList *cmds, const char *name, + QmpCommandAsyncFunc *fn, + QmpCommandOptions options); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); void qmp_session_init(QmpSession *session, diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 546a6c9f7b..1f493af67a 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -171,7 +171,7 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, return dict; } -static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, +static QObject *do_qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob, Error **errp) { Error *local_err = NULL; @@ -193,7 +193,7 @@ static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, command = qdict_get_str(dict, "exec-oob"); oob = true; } - cmd = qmp_find_command(cmds, command); + cmd = qmp_find_command(session->cmds, command); if (cmd == NULL) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found", command); @@ -224,14 +224,19 @@ static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, qobject_ref(args); } - cmd->fn(args, &ret, &local_err); - if (local_err) { - error_propagate(errp, local_err); - } else if (cmd->options & QCO_NO_SUCCESS_RESP) { - g_assert(!ret); - } else if (!ret) { - /* TODO turn into assertion */ - ret = QOBJECT(qdict_new()); + + if (cmd->options & QCO_ASYNC) { + cmd->async_fn(args, qmp_return_new(session, request)); + } else { + cmd->fn(args, &ret, &local_err); + if (local_err) { + error_propagate(errp, local_err); + } else if (cmd->options & QCO_NO_SUCCESS_RESP) { + g_assert(!ret); + } else if (!ret) { + /* TODO turn into assertion */ + ret = QOBJECT(qdict_new()); + } } qobject_unref(args); @@ -304,7 +309,7 @@ void qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob) Error *err = NULL; QObject *ret; - ret = do_qmp_dispatch(session->cmds, request, allow_oob, &err); + ret = do_qmp_dispatch(session, request, allow_oob, &err); if (err) { qmp_return_error(qmp_return_new(session, request), err); } else if (ret) { diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index d0f9a1d3e3..0f3d521ce5 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -15,16 +15,37 @@ #include "qemu/osdep.h" #include "qapi/qmp/dispatch.h" -void qmp_register_command(QmpCommandList *cmds, const char *name, - QmpCommandFunc *fn, QmpCommandOptions options) + +static QmpCommand *qmp_command_new(QmpCommandList *cmds, const char *name, + QmpCommandOptions options) { QmpCommand *cmd = g_malloc0(sizeof(*cmd)); cmd->name = name; - cmd->fn = fn; cmd->enabled = true; cmd->options = options; QTAILQ_INSERT_TAIL(cmds, cmd, node); + + return cmd; +} + + +void qmp_register_command(QmpCommandList *cmds, const char *name, + QmpCommandFunc *fn, QmpCommandOptions options) +{ + QmpCommand *cmd = qmp_command_new(cmds, name, options); + + assert(!(options & QCO_ASYNC)); + cmd->fn = fn; +} + +void qmp_register_async_command(QmpCommandList *cmds, const char *name, + QmpCommandAsyncFunc *fn, QmpCommandOptions options) +{ + QmpCommand *cmd = qmp_command_new(cmds, name, options); + + assert(options & QCO_ASYNC); + cmd->async_fn = fn; } const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name) -- 2.22.0.428.g6d5b264208