[libvirt] [PATCH 15/21] gendispatch: Be able to generate multi-return values

Martin Kletzander mkletzan at redhat.com
Thu Mar 10 04:54:04 UTC 2016


Let's call it modern_ret_as_list as opposed to single_ret_as_list.  The
latter was able to return list of things.  However the new, more modern,
version came and it is used since listAllDomains till nowadays in
ListServers.

Signed-off-by: Martin Kletzander <mkletzan at redhat.com>
---
 src/rpc/gendispatch.pl | 200 ++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 172 insertions(+), 28 deletions(-)

diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl
index 29f2c6ac033b..d6ce9b85e158 100755
--- a/src/rpc/gendispatch.pl
+++ b/src/rpc/gendispatch.pl
@@ -617,6 +617,9 @@ elsif ($mode eq "server") {
         my $single_ret_list_max_var = "undefined";
         my $single_ret_list_max_define = "undefined";
         my $multi_ret = 0;
+        my $modern_ret_as_list = 0;
+        my $modern_ret_struct_name = "undefined";
+        my $single_ret_list_error_msg_type = "undefined";

         if ($rettype ne "void" and
             scalar(@{$call->{ret_members}}) > 1) {
@@ -633,7 +636,16 @@ elsif ($mode eq "server") {

                         push(@ret_list, "memcpy(ret->$3, tmp.$3, sizeof(ret->$3));");
                     } elsif ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+);/) {
-                        push(@ret_list, "ret->$3 = tmp.$3;");
+                        if (!$modern_ret_as_list) {
+                            push(@ret_list, "ret->$3 = tmp.$3;");
+                        }
+                    } elsif ($ret_member =~ m/admin_nonnull_(server) (\S+)<(\S+)>;/) {
+                        $modern_ret_struct_name = $1;
+                        $single_ret_list_error_msg_type = $1;
+                        $single_ret_list_name = $2;
+                        $single_ret_list_max_define = $3;
+
+                        $modern_ret_as_list = 1;
                     } else {
                         die "unhandled type for multi-return-value: $ret_member";
                     }
@@ -817,27 +829,43 @@ elsif ($mode eq "server") {

         # select struct type for multi-return-value functions
         if ($multi_ret) {
-            if (!(defined $call->{ret_offset})) {
-                die "multi-return-value without insert@<offset> annotation: $call->{ret}";
-            }
+            if (defined $call->{ret_offset}) {
+                push_privconn(\@args_list);

-            push_privconn(\@args_list);
+                if ($modern_ret_as_list) {
+                    my $struct_name = name_to_TypeName($modern_ret_struct_name);

-            my $struct_name = $call->{ProcName};
-            $struct_name =~ s/Get//;
+                    if ($structprefix eq "admin") {
+                        $struct_name = "Net${struct_name}";
+                    }

-            splice(@args_list, $call->{ret_offset}, 0, ("&tmp"));
+                    push(@vars_list, "vir${struct_name}Ptr *result = NULL");
+                    push(@vars_list, "int nresults = 0");

-            if ($call->{ProcName} eq "DomainBlockStats" ||
-                $call->{ProcName} eq "DomainInterfaceStats") {
-                # SPECIAL: virDomainBlockStats and virDomainInterfaceStats
-                #          have a 'Struct' suffix on the actual struct name
-                #          and take the struct size as additional argument
-                $struct_name .= "Struct";
-                splice(@args_list, $call->{ret_offset} + 1, 0, ("sizeof(tmp)"));
-            }
+                    @args_list = grep {!/\bneed_results\b/} @args_list;

-            push(@vars_list, "vir$struct_name tmp");
+                    splice(@args_list, $call->{ret_offset}, 0,
+                           ("args->need_results ? &result : NULL"));
+                } else {
+                    my $struct_name = $call->{ProcName};
+                    $struct_name =~ s/Get//;
+
+                    splice(@args_list, $call->{ret_offset}, 0, ("&tmp"));
+
+                    if ($call->{ProcName} eq "DomainBlockStats" ||
+                        $call->{ProcName} eq "DomainInterfaceStats") {
+                        # SPECIAL: virDomainBlockStats and virDomainInterfaceStats
+                        #          have a 'Struct' suffix on the actual struct name
+                        #          and take the struct size as additional argument
+                        $struct_name .= "Struct";
+                        splice(@args_list, $call->{ret_offset} + 1, 0, ("sizeof(tmp)"));
+                    }
+
+                    push(@vars_list, "vir$struct_name tmp");
+                }
+            } else {
+                die "multi-return-value without insert@<offset> annotation: $call->{ret}";
+            }
         }

         if ($call->{streamflag} ne "none") {
@@ -868,6 +896,10 @@ elsif ($mode eq "server") {
         print "{\n";
         print "    int rv = -1;\n";

+        if ($modern_ret_as_list) {
+            print "    size_t i;\n";
+        }
+
         foreach my $var (@vars_list) {
             print "    $var;\n";
         }
@@ -974,9 +1006,17 @@ elsif ($mode eq "server") {
             print "        goto cleanup;\n";
             print "\n";
         } else {
-            print "    if (vir$call->{ProcName}(";
-            print join(', ', @args_list);
-            print ") < 0)\n";
+            if ($modern_ret_as_list) {
+                print "    if ((nresults = \n";
+                my $indln = "            $prefix$call->{ProcName}(";
+                print $indln;
+                print join(",\n" . ' ' x (length $indln), @args_list);
+                print ")) < 0)\n";
+            } else {
+                print "    if ($prefix$call->{ProcName}(";
+                print join(', ', @args_list);
+                print ") < 0)\n";
+            }
             print "        goto cleanup;\n";
             print "\n";
         }
@@ -995,6 +1035,29 @@ elsif ($mode eq "server") {
             print "\n";
         }

+        if ($modern_ret_as_list) {
+            print "    if (nresults > $single_ret_list_max_define) {\n";
+            print "        virReportError(VIR_ERR_INTERNAL_ERROR,\n";
+            print "                       _(\"Too many ${single_ret_list_error_msg_type}s '%d' for limit '%d'\"),\n";
+                print "                   nresults, $single_ret_list_max_define);\n";
+            print "        goto cleanup;\n";
+            print "    }\n";
+            print "\n";
+            print "    if (result && nresults) {\n";
+            print "        if (VIR_ALLOC_N(ret->$single_ret_list_name.${single_ret_list_name}_val, nresults) < 0)\n";
+            print "            goto cleanup;\n";
+            print "\n";
+            print "        ret->$single_ret_list_name.${single_ret_list_name}_len = nresults;\n";
+            print "        for (i = 0; i < nresults; i++)\n";
+            print "            make_nonnull_$modern_ret_struct_name(ret->$single_ret_list_name.${single_ret_list_name}_val + i, result[i]);\n";
+            print "    } else {\n";
+            print "        ret->$single_ret_list_name.${single_ret_list_name}_len = 0;\n";
+            print "        ret->$single_ret_list_name.${single_ret_list_name}_val = NULL;\n";
+            print "    }\n";
+            print "\n";
+            print "    ret->ret = nresults;\n";
+        }
+
         if (@prepare_ret_list) {
             print "    ";
             print join("\n    ", @prepare_ret_list);
@@ -1030,6 +1093,14 @@ elsif ($mode eq "server") {
             print "\n";
         }

+        if ($modern_ret_as_list) {
+            print "    if (result) {\n";
+            print "        for (i = 0; i < nresults; i++)\n";
+            print "            virObjectUnref(result[i]);\n";
+            print "    }\n";
+            print "    VIR_FREE(result);\n";
+        }
+
         print "    return rv;\n";
         print "}\n\n\n\n";
     }
@@ -1277,6 +1348,9 @@ elsif ($mode eq "client") {
         my $single_ret_list_max_define = "undefined";
         my $single_ret_cleanup = 0;
         my $multi_ret = 0;
+        my $modern_ret_as_list = 0;
+        my $modern_ret_struct_name = 0;
+        my $modern_ret_var_type = "undefined";

         if ($rettype ne "void" and
             scalar(@{$call->{ret_members}}) > 1) {
@@ -1296,6 +1370,20 @@ elsif ($mode eq "client") {
                         }

                         push(@ret_list, "memcpy(result->$3, ret.$3, sizeof(result->$3));");
+                    } elsif ($ret_member =~ m/admin_nonnull_(server) (\S+)<(\S+)>;/) {
+                        my $proc_name = name_to_TypeName($1);
+
+                        if ($structprefix eq "admin") {
+                            $modern_ret_var_type = "virAdm${proc_name}Ptr";
+                        } else {
+                            $modern_ret_var_type = "vir${proc_name}Ptr";
+                        }
+
+                        $modern_ret_struct_name = $1;
+                        $single_ret_list_name = $2;
+                        $single_ret_list_max_var = $3;
+
+                        $modern_ret_as_list = 1;
                     } elsif ($ret_member =~ m/<\S+>;/ or $ret_member =~ m/\[\S+\];/) {
                         # just make all other array types fail
                         die "unhandled type for multi-return-value for " .
@@ -1305,7 +1393,7 @@ elsif ($mode eq "client") {
                             my $sign = ""; $sign = "U" if ($1);

                             push(@ret_list, "HYPER_TO_${sign}LONG(result->$3, ret.$3);");
-                        } else {
+                        } elsif (!$modern_ret_as_list) {
                             push(@ret_list, "result->$3 = ret.$3;");
                         }
                     } else {
@@ -1434,16 +1522,40 @@ elsif ($mode eq "client") {
             }
         }

+        if ($modern_ret_as_list) {
+            # clear arguments and setters we don't want in this code
+            @args_list = grep {!/\bneed_results\b/} @args_list;
+            @setters_list = grep {!/\bneed_results\b/} @setters_list;
+
+            push(@vars_list, "${modern_ret_var_type} *tmp_results = NULL");
+            push(@setters_list, "args.need_results = !!result;");
+
+            $single_ret_var = "int rv = -1";
+            $single_ret_type = "int";
+        }
+
         # select struct type for multi-return-value functions
         if ($multi_ret) {
+            my $struct_name = "undefined";
+
             if (!(defined $call->{ret_offset})) {
                 die "multi-return-value without insert@<offset> annotation: $call->{ret}";
             }

-            my $struct_name = $call->{ProcName};
-            $struct_name =~ s/Get//;
+            if ($modern_ret_as_list) {
+                $struct_name = name_to_TypeName($modern_ret_struct_name);
+
+                $struct_name .= "Ptr **";
+                if ($structprefix eq "admin") {
+                    $struct_name = "Adm${struct_name}";
+                }
+            } else {
+                $struct_name = $call->{ProcName};

-            splice(@args_list, $call->{ret_offset}, 0, ("vir${struct_name}Ptr result"));
+                $struct_name =~ s/Get//;
+                $struct_name = "${struct_name}Ptr "
+            }
+            splice(@args_list, $call->{ret_offset}, 0, ("vir$struct_name result"));
         }

         if ($call->{streamflag} ne "none") {
@@ -1482,7 +1594,8 @@ elsif ($mode eq "client") {
             print "    $var;\n";
         }

-        if ($single_ret_as_list) {
+        if ($single_ret_as_list or
+            $modern_ret_as_list) {
             print "    size_t i;\n";
         }

@@ -1595,7 +1708,8 @@ elsif ($mode eq "client") {
         print "    }\n";
         print "\n";

-        if ($single_ret_as_list) {
+        if ($single_ret_as_list or
+            $modern_ret_as_list) {
             print "    if (ret.$single_ret_list_name.${single_ret_list_name}_len > $single_ret_list_max_var) {\n";
             print "        virReportError(VIR_ERR_RPC,\n";
             print "                       _(\"too many remote ${single_ret_list_error_msg_type}s: %d > %d\"),\n";
@@ -1603,6 +1717,9 @@ elsif ($mode eq "client") {
             print "        goto cleanup;\n";
             print "    }\n";
             print "\n";
+        }
+
+        if ($single_ret_as_list) {
             print "    /* This call is caller-frees (although that isn't clear from\n";
             print "     * the documentation).  However xdr_free will free up both the\n";
             print "     * names and the list of pointers, so we have to VIR_STRDUP the\n";
@@ -1618,8 +1735,23 @@ elsif ($mode eq "client") {
             print "        }\n";
             print "    }\n";
             print "\n";
+        } elsif ($modern_ret_as_list) {
+            print "    if (result) {\n";
+            print "        if (VIR_ALLOC_N(tmp_results, ret.$single_ret_list_name.${single_ret_list_name}_len + 1) < 0)\n";
+            print "            goto cleanup;\n";
+            print "\n";
+            print "        for (i = 0; i < ret.$single_ret_list_name.${single_ret_list_name}_len; i++) {\n";
+            print "            tmp_results[i] = get_nonnull_$modern_ret_struct_name($priv_src, ret.$single_ret_list_name.${single_ret_list_name}_val[i]);\n";
+            print "            if (!tmp_results[i])\n";
+            print "                goto cleanup;\n";
+            print "        }\n";
+            print "        *result = tmp_results;\n";
+            print "        tmp_results = NULL;\n";
+            print "    }\n";
+            print "\n";
         }

+
         if (@ret_list2) {
             print "    ";
             print join("\n    ", @ret_list2);
@@ -1641,12 +1773,24 @@ elsif ($mode eq "client") {
         }

         if ($multi_ret or !@ret_list) {
-            print "    rv = 0;\n";
+            if ($modern_ret_as_list) {
+                print "    rv = ret.ret;\n";
+            } else {
+                print "    rv = 0;\n";
+            }
         }

-        if ($single_ret_as_list or $single_ret_cleanup) {
+        if ($single_ret_as_list or $single_ret_cleanup or $modern_ret_as_list) {
             print "\n";
             print "cleanup:\n";
+            if ($modern_ret_as_list) {
+                print "    if (tmp_results) {\n";
+                print "        for (i = 0; i < ret.$single_ret_list_name.${single_ret_list_name}_len; i++)\n";
+                print "            virObjectUnref(tmp_results[i]);\n";
+                print "        VIR_FREE(tmp_results);\n";
+                print "    }\n";
+                print "\n";
+            }
             print "    xdr_free((xdrproc_t)xdr_$call->{ret}, (char *)&ret);\n";
         }

-- 
2.7.2




More information about the libvir-list mailing list