All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Marc-André Lureau" <marcandre.lureau@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Eduardo Habkost" <ehabkost@redhat.com>,
	"Juan Quintela" <quintela@redhat.com>,
	"Dr. David Alan Gilbert" <dgilbert@redhat.com>,
	"Eric Blake" <eblake@redhat.com>,
	"Cleber Rosa" <crosa@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Gerd Hoffmann" <kraxel@redhat.com>,
	"Michael Roth" <mdroth@linux.vnet.ibm.com>,
	"Marc-André Lureau" <marcandre.lureau@redhat.com>
Subject: [Qemu-devel] [PATCH v3 19/38] QmpSession: add a return_cb
Date: Mon, 26 Mar 2018 17:08:57 +0200	[thread overview]
Message-ID: <20180326150916.9602-20-marcandre.lureau@redhat.com> (raw)
In-Reply-To: <20180326150916.9602-1-marcandre.lureau@redhat.com>

The introduced return_cb will allow to delay finishing the dispatch
and sending the response asynchronously. For now, this is just
modifying qmp_dispatch() to call the callback synchronously, and
return void.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/qmp/dispatch.h |  8 +++-
 monitor.c                   | 52 +++++++++++-----------
 qapi/qmp-dispatch.c         | 19 ++++++--
 qga/main.c                  | 25 ++++++-----
 tests/test-qmp-cmds.c       | 86 ++++++++++++++++++-------------------
 5 files changed, 101 insertions(+), 89 deletions(-)

diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index 10ba0745c7..7bf0b6a437 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -37,8 +37,10 @@ typedef struct QmpCommand
 typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList;
 
 typedef struct QmpSession QmpSession;
+typedef void (QmpDispatchReturn) (QmpSession *session, QDict *rsp);
 
 struct QmpSession {
+    QmpDispatchReturn *return_cb;
     QmpCommandList *cmds;
 };
 
@@ -46,9 +48,11 @@ void qmp_register_command(QmpCommandList *cmds, const char *name,
                           QmpCommandFunc *fn, QmpCommandOptions options);
 void qmp_unregister_command(QmpCommandList *cmds, const char *name);
 QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name);
-void qmp_session_init(QmpSession *session, QmpCommandList *cmds);
+void qmp_session_init(QmpSession *session,
+                      QmpCommandList *cmds, QmpDispatchReturn *return_cb);
+
 void qmp_session_destroy(QmpSession *session);
-QDict *qmp_dispatch(QmpSession *session, QDict *request);
+void qmp_dispatch(QmpSession *session, QDict *request);
 void qmp_disable_command(QmpCommandList *cmds, const char *name);
 void qmp_enable_command(QmpCommandList *cmds, const char *name);
 
diff --git a/monitor.c b/monitor.c
index b90f8566c8..93ecb03d04 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3900,23 +3900,6 @@ static int monitor_can_read(void *opaque)
     return !atomic_mb_read(&mon->suspend_cnt);
 }
 
-/* take the ownership of rsp */
-static void monitor_qmp_respond(Monitor *mon, QDict *rsp)
-{
-    if (mon->qmp.session.cmds == &qmp_cap_negotiation_commands) {
-        QDict *qdict = qdict_get_qdict(rsp, "error");
-        if (qdict
-            && !g_strcmp0(qdict_get_try_str(qdict, "class"),
-                          QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
-            /* Provide a more useful error message */
-            qdict_put_str(qdict, "desc", "Expecting capabilities"
-                          " negotiation with 'qmp_capabilities'");
-        }
-    }
-    monitor_json_emitter(mon, QOBJECT(rsp));
-    QDECREF(rsp);
-}
-
 struct QMPRequest {
     /* Owner of the request */
     Monitor *mon;
@@ -3927,6 +3910,24 @@ struct QMPRequest {
 };
 typedef struct QMPRequest QMPRequest;
 
+static void dispatch_return_cb(QmpSession *session, QDict *rsp)
+{
+    Monitor *mon = container_of(session, Monitor, qmp.session);
+
+    if (mon->qmp.session.cmds == &qmp_cap_negotiation_commands) {
+        QDict *qdict = qdict = qdict_get_qdict(rsp, "error");
+        if (qdict
+            && !g_strcmp0(qdict_get_try_str(qdict, "class"),
+                    QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
+            /* Provide a more useful error message */
+            qdict_put_str(qdict, "desc", "Expecting capabilities negotiation"
+                          " with 'qmp_capabilities'");
+        }
+    }
+
+    monitor_json_emitter(mon, QOBJECT(rsp));
+}
+
 /*
  * Dispatch one single QMP request. The function will free the req_obj
  * and objects inside it before return.
@@ -3934,7 +3935,7 @@ typedef struct QMPRequest QMPRequest;
 static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
 {
     Monitor *mon, *old_mon;
-    QDict *req, *rsp = NULL;
+    QDict *req;
 
     req = req_obj->req;
     mon = req_obj->mon;
@@ -3950,16 +3951,9 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
     old_mon = cur_mon;
     cur_mon = mon;
 
-    rsp = qmp_dispatch(&mon->qmp.session, req);
+    qmp_dispatch(&mon->qmp.session, req);
 
     cur_mon = old_mon;
-
-    /* Respond if necessary */
-    if (rsp) {
-        monitor_qmp_respond(mon, rsp);
-    }
-
-
     QDECREF(req);
 }
 
@@ -4107,7 +4101,8 @@ err:
     qdict = qdict_new();
     qdict_put_obj(qdict, "error", qmp_build_error_object(err));
     error_free(err);
-    monitor_qmp_respond(mon, qdict);
+    monitor_json_emitter(mon, QOBJECT(qdict));
+    QDECREF(qdict);
     qobject_decref(req);
 }
 
@@ -4223,7 +4218,8 @@ static void monitor_qmp_event(void *opaque, int event)
 
     switch (event) {
     case CHR_EVENT_OPENED:
-        qmp_session_init(&mon->qmp.session, &qmp_cap_negotiation_commands);
+        qmp_session_init(&mon->qmp.session,
+                         &qmp_cap_negotiation_commands, dispatch_return_cb);
         monitor_qmp_caps_reset(mon);
         data = get_qmp_greeting(mon);
         monitor_json_emitter(mon, data);
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 7fd4e41b26..5274aa59cc 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -146,17 +146,27 @@ bool qmp_is_oob(const QDict *dict)
     return qbool_get_bool(bool_obj);
 }
 
-void qmp_session_init(QmpSession *session, QmpCommandList *cmds)
+void qmp_session_init(QmpSession *session,
+                      QmpCommandList *cmds, QmpDispatchReturn *return_cb)
 {
+    assert(return_cb);
+    assert(!session->return_cb);
+
     session->cmds = cmds;
+    session->return_cb = return_cb;
 }
 
 void qmp_session_destroy(QmpSession *session)
 {
+    if (!session->return_cb) {
+        return;
+    }
+
     session->cmds = NULL;
+    session->return_cb = NULL;
 }
 
-QDict *qmp_dispatch(QmpSession *session, QDict *req)
+void qmp_dispatch(QmpSession *session, QDict *req)
 {
     Error *err = NULL;
     QObject *ret;
@@ -177,8 +187,9 @@ QDict *qmp_dispatch(QmpSession *session, QDict *req)
         qdict_put_obj(rsp, "return", ret);
     } else {
         QDECREF(rsp);
-        return NULL;
+        return;
     }
 
-    return rsp;
+    session->return_cb(session, rsp);
+    QDECREF(rsp);
 }
diff --git a/qga/main.c b/qga/main.c
index b5d7cc9e8f..46349395ba 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -580,14 +580,22 @@ static int send_response(GAState *s, QObject *payload)
     return 0;
 }
 
+static void dispatch_return_cb(QmpSession *session, QDict *rsp)
+{
+    GAState *s = container_of(session, GAState, session);
+    int ret = send_response(s, QOBJECT(rsp));
+    if (ret < 0) {
+        g_warning("error sending response: %s", strerror(-ret));
+    }
+}
+
 /* handle requests/control events coming in over the channel */
 static void process_event(JSONMessageParser *parser, GQueue *tokens)
 {
     GAState *s = container_of(parser, GAState, parser);
     QObject *obj;
-    QDict *req, *rsp = NULL;
+    QDict *req;
     Error *err = NULL;
-    int ret;
 
     g_assert(s && parser);
 
@@ -604,19 +612,14 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
     }
 
     g_debug("processing command");
-    rsp = qmp_dispatch(&s->session, req);
+    qmp_dispatch(&s->session, req);
 
 end:
     if (err) {
-        rsp = qdict_new();
+        QDict *rsp = qdict_new();
         qdict_put_obj(rsp, "error", qmp_build_error_object(err));
         error_free(err);
-    }
-    if (rsp) {
-        ret = send_response(s, QOBJECT(rsp));
-        if (ret < 0) {
-            g_warning("error sending error response: %s", strerror(-ret));
-        }
+        dispatch_return_cb(&s->session, rsp);
         QDECREF(rsp);
     }
     qobject_decref(obj);
@@ -1305,7 +1308,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
     ga_command_state_init(s, s->command_state);
     ga_command_state_init_all(s->command_state);
     json_message_parser_init(&s->parser, process_event);
-    qmp_session_init(&s->session, &ga_commands);
+    qmp_session_init(&s->session, &ga_commands, dispatch_return_cb);
 #ifndef _WIN32
     if (!register_signal_handlers()) {
         g_critical("failed to register signal handlers");
diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c
index 0c1fecb281..5e6e75b133 100644
--- a/tests/test-qmp-cmds.c
+++ b/tests/test-qmp-cmds.c
@@ -93,85 +93,83 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
     return ret;
 }
 
+static void dispatch_cmd_return(QmpSession *session, QDict *resp)
+{
+    assert(resp != NULL);
+    assert(!qdict_haskey(resp, "error"));
+}
 
 /* test commands with no input and no return value */
 static void test_dispatch_cmd(void)
 {
     QmpSession session = { 0, };
-    QDict *resp, *req = qdict_new();
+    QDict *req = qdict_new();
 
-    qmp_session_init(&session, &qmp_commands);
+    qmp_session_init(&session, &qmp_commands, dispatch_cmd_return);
     qdict_put_str(req, "execute", "user_def_cmd");
-
-    resp = qmp_dispatch(&session, req);
-    assert(resp != NULL);
-    assert(!qdict_haskey(resp, "error"));
-
-    QDECREF(resp);
+    qmp_dispatch(&session, req);
     QDECREF(req);
     qmp_session_destroy(&session);
 }
 
+static void dispatch_cmd_failure_return(QmpSession *session, QDict *resp)
+{
+    assert(resp != NULL);
+    assert(qdict_haskey(resp, "error"));
+}
+
 /* test commands that return an error due to invalid parameters */
 static void test_dispatch_cmd_failure(void)
 {
     QmpSession session = { 0, };
     QDict *req = qdict_new();
-    QDict *resp, *args = qdict_new();
+    QDict *args = qdict_new();
 
-    qmp_session_init(&session, &qmp_commands);
+    qmp_session_init(&session, &qmp_commands, dispatch_cmd_failure_return);
     qdict_put_str(req, "execute", "user_def_cmd2");
-
-    resp = qmp_dispatch(&session, req);
-    assert(resp != NULL);
-    assert(qdict_haskey(resp, "error"));
-
-    QDECREF(resp);
+    qmp_dispatch(&session, req);
     QDECREF(req);
 
     /* check that with extra arguments it throws an error */
     req = qdict_new();
     qdict_put_int(args, "a", 66);
     qdict_put(req, "arguments", args);
-
     qdict_put_str(req, "execute", "user_def_cmd");
-
-    resp = qmp_dispatch(&session, req);
-    assert(resp != NULL);
-    assert(qdict_haskey(resp, "error"));
-
-    QDECREF(resp);
+    qmp_dispatch(&session, req);
     QDECREF(req);
     qmp_session_destroy(&session);
 }
 
+static QObject *dispatch_ret;
+
 static void test_dispatch_cmd_success_response(void)
 {
     QmpSession session = { 0, };
-    QDict *resp, *req = qdict_new();
+    QDict *req = qdict_new();
 
-    qmp_session_init(&session, &qmp_commands);
+    qmp_session_init(&session, &qmp_commands, (QmpDispatchReturn *)abort);
     qdict_put_str(req, "execute", "cmd-success-response");
-    resp = qmp_dispatch(&session, req);
-    assert(resp == NULL);
+    qmp_dispatch(&session, req);
     QDECREF(req);
     qmp_session_destroy(&session);
 }
 
+static void dispatch_return(QmpSession *session, QDict *resp)
+{
+    assert(resp && !qdict_haskey(resp, "error"));
+    dispatch_ret = qdict_get(resp, "return");
+    qobject_incref(dispatch_ret);
+}
 
 static QObject *test_qmp_dispatch(QDict *req)
 {
     QmpSession session = { 0, };
-    QDict *resp;
     QObject *ret;
 
-    qmp_session_init(&session, &qmp_commands);
-    resp = qmp_dispatch(&session, req);
-    assert(resp && !qdict_haskey(resp, "error"));
-    ret = qdict_get(resp, "return");
-    assert(ret);
-    qobject_incref(ret);
-    QDECREF(resp);
+    qmp_session_init(&session, &qmp_commands, dispatch_return);
+    qmp_dispatch(&session, req);
+    ret = dispatch_ret;
+    dispatch_ret = NULL;
     qmp_session_destroy(&session);
     return ret;
 }
@@ -290,21 +288,21 @@ static void test_dealloc_partial(void)
     qapi_free_UserDefTwo(ud2);
 }
 
+static void dispatch_return_id42(QmpSession *session, QDict *resp)
+{
+    assert(!qdict_haskey(resp, "error"));
+    assert(!strcmp(qdict_get_str(resp, "id"), "ID42"));
+}
+
 static void test_dispatch_cmd_id(void)
 {
     QmpSession session = { 0, };
-    QDict *resp, *req = qdict_new();
+    QDict *req = qdict_new();
 
-    qmp_session_init(&session, &qmp_commands);
+    qmp_session_init(&session, &qmp_commands, dispatch_return_id42);
     qdict_put_str(req, "execute", "user_def_cmd");
     qdict_put_str(req, "id", "ID42");
-
-    resp = qmp_dispatch(&session, req);
-    assert(resp != NULL);
-    assert(!qdict_haskey(resp, "error"));
-    assert(!strcmp(qdict_get_str(resp, "id"), "ID42"));
-
-    QDECREF(resp);
+    qmp_dispatch(&session, req);
     QDECREF(req);
     qmp_session_destroy(&session);
 }
-- 
2.17.0.rc1.1.g4c4f2b46a3

  parent reply	other threads:[~2018-03-26 15:10 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-26 15:08 [Qemu-devel] [PATCH v3 00/38] RFC: monitor: add asynchronous command type Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 01/38] HACK: add back OOB Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 02/38] qmp-shell: learn to send commands with quoted arguments Marc-André Lureau
2018-03-26 18:26   ` Eduardo Habkost
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 03/38] Revert "qmp: isolate responses into io thread" Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 04/38] monitor: no need to save need_resume Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 05/38] monitor: further simplify previous patch Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 06/38] monitor: no need to remove desc before replacing it Marc-André Lureau
2018-03-26 19:31   ` Eric Blake
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 07/38] json-parser: always set an error if return NULL Marc-André Lureau
2018-07-05 11:35   ` Markus Armbruster
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 08/38] json-lexer: make it safe to call multiple times Marc-André Lureau
2018-07-05 11:42   ` Markus Armbruster
2018-07-05 12:17     ` Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 09/38] json: remove useless return value from lexer/parser Marc-André Lureau
2018-07-05 11:49   ` Markus Armbruster
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 10/38] tests: add a few qemu-qmp tests Marc-André Lureau
2018-07-05 11:55   ` Markus Armbruster
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 11/38] tests: change /0.15/* tests to /qmp/* Marc-André Lureau
2018-07-05 11:56   ` Markus Armbruster
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 12/38] tests: add a qmp success-response test Marc-André Lureau
2018-07-05 11:59   ` Markus Armbruster
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 13/38] qga: process_event() simplification Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 14/38] monitor: simplify monitor_qmp_respond() Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 15/38] qmp: pass and return a QDict to qmp_dispatch() Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 16/38] qmp: move 'id' copy " Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 17/38] qmp: constify qmp_is_oob() Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 18/38] qmp: add QmpSession Marc-André Lureau
2018-03-26 15:08 ` Marc-André Lureau [this message]
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 20/38] QmpSession: add json parser and use it in qga Marc-André Lureau
2018-03-26 15:08 ` [Qemu-devel] [PATCH v3 21/38] QmpSession: add a dispatch callback Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 22/38] monitor: use QmpSession parsing and common dispatch code Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 23/38] QmpSession: introduce QmpReturn Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 24/38] qmp: remove qmp_build_error_object() Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 25/38] qmp: remove need for qobject_from_jsonf() Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 26/38] qmp: fold do_qmp_dispatch() in qmp_dispatch() Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 27/38] QmpSession: keep a queue of pending commands Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 28/38] QmpSession: return orderly Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 29/38] qmp: introduce asynchronous command type Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 30/38] scripts: learn 'async' qapi commands Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 31/38] qmp: add qmp_return_is_cancelled() Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 32/38] monitor: add qmp_return_get_monitor() Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 33/38] console: graphic_hw_update return true if async Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 34/38] console: add graphic_hw_update_done() Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 35/38] console: make screendump asynchronous Marc-André Lureau
2018-04-12 14:48   ` Dr. David Alan Gilbert
2018-04-19 16:05     ` Marc-André Lureau
2019-04-09 14:06     ` Marc-André Lureau
2019-04-09 14:06       ` Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 36/38] monitor: start making qmp_human_monitor_command() asynchronous Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 37/38] monitor: teach HMP about asynchronous commands Marc-André Lureau
2018-03-26 15:09 ` [Qemu-devel] [PATCH v3 38/38] hmp: call the asynchronous QMP screendump to fix outdated/glitches Marc-André Lureau
2018-03-26 17:24 ` [Qemu-devel] [PATCH v3 00/38] RFC: monitor: add asynchronous command type Dr. David Alan Gilbert
2018-03-26 17:30   ` Marc-André Lureau
2018-03-26 17:43 ` no-reply
2018-03-26 17:55 ` no-reply
2018-04-04  9:34 ` Stefan Hajnoczi
2018-04-04 10:01   ` Marc-André Lureau
2018-04-04 13:45 ` Eric Blake
2018-04-04 13:57   ` Marc-André Lureau
2018-07-05 13:05 ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180326150916.9602-20-marcandre.lureau@redhat.com \
    --to=marcandre.lureau@redhat.com \
    --cc=armbru@redhat.com \
    --cc=crosa@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=eblake@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=kraxel@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    --cc=quintela@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.