qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 27/36] scripts: learn 'async' qapi commands


From: marcandre . lureau
Subject: [Qemu-devel] [PATCH 27/36] scripts: learn 'async' qapi commands
Date: Fri, 25 Sep 2015 16:03:55 +0200

From: Marc-André Lureau <address@hidden>

Commands with the 'async' key will be registered as async type (see
previous commit), and will allow a synchronous (in command cb) or
asynchronous return (when ready, in idle etc).

Ex:
{ 'command': 'foo-async, 'data': {'arg': 'str'}, async: true }

Signed-off-by: Marc-André Lureau <address@hidden>
---
 qapi/introspect.json           |   2 +-
 scripts/qapi-commands.py       | 133 +++++++++++++++++++++++++++++++++--------
 scripts/qapi-introspect.py     |   7 ++-
 scripts/qapi.py                |  14 +++--
 tests/Makefile                 |   1 +
 tests/qapi-schema/async.err    |   0
 tests/qapi-schema/async.exit   |   1 +
 tests/qapi-schema/async.json   |   1 +
 tests/qapi-schema/async.out    |   5 ++
 tests/qapi-schema/test-qapi.py |   6 +-
 10 files changed, 133 insertions(+), 37 deletions(-)
 create mode 100644 tests/qapi-schema/async.err
 create mode 100644 tests/qapi-schema/async.exit
 create mode 100644 tests/qapi-schema/async.json
 create mode 100644 tests/qapi-schema/async.out

diff --git a/qapi/introspect.json b/qapi/introspect.json
index 5a3bd3e..5b878ff 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -259,7 +259,7 @@
 # Since: 2.5
 ##
 { 'struct': 'SchemaInfoCommand',
-  'data': { 'arg-type': 'str', 'ret-type': 'str' } }
+  'data': { 'arg-type': 'str', 'ret-type': 'str', 'async': 'bool' } }
 
 ##
 # @SchemaInfoEvent
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 4db1ae3..b0b922d 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -16,18 +16,30 @@ from qapi import *
 import re
 
 
-def gen_command_decl(name, arg_type, box, ret_type):
-    return mcgen('''
+def gen_command_decl(name, arg_type, box, ret_type, async):
+    if async:
+        extra = "QmpReturn *qret"
+    else:
+        extra = 'Error **errp'
+    if async:
+        return mcgen('''
+void qmp_%(name)s(%(params)s);
+void qmp_%(name)s_return(QmpReturn *qret%(c_type)s);
+''',
+                     c_type=(", " + ret_type.c_type() if ret_type else ""),
+                     name=c_name(name),
+                     params=gen_params(arg_type, box, extra))
+    else:
+
+        return mcgen('''
 %(c_type)s qmp_%(c_name)s(%(params)s);
 ''',
-                 c_type=(ret_type and ret_type.c_type()) or 'void',
-                 c_name=c_name(name),
-                 params=gen_params(arg_type, box, 'Error **errp'))
+                     c_type=(ret_type and ret_type.c_type()) or 'void',
+                     c_name=c_name(name),
+                     params=gen_params(arg_type, box, extra))
 
 
-def gen_call(name, arg_type, box, ret_type):
-    ret = ''
-
+def gen_argstr(arg_type, box):
     argstr = ''
     if box:
         argstr = 'arg, '
@@ -38,6 +50,13 @@ def gen_call(name, arg_type, box, ret_type):
                     argstr += 'has_%s, ' % c_name(memb.name)
                 argstr += '%s, ' % c_name(memb.name)
 
+    return argstr
+
+
+def gen_call(name, arg_type, box, ret_type):
+    ret = ''
+
+    argstr = gen_argstr(arg_type, box)
     lhs = ''
     if ret_type:
         lhs = 'retval = '
@@ -59,14 +78,57 @@ qmp_marshal_output_%(c_name)s(retval, ret, &err);
     return ret
 
 
-def gen_marshal_vars(arg_type, box, ret_type):
+def gen_async_call(name, arg_type, box):
+    argstr = gen_argstr(arg_type, box)
+
+    push_indent()
+    ret = mcgen('''
+
+qmp_%(c_name)s(%(args)sqret);
+''',
+                c_name=c_name(name), args=argstr)
+
+    pop_indent()
+    return ret
+
+
+def gen_async_return(name, arg_type, ret_type):
+    if ret_type:
+        return mcgen('''
+void qmp_%(c_name)s_return(QmpReturn *qret, %(ret_type)s ret_in)
+{
+    Error *err = NULL;
+    QObject *ret_out = NULL;
+
+    qmp_marshal_output_%(ret_c_name)s(ret_in, &ret_out, &err);
+
+    if (err) {
+        qmp_return_error(qret, err);
+    } else {
+        qmp_return(qret, ret_out);
+    }
+}
+''',
+                     c_name=c_name(name),
+                     ret_type=ret_type.c_type(), ret_c_name=ret_type.c_name())
+    else:
+        return mcgen('''
+void qmp_%(c_name)s_return(QmpReturn *qret)
+{
+    qmp_return(qret, QOBJECT(qdict_new()));
+}
+''',
+                     c_name=c_name(name))
+
+
+def gen_marshal_vars(arg_type, box, ret_type, async):
     ret = mcgen('''
     Error *err = NULL;
 ''')
 
     push_indent()
 
-    if ret_type:
+    if ret_type and not async:
         ret += mcgen('''
 %(c_type)s retval;
 ''',
@@ -178,29 +240,42 @@ out:
                  c_type=ret_type.c_type(), c_name=ret_type.c_name())
 
 
-def gen_marshal_proto(name):
-    return 'static void qmp_marshal_%s' % c_name(name) + \
-        '(QDict *args, QObject **ret, Error **errp)'
+def gen_marshal_proto(name, async):
+    if async:
+        tmpl = 'static void qmp_marshal_%s(QDict *args, QmpReturn *qret)'
+    else:
+        tmpl = 'static void qmp_marshal_%s(QDict *args, QObject **ret, Error 
**errp)'
+    return tmpl % c_name(name)
 
 
-def gen_marshal(name, arg_type, box, ret_type):
+def gen_marshal(name, arg_type, box, ret_type, async):
     ret = mcgen('''
 
 %(proto)s
 {
 ''',
-                proto=gen_marshal_proto(name))
+                proto=gen_marshal_proto(name, async))
 
-    ret += gen_marshal_vars(arg_type, box, ret_type)
+    ret += gen_marshal_vars(arg_type, box, ret_type, async)
     ret += gen_marshal_input_visit(arg_type, box)
-    ret += gen_call(name, arg_type, box, ret_type)
+    if async:
+        ret += gen_async_call(name, arg_type, box)
+    else:
+        ret += gen_call(name, arg_type, box, ret_type)
 
     if re.search('^ *goto out;', ret, re.MULTILINE):
         ret += mcgen('''
 
 out:
 ''')
-    ret += mcgen('''
+    if async:
+        ret += mcgen('''
+    if (err) {
+        qmp_return_error(qret, err);
+    }
+''')
+    else:
+        ret += mcgen('''
     error_propagate(errp, err);
 ''')
     ret += gen_marshal_input_visit(arg_type, box, dealloc=True)
@@ -210,16 +285,18 @@ out:
     return ret
 
 
-def gen_register_command(name, success_response):
+def gen_register_command(name, success_response, async):
     push_indent()
     options = 'QCO_NO_OPTIONS'
     if not success_response:
         options = 'QCO_NO_SUCCESS_RESP'
-
+    func = 'qmp_register_command'
+    if async:
+        func = 'qmp_register_async_command'
     ret = mcgen('''
-qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
+%(func)s("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
 ''',
-                name=name, c_name=c_name(name),
+                func=func, name=name, c_name=c_name(name),
                 opts=options)
     pop_indent()
     return ret
@@ -259,15 +336,18 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         self._visited_ret_types = None
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, box):
+                      gen, success_response, box, async):
         if not gen:
             return
-        self.decl += gen_command_decl(name, arg_type, box, ret_type)
+        self.decl += gen_command_decl(name, arg_type, box,
+                                      ret_type, async)
         if ret_type and ret_type not in self._visited_ret_types:
             self._visited_ret_types.add(ret_type)
             self.defn += gen_marshal_output(ret_type)
-        self.defn += gen_marshal(name, arg_type, box, ret_type)
-        self._regy += gen_register_command(name, success_response)
+        if async:
+            self.defn += gen_async_return(name, arg_type, ret_type)
+        self.defn += gen_marshal(name, arg_type, box, ret_type, async)
+        self._regy += gen_register_command(name, success_response, async)
 
 
 (input_file, output_dir, do_c, do_h, prefix, opts) = \
@@ -325,6 +405,7 @@ fdef.write(mcgen('''
 fdecl.write(mcgen('''
 #include "%(prefix)sqapi-types.h"
 #include "qapi/qmp/qdict.h"
+#include "qapi/qmp/dispatch.h"
 #include "qapi/error.h"
 
 ''',
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 8023f1b..6add6a0 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -29,6 +29,8 @@ def to_json(obj, level=0):
                               to_json(obj[key], level + 1))
                 for key in sorted(obj.keys())]
         ret = '{' + ', '.join(elts) + '}'
+    elif isinstance(obj, bool):
+        ret = 'true' if obj else 'false'
     else:
         assert False                # not implemented
     if level == 1:
@@ -154,12 +156,13 @@ const char %(c_name)s[] = %(c_string)s;
                                     for m in variants.variants]})
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, box):
+                      gen, success_response, box, async):
         arg_type = arg_type or self._schema.the_empty_object_type
         ret_type = ret_type or self._schema.the_empty_object_type
         self._gen_json(name, 'command',
                        {'arg-type': self._use_type(arg_type),
-                        'ret-type': self._use_type(ret_type)})
+                        'ret-type': self._use_type(ret_type),
+                        'async': async})
 
     def visit_event(self, name, info, arg_type, box):
         arg_type = arg_type or self._schema.the_empty_object_type
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 2a9b6e5..96901ab 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -822,7 +822,8 @@ def check_exprs(exprs):
             add_struct(expr, info)
         elif 'command' in expr:
             check_keys(expr_elem, 'command', [],
-                       ['data', 'returns', 'gen', 'success-response', 'box'])
+                       ['data', 'returns', 'gen', 'success-response', 'box',
+                        'async'])
             add_name(expr['command'], info, 'command')
         elif 'event' in expr:
             check_keys(expr_elem, 'event', [], ['data', 'box'])
@@ -911,7 +912,7 @@ class QAPISchemaVisitor(object):
         pass
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, box):
+                      gen, success_response, box, async):
         pass
 
     def visit_event(self, name, info, arg_type, box):
@@ -1194,7 +1195,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
 
 class QAPISchemaCommand(QAPISchemaEntity):
     def __init__(self, name, info, arg_type, ret_type, gen, success_response,
-                 box):
+                 box, async):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
         assert not ret_type or isinstance(ret_type, str)
@@ -1205,6 +1206,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
         self.gen = gen
         self.success_response = success_response
         self.box = box
+        self.async = async
 
     def check(self, schema):
         if self._arg_type_name:
@@ -1219,7 +1221,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
     def visit(self, visitor):
         visitor.visit_command(self.name, self._info,
                               self.arg_type, self.ret_type,
-                              self.gen, self.success_response, self.box)
+                              self.gen, self.success_response, self.box,
+                              self.async)
 
 
 class QAPISchemaEvent(QAPISchemaEntity):
@@ -1399,6 +1402,7 @@ class QAPISchema(object):
         data = expr.get('data')
         rets = expr.get('returns')
         gen = expr.get('gen', True)
+        async = expr.get('async', False)
         success_response = expr.get('success-response', True)
         box = expr.get('box', False)
         if isinstance(data, dict):
@@ -1410,7 +1414,7 @@ class QAPISchema(object):
             assert len(rets) == 1
             rets = self._make_array_type(rets[0])
         self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
-                                           success_response, box))
+                                           success_response, box, async))
 
     def _def_event(self, expr, info):
         name = expr['event']
diff --git a/tests/Makefile b/tests/Makefile
index 7276b54..e5cdbbe 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -245,6 +245,7 @@ qapi-schema += args-member-unknown.json
 qapi-schema += args-name-clash.json
 qapi-schema += args-union.json
 qapi-schema += args-unknown.json
+qapi-schema += async.json
 qapi-schema += bad-base.json
 qapi-schema += bad-data.json
 qapi-schema += bad-ident.json
diff --git a/tests/qapi-schema/async.err b/tests/qapi-schema/async.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/async.exit b/tests/qapi-schema/async.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/async.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/async.json b/tests/qapi-schema/async.json
new file mode 100644
index 0000000..3b719e3
--- /dev/null
+++ b/tests/qapi-schema/async.json
@@ -0,0 +1 @@
+{ 'command': 'screendump-async', 'data': {'filename': 'str'}, 'async': true }
diff --git a/tests/qapi-schema/async.out b/tests/qapi-schema/async.out
new file mode 100644
index 0000000..c83bea5
--- /dev/null
+++ b/tests/qapi-schema/async.out
@@ -0,0 +1,5 @@
+object :empty
+object :obj-screendump-async-arg
+    member filename: str optional=False
+command screendump-async :obj-screendump-async-arg -> None
+   gen=True success_response=True box=False async
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 559f0e8..f99a980 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -36,11 +36,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         self._print_variants(variants)
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, box):
+                      gen, success_response, box, async):
         print 'command %s %s -> %s' % \
             (name, arg_type and arg_type.name, ret_type and ret_type.name)
-        print '   gen=%s success_response=%s box=%s' % (gen, success_response,
-                                                        box)
+        print '   gen=%s success_response=%s box=%s%s' % \
+            (gen, success_response, box, ' async' if async else '')
 
     def visit_event(self, name, info, arg_type, box):
         print 'event %s %s' % (name, arg_type and arg_type.name)
-- 
2.4.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]