@@ -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
@@ -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"
''',
@@ -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
@@ -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']
@@ -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
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
+0
new file mode 100644
@@ -0,0 +1 @@
+{ 'command': 'screendump-async', 'data': {'filename': 'str'}, 'async': true }
new file mode 100644
@@ -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
@@ -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)