From: "Marc-André Lureau" <marcandre.lureau@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Marc-André Lureau" <marcandre.lureau@redhat.com>,
armbru@redhat.com, kraxel@redhat.com
Subject: [PATCH v6 11/25] QmpSession: return orderly
Date: Fri, 8 Nov 2019 19:01:09 +0400 [thread overview]
Message-ID: <20191108150123.12213-12-marcandre.lureau@redhat.com> (raw)
In-Reply-To: <20191108150123.12213-1-marcandre.lureau@redhat.com>
QEMU will gain support for asynchronous commands, and may thus finish
commands in various order. However, the clients expect replies in
order. Let's enforce ordering of replies in QmpReturn: starting from
the older command, process each pending QmpReturn, and return until
reaching one that is unfinished.
Or if the command is OOB, it should return immediately.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
include/qapi/qmp/dispatch.h | 2 ++
qapi/qmp-dispatch.c | 61 ++++++++++++++++++++++++++++++-------
tests/test-qmp-cmds.c | 33 ++++++++++++++++++++
3 files changed, 85 insertions(+), 11 deletions(-)
diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index 7c9de9780d..92d6fd1afb 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -55,6 +55,8 @@ struct QmpSession {
struct QmpReturn {
QmpSession *session;
QDict *rsp;
+ bool oob;
+ bool finished;
QTAILQ_ENTRY(QmpReturn) entry;
};
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index aed5c91057..3eb7e4b610 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -25,6 +25,7 @@ QmpReturn *qmp_return_new(QmpSession *session, const QObject *request)
const QDict *req = qobject_to(QDict, request);
QObject *id = req ? qdict_get(req, "id") : NULL;
+ qret->oob = req ? qmp_is_oob(req) : false;
qret->session = session;
qret->rsp = qdict_new();
if (id) {
@@ -39,6 +40,15 @@ QmpReturn *qmp_return_new(QmpSession *session, const QObject *request)
return qret;
}
+static void qmp_return_free_with_lock(QmpReturn *qret)
+{
+ if (qret->session) {
+ QTAILQ_REMOVE(&qret->session->pending, qret, entry);
+ }
+ qobject_unref(qret->rsp);
+ g_free(qret);
+}
+
void qmp_return_free(QmpReturn *qret)
{
QmpSession *session = qret->session;
@@ -46,21 +56,53 @@ void qmp_return_free(QmpReturn *qret)
if (session) {
qemu_mutex_lock(&session->pending_lock);
}
- QTAILQ_REMOVE(&session->pending, qret, entry);
+
+ qmp_return_free_with_lock(qret);
+
if (session) {
qemu_mutex_unlock(&session->pending_lock);
}
- qobject_unref(qret->rsp);
- g_free(qret);
+}
+
+static void qmp_return_orderly(QmpReturn *qret)
+{
+ QmpSession *session = qret->session;
+ QmpReturn *ret, *next;
+
+ if (!session) {
+ /* the session was destroyed before return, discard */
+ qmp_return_free(qret);
+ return;
+ }
+ if (qret->oob) {
+ session->return_cb(session, qret->rsp);
+ qmp_return_free(qret);
+ return;
+ }
+
+ qret->finished = true;
+
+ qemu_mutex_lock(&session->pending_lock);
+ /*
+ * Process the list of pending and call return_cb until reaching
+ * an unfinished.
+ */
+ QTAILQ_FOREACH_SAFE(ret, &session->pending, entry, next) {
+ if (!ret->finished) {
+ break;
+ }
+ session->return_cb(session, ret->rsp);
+ ret->session = session;
+ qmp_return_free_with_lock(ret);
+ }
+
+ qemu_mutex_unlock(&session->pending_lock);
}
void qmp_return(QmpReturn *qret, QObject *rsp)
{
qdict_put_obj(qret->rsp, "return", rsp ?: QOBJECT(qdict_new()));
- if (qret->session) {
- qret->session->return_cb(qret->session, qret->rsp);
- }
- qmp_return_free(qret);
+ qmp_return_orderly(qret);
}
void qmp_return_error(QmpReturn *qret, Error *err)
@@ -70,10 +112,7 @@ void qmp_return_error(QmpReturn *qret, Error *err)
qdict_put_str(qdict, "desc", error_get_pretty(err));
qdict_put_obj(qret->rsp, "error", QOBJECT(qdict));
error_free(err);
- if (qret->session) {
- qret->session->return_cb(qret->session, qret->rsp);
- }
- qmp_return_free(qret);
+ qmp_return_orderly(qret);
}
static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c
index d9623d10b6..213df7e53a 100644
--- a/tests/test-qmp-cmds.c
+++ b/tests/test-qmp-cmds.c
@@ -361,6 +361,38 @@ static void test_dealloc_partial(void)
qapi_free_UserDefTwo(ud2);
}
+typedef struct QmpReturnOrderly {
+ QmpSession session;
+ int returns;
+} QmpReturnOrderly;
+
+static void dispatch_return_orderly(QmpSession *session, QDict *resp)
+{
+ QmpReturnOrderly *o = container_of(session, QmpReturnOrderly, session);
+
+ o->returns++;
+}
+
+static void test_qmp_return_orderly(void)
+{
+ QDict *dict = qdict_new();
+ QmpReturnOrderly o = { { 0 }, };
+ QmpReturn *r1, *r2, *r3;
+
+ qmp_session_init(&o.session, &qmp_commands, NULL, dispatch_return_orderly);
+ r1 = qmp_return_new(&o.session, NULL);
+ qdict_put_str(dict, "exec-oob", "test");
+ r2 = qmp_return_new(&o.session, QOBJECT(dict));
+ r3 = qmp_return_new(&o.session, NULL);
+ qmp_return(r3, NULL);
+ g_assert_cmpint(o.returns, ==, 0);
+ qmp_return(r2, NULL);
+ g_assert_cmpint(o.returns, ==, 1);
+ qmp_return(r1, NULL);
+ g_assert_cmpint(o.returns, ==, 3);
+ qmp_session_destroy(&o.session);
+ qobject_unref(dict);
+}
int main(int argc, char **argv)
{
@@ -374,6 +406,7 @@ int main(int argc, char **argv)
test_dispatch_cmd_success_response);
g_test_add_func("/qmp/dealloc_types", test_dealloc_types);
g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial);
+ g_test_add_func("/qmp/return_orderly", test_qmp_return_orderly);
test_qmp_init_marshal(&qmp_commands);
g_test_run();
--
2.24.0
next prev parent reply other threads:[~2019-11-08 15:21 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-11-08 15:00 [PATCH v6 00/25] monitor: add asynchronous command type Marc-André Lureau
2019-11-08 15:00 ` [PATCH v6 01/25] qmp: constify QmpCommand and list Marc-André Lureau
2019-11-08 16:50 ` Damien Hedde
2020-02-17 11:47 ` Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 02/25] json-lexer: make it safe to call destroy multiple times Marc-André Lureau
2019-11-08 16:04 ` Damien Hedde
2019-11-08 15:01 ` [PATCH v6 03/25] qmp: add QmpSession Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 04/25] QmpSession: add a return callback Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 05/25] QmpSession: add json parser and use it in qga Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 06/25] monitor: use qmp session to parse json feed Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 07/25] qga: simplify dispatch_return_cb Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 08/25] QmpSession: introduce QmpReturn Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 09/25] qmp: simplify qmp_return_error() Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 10/25] QmpSession: keep a queue of pending commands Marc-André Lureau
2019-11-08 15:01 ` Marc-André Lureau [this message]
2019-11-08 15:01 ` [PATCH v6 12/25] qmp: introduce asynchronous command type Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 13/25] scripts: learn 'async' qapi commands Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 14/25] qmp: add qmp_return_is_cancelled() Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 15/25] console: add graphic_hw_update_done() Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 16/25] ppm-save: pass opened fd Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 17/25] ui: add pixman image g_autoptr support Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 18/25] object: add " Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 19/25] screendump: replace FILE with QIOChannel and fix close()/qemu_close() Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 20/25] osdep: add qemu_unlink() Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 21/25] screendump: use qemu_unlink() Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 22/25] console: make screendump asynchronous Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 23/25] monitor: start making qmp_human_monitor_command() asynchronous Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 24/25] monitor: teach HMP about asynchronous commands Marc-André Lureau
2019-11-08 15:01 ` [PATCH v6 25/25] hmp: call the asynchronous QMP screendump to fix outdated/glitches Marc-André Lureau
2019-12-13 16:03 ` [PATCH v6 00/25] monitor: add asynchronous command type Kevin Wolf
2019-12-13 16:28 ` Marc-André Lureau
2019-12-16 12:07 ` Kevin Wolf
2020-01-06 18:21 ` Marc-André Lureau
2020-01-07 5:17 ` Kevin Wolf
2020-01-07 12:11 ` Marc-André Lureau
2020-01-13 15:58 ` Markus Armbruster
2020-01-13 16:54 ` Kevin Wolf
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=20191108150123.12213-12-marcandre.lureau@redhat.com \
--to=marcandre.lureau@redhat.com \
--cc=armbru@redhat.com \
--cc=kraxel@redhat.com \
--cc=qemu-devel@nongnu.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).