diff mbox

[v2,04/25] qmp: use a return callback for the command reply

Message ID 20170118160332.13390-5-marcandre.lureau@redhat.com
State New
Headers show

Commit Message

Marc-André Lureau Jan. 18, 2017, 4:03 p.m. UTC
Introduce QmpDispatchReturn, a callback called when a command reply is
ready to be sent. Future patches will extend the context to enable async
replies. Currently, all qmp commands reply in the calling context (they
are all sync).

QmpReturn and associated functions are used internally for the sync
dispatch, but will be the basis of the following async context, that's
the reason why they are exported.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/qmp/dispatch.h | 22 ++++++++++++++++-
 monitor.c                   | 19 +++++++++------
 qapi/qmp-dispatch.c         | 58 +++++++++++++++++++++++++++++++++++----------
 qga/main.c                  | 22 ++++++++---------
 tests/test-qmp-commands.c   | 54 ++++++++++++++++++++++-------------------
 5 files changed, 119 insertions(+), 56 deletions(-)
diff mbox

Patch

diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index db483481b0..780b3e2a09 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -17,6 +17,14 @@ 
 #include "qapi/qmp/qobject.h"
 #include "qapi/qmp/qdict.h"
 
+typedef void (QmpDispatchReturn) (QObject *rsp, void *opaque);
+
+typedef struct QmpReturn {
+    QDict *rsp;
+    QmpDispatchReturn *return_cb;
+    void *opaque;
+} QmpReturn;
+
 typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
 
 typedef enum QmpCommandOptions
@@ -38,7 +46,8 @@  void qmp_register_command(const char *name, QmpCommandFunc *fn,
                           QmpCommandOptions options);
 void qmp_unregister_command(const char *name);
 QmpCommand *qmp_find_command(const char *name);
-QObject *qmp_dispatch(QObject *request, QDict *rsp);
+void qmp_dispatch(QObject *request, QDict *rsp,
+                  QmpDispatchReturn *return_cb, void *opaque);
 void qmp_disable_command(const char *name);
 void qmp_enable_command(const char *name);
 bool qmp_command_is_enabled(const QmpCommand *cmd);
@@ -48,4 +57,15 @@  QObject *qmp_build_error_object(Error *err);
 typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
 void qmp_for_each_command(qmp_cmd_callback_fn fn, void *opaque);
 
+/*
+ * qmp_return{_error}:
+ *
+ * Construct the command reply, and call the
+ * return_cb() associated with the dispatch.
+ *
+ * Finally, free the QmpReturn.
+ */
+void qmp_return(QmpReturn *qret, QObject *cmd_rsp);
+void qmp_return_error(QmpReturn *qret, Error *err);
+
 #endif
diff --git a/monitor.c b/monitor.c
index edd30e4d8f..462ee127b4 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3723,15 +3723,21 @@  static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp)
     return input_dict;
 }
 
+static void qmp_dispatch_return(QObject *rsp, void *opaque)
+{
+    Monitor *mon = opaque;
+
+    monitor_json_emitter(mon, rsp);
+}
+
 static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
 {
-    QObject *req, *rsp, *id = NULL;
+    QObject *req, *id = NULL;
     QDict *qdict, *rqdict = qdict_new();
     const char *cmd_name;
     Monitor *mon = cur_mon;
     Error *err = NULL;
 
-    rsp = QOBJECT(rqdict);
     req = json_parser_parse_err(tokens, NULL, &err);
     if (err || !req || qobject_type(req) != QTYPE_QDICT) {
         if (!err) {
@@ -3759,16 +3765,15 @@  static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
         goto err_out;
     }
 
-    rsp = qmp_dispatch(req, rqdict);
+    qmp_dispatch(req, rqdict, qmp_dispatch_return, mon);
+    qobject_decref(req);
+    return;
 
 err_out:
     if (err) {
         qdict_put_obj(rqdict, "error", qmp_build_error_object(err));
         error_free(err);
-    }
-
-    if (rsp) {
-        monitor_json_emitter(mon, rsp);
+        monitor_json_emitter(mon, QOBJECT(rqdict));
     }
 
     QDECREF(rqdict);
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index dbf2e5b655..7e9d03f262 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -62,7 +62,7 @@  static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
     return dict;
 }
 
-static QObject *do_qmp_dispatch(QObject *request, Error **errp)
+static QObject *do_qmp_dispatch(QObject *request, QmpReturn *qret, Error **errp)
 {
     Error *local_err = NULL;
     const char *command;
@@ -116,23 +116,57 @@  QObject *qmp_build_error_object(Error *err)
                               error_get_pretty(err));
 }
 
-QObject *qmp_dispatch(QObject *request, QDict *rsp)
+static void qmp_return_free(QmpReturn *qret)
+{
+    QDict *rsp = qret->rsp;
+
+    qobject_decref(QOBJECT(rsp));
+    g_free(qret);
+}
+
+static void do_qmp_return(QmpReturn *qret)
+{
+    QDict *rsp = qret->rsp;
+
+    qret->return_cb(QOBJECT(rsp), qret->opaque);
+
+    qmp_return_free(qret);
+}
+
+void qmp_return(QmpReturn *qret, QObject *cmd_rsp)
+{
+    qdict_put_obj(qret->rsp, "return", cmd_rsp ?: QOBJECT(qdict_new()));
+
+    do_qmp_return(qret);
+}
+
+void qmp_return_error(QmpReturn *qret, Error *err)
+{
+    qdict_put_obj(qret->rsp, "error", qmp_build_error_object(err));
+    error_free(err);
+
+    do_qmp_return(qret);
+}
+
+void qmp_dispatch(QObject *request, QDict *rsp,
+                  QmpDispatchReturn *return_cb, void *opaque)
 {
     Error *err = NULL;
+    QmpReturn *qret = g_new0(QmpReturn, 1);
     QObject *ret;
 
-    ret = do_qmp_dispatch(request, &err);
+    assert(return_cb);
+
+    qret->rsp = rsp ?: qdict_new();
+    qret->return_cb = return_cb;
+    qret->opaque = opaque;
+
+    ret = do_qmp_dispatch(request, qret, &err);
 
-    rsp = rsp ?: qdict_new();
     if (err) {
-        qdict_put_obj(rsp, "error", qmp_build_error_object(err));
-        error_free(err);
+        assert(!ret);
+        qmp_return_error(qret, err);
     } else if (ret) {
-        qdict_put_obj(rsp, "return", ret);
-    } else {
-        QDECREF(rsp);
-        return NULL;
+        qmp_return(qret, ret);
     }
-
-    return QOBJECT(rsp);
 }
diff --git a/qga/main.c b/qga/main.c
index 442990c869..a50acf27a5 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -548,21 +548,21 @@  static int send_response(GAState *s, QObject *payload)
     return 0;
 }
 
-static void process_command(GAState *s, QDict *req)
+static void dispatch_return_cb(QObject *rsp, void *opaque)
 {
-    QObject *rsp = NULL;
-    int ret;
+    GAState *s = opaque;
+    int ret = send_response(s, rsp);
+
+    if (ret) {
+        g_warning("error sending response: %s", strerror(ret));
+    }
+}
 
+static void process_command(GAState *s, QDict *req)
+{
     g_assert(req);
     g_debug("processing command");
-    rsp = qmp_dispatch(QOBJECT(req), NULL);
-    if (rsp) {
-        ret = send_response(s, rsp);
-        if (ret) {
-            g_warning("error sending response: %s", strerror(ret));
-        }
-        qobject_decref(rsp);
-    }
+    qmp_dispatch(QOBJECT(req), NULL, dispatch_return_cb, s);
 }
 
 /* handle requests/control events coming in over the channel */
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index a6d8e4b141..6510245b4f 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -85,23 +85,30 @@  __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
     return ret;
 }
 
+static void dispatch_cmd_return(QObject *resp, void *opaque)
+{
+    assert(resp != NULL);
+    assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+}
 
 /* test commands with no input and no return value */
 static void test_dispatch_cmd(void)
 {
     QDict *req = qdict_new();
-    QObject *resp;
 
     qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
 
-    resp = qmp_dispatch(QOBJECT(req), NULL);
-    assert(resp != NULL);
-    assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+    qmp_dispatch(QOBJECT(req), NULL, dispatch_cmd_return, NULL);
 
-    qobject_decref(resp);
     QDECREF(req);
 }
 
+static void dispatch_cmd_error_return(QObject *resp, void *opaque)
+{
+    assert(resp != NULL);
+    assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+}
+
 /* test commands that return an error due to invalid parameters */
 static void test_dispatch_cmd_failure(void)
 {
@@ -111,11 +118,7 @@  static void test_dispatch_cmd_failure(void)
 
     qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
 
-    resp = qmp_dispatch(QOBJECT(req), NULL);
-    assert(resp != NULL);
-    assert(qdict_haskey(qobject_to_qdict(resp), "error"));
-
-    qobject_decref(resp);
+    qmp_dispatch(QOBJECT(req), NULL, dispatch_cmd_error_return, NULL);
     QDECREF(req);
 
     /* check that with extra arguments it throws an error */
@@ -125,28 +128,29 @@  static void test_dispatch_cmd_failure(void)
 
     qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
 
-    resp = qmp_dispatch(QOBJECT(req), NULL);
-    assert(resp != NULL);
-    assert(qdict_haskey(qobject_to_qdict(resp), "error"));
-
-    qobject_decref(resp);
+    qmp_dispatch(QOBJECT(req), NULL, dispatch_cmd_error_return, NULL);
     QDECREF(req);
 }
 
+static QObject *dispatch_ret;
+
+static void qmp_dispatch_return(QObject *resp_obj, void *opaque)
+{
+    QDict *resp = qobject_to_qdict(resp_obj);
+    assert(resp && !qdict_haskey(resp, "error"));
+    dispatch_ret = qdict_get(resp, "return");
+    assert(dispatch_ret);
+    qobject_incref(dispatch_ret);
+}
+
 static QObject *test_qmp_dispatch(QDict *req)
 {
-    QObject *resp_obj;
-    QDict *resp;
     QObject *ret;
 
-    resp_obj = qmp_dispatch(QOBJECT(req), NULL);
-    assert(resp_obj);
-    resp = qobject_to_qdict(resp_obj);
-    assert(resp && !qdict_haskey(resp, "error"));
-    ret = qdict_get(resp, "return");
-    assert(ret);
-    qobject_incref(ret);
-    qobject_decref(resp_obj);
+    qmp_dispatch(QOBJECT(req), NULL, qmp_dispatch_return, NULL);
+    ret = dispatch_ret;
+    dispatch_ret = NULL;
+
     return ret;
 }