qapi: Split visit_end_struct() into pieces

As mentioned in previous patches, we want to call visit_end_struct()
functions unconditionally, so that visitors can release resources
tied up since the matching visit_start_struct() without also having
to worry about error priority if more than one error occurs.

Even though error_propagate() can be safely used to ignore a second
error during cleanup caused by a first error, it is simpler if the
cleanup cannot set an error.  So, split out the error checking
portion (basically, input visitors checking for unvisited keys) into
a new function visit_check_struct(), which can be safely skipped if
any earlier errors are encountered, and leave the cleanup portion
(which never fails, but must be called unconditionally if
visit_start_struct() succeeded) in visit_end_struct().

Generated code in qapi-visit.c has diffs resembling:

|@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v,
|         goto out_obj;
|     }
|     visit_type_ACPIOSTInfo_members(v, obj, &err);
|-    error_propagate(errp, err);
|-    err = NULL;
|+    if (err) {
|+        goto out_obj;
|+    }
|+    visit_check_struct(v, &err);
| out_obj:
|-    visit_end_struct(v, &err);
|+    visit_end_struct(v);
| out:

and in qapi-event.c:

@@ -47,7 +47,10 @@ void qapi_event_send_acpi_device_ost(ACP
|         goto out;
|     }
|     visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, &param, &err);
|-    visit_end_struct(v, err ? NULL : &err);
|+    if (!err) {
|+        visit_check_struct(v, &err);
|+    }
|+    visit_end_struct(v);
|     if (err) {
|         goto out;

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1461879932-9020-20-git-send-email-eblake@redhat.com>
[Conflict with a doc fixup resolved]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Eric Blake 2016-04-28 15:45:27 -06:00 committed by Markus Armbruster
parent 56a6f02b8c
commit 15c2f669e3
18 changed files with 128 additions and 73 deletions

View file

@ -43,8 +43,11 @@ struct Visitor
void (*start_struct)(Visitor *v, const char *name, void **obj,
size_t size, Error **errp);
/* Optional; intended for input visitors */
void (*check_struct)(Visitor *v, Error **errp);
/* Must be set to visit structs */
void (*end_struct)(Visitor *v, Error **errp);
void (*end_struct)(Visitor *v);
/* Must be set */
void (*start_list)(Visitor *v, const char *name, Error **errp);

View file

@ -192,10 +192,11 @@
* }
* outlist:
* visit_end_list(v);
* if (!err) {
* visit_check_struct(v, &err);
* }
* outobj:
* error_propagate(errp, err);
* err = NULL;
* visit_end_struct(v, &err);
* visit_end_struct(v);
* out:
* error_propagate(errp, err);
* ...clean up v...
@ -249,17 +250,27 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
size_t size, Error **errp);
/*
* Complete an object visit started earlier.
* Prepare for completing an object visit.
*
* @errp obeys typical error usage, and reports failures such as
* unparsed keys remaining in the input stream.
*
* Should be called prior to visit_end_struct() if all other
* intermediate visit steps were successful, to allow the visitor one
* last chance to report errors. May be skipped on a cleanup path,
* where there is no need to check for further errors.
*/
void visit_check_struct(Visitor *v, Error **errp);
/*
* Complete an object visit started earlier.
*
* Must be called after any successful use of visit_start_struct(),
* even if intermediate processing was skipped due to errors, to allow
* the backend to release any resources. Destroying the visitor early
* behaves as if this was implicitly called.
*/
void visit_end_struct(Visitor *v, Error **errp);
void visit_end_struct(Visitor *v);
/*** Visiting lists ***/