diff mbox

[23/36] qmp: use a return callback for the command reply

Message ID 1443189844-20341-24-git-send-email-marcandre.lureau@redhat.com
State New
Headers show

Commit Message

Marc-André Lureau Sept. 25, 2015, 2:03 p.m. UTC
From: Marc-André Lureau <marcandre.lureau@redhat.com>

Introduce QmpDispatchReturn, a callback called when a command reply is
ready to be sent. Future patches will extend the concept to allow async
replies.

QmpReturn and associated functions is used internally for sync
fonctions, but will be the basis of a async context (the dispatch return
callback and the top response dict).

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/qmp/dispatch.h | 14 +++++++++++--
 monitor.c                   | 13 ++++++++----
 qapi/qmp-dispatch.c         | 51 ++++++++++++++++++++++++++++++++++-----------
 qga/main.c                  | 22 +++++++++----------
 tests/test-qmp-commands.c   | 42 ++++++++++++++++++++-----------------
 5 files changed, 94 insertions(+), 48 deletions(-)
diff mbox

Patch

diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index 96de3bf..b62acba 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -18,6 +18,14 @@ 
 #include "qapi/qmp/qdict.h"
 #include "qapi/error.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 QmpCommandType
@@ -44,7 +52,8 @@  typedef struct QmpCommand
 void qmp_register_command(const char *name, QmpCommandFunc *fn,
                           QmpCommandOptions options);
 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);
@@ -53,6 +62,7 @@  bool qmp_has_success_response(const QmpCommand *cmd);
 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);
+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 d7ee509..677061d 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3570,6 +3570,13 @@  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, QList *tokens)
 {
     QObject *req, *rsp, *id = NULL;
@@ -3606,15 +3613,13 @@  static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
         goto err_out;
     }
 
-    rsp = qmp_dispatch(req, rqdict);
+    qmp_dispatch(req, rqdict, qmp_dispatch_return, mon);
+    rsp = NULL;
 
 err_out:
     if (err) {
         qdict_put_obj(rqdict, "error", qmp_build_error_object(err));
         error_free(err);
-    }
-
-    if (rsp) {
         monitor_json_emitter(mon, rsp);
     }
 
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index d2ae5f0..263ab65 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -60,7 +60,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;
@@ -118,23 +118,50 @@  QObject *qmp_build_error_object(Error *err)
                               error_get_pretty(err));
 }
 
-QObject *qmp_dispatch(QObject *request, QDict *rsp)
+static void do_qmp_return(QmpReturn *qret)
+{
+    QDict *rsp = qret->rsp;
+
+    qret->return_cb(QOBJECT(rsp), qret->opaque);
+
+    qobject_decref(QOBJECT(rsp));
+    g_free(qret);
+}
+
+void qmp_return(QmpReturn *qret, QObject *cmd_rsp)
+{
+    qdict_put_obj(qret->rsp, "return", cmd_rsp);
+
+    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 bfcde0b..21dba8f 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -546,21 +546,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 068db21..64d26af 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -76,53 +76,57 @@  __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_error(void)
 {
     QDict *req = qdict_new();
-    QObject *resp;
 
     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"));
+    qmp_dispatch(QOBJECT(req), NULL, dispatch_cmd_error_return, NULL);
 
-    qobject_decref(resp);
     QDECREF(req);
 }
 
-static QObject *test_qmp_dispatch(QDict *req)
-{
-    QObject *resp_obj;
-    QDict *resp;
-    QObject *ret;
+static QObject *ret;
 
-    resp_obj = qmp_dispatch(QOBJECT(req), NULL);
-    assert(resp_obj);
-    resp = qobject_to_qdict(resp_obj);
+static void qmp_dispatch_return(QObject *resp_obj, void *opaque)
+{
+    QDict *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);
+}
+
+static QObject *test_qmp_dispatch(QDict *req)
+{
+    qmp_dispatch(QOBJECT(req), NULL, qmp_dispatch_return, NULL);
+
     return ret;
 }