afb-stub-ws: Enforce asynchronous describe 57/23157/1
authorJose Bollo <jose.bollo@iot.bzh>
Mon, 4 Nov 2019 16:57:23 +0000 (17:57 +0100)
committerJosé Bollo <jose.bollo@iot.bzh>
Fri, 29 Nov 2019 11:48:17 +0000 (12:48 +0100)
Because remote apis describe themselves asynchronousely,
it is better to have asynchronous describe api.

Bug-AGL: SPEC-2968

Change-Id: I52b4dab697f229ad01ea2b73d6b8dee22d507912
Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
src/afb-api.h
src/afb-apiset.c
src/afb-apiset.h
src/afb-export.c
src/afb-monitor.c
src/afb-stub-ws.c

index d55970c..1f2a4a9 100644 (file)
@@ -30,7 +30,7 @@ struct afb_api_itf
 #endif
        int (*get_logmask)(void *closure);
        void (*set_logmask)(void *closure, int level);
-       struct json_object *(*describe)(void *closure);
+       void (*describe)(void *closure, void (*describecb)(void *, struct json_object *), void *clocb);
        void (*unref)(void *closure);
 };
 
index 0a8b339..9fddd46 100644 (file)
@@ -28,6 +28,7 @@
 #include "afb-apiset.h"
 #include "afb-context.h"
 #include "afb-xreq.h"
+#include "jobs.h"
 
 #define INCR 8         /* CAUTION: must be a power of 2 */
 
@@ -941,20 +942,25 @@ int afb_apiset_get_logmask(struct afb_apiset *set, const char *name)
        return i->api.itf->get_logmask(i->api.closure);
 }
 
-/**
- * Get the description of the API of 'name'
- * @param set the api set
- * @param name the api whose description is required
- * @return the description or NULL
- */
-struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name)
+void afb_apiset_describe(struct afb_apiset *set, const char *name, void (*describecb)(void *, struct json_object *), void *closure)
 {
        const struct api_desc *i;
-
-       i = name ? searchrec(set, name) : NULL;
-       return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
+       struct json_object *r;
+
+       r = NULL;
+       if (name) {
+               i = searchrec(set, name);
+               if (i) {
+                       if (i->api.itf->describe) {
+                               i->api.itf->describe(i->api.closure, describecb, closure);
+                               return;
+                       }
+               }
+       }
+       describecb(closure, r);
 }
 
+
 struct get_names {
        union  {
                struct {
index d1f968b..f21277a 100644 (file)
@@ -60,7 +60,7 @@ extern void afb_apiset_update_hooks(struct afb_apiset *set, const char *name);
 extern void afb_apiset_set_logmask(struct afb_apiset *set, const char *name, int mask);
 extern int afb_apiset_get_logmask(struct afb_apiset *set, const char *name);
 
-extern struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name);
+extern void afb_apiset_describe(struct afb_apiset *set, const char *name, void (*describecb)(void *, struct json_object *), void *closure);
 
 extern const char **afb_apiset_get_names(struct afb_apiset *set, int rec, int type);
 extern void afb_apiset_enum(
index a9643f5..f96f8f8 100644 (file)
@@ -1823,7 +1823,7 @@ static void api_call_cb(void *closure, struct afb_xreq *xreq)
        }
 }
 
-static struct json_object *api_describe_cb(void *closure)
+static void api_describe_cb(void *closure, void (*describecb)(void *, struct json_object *), void *clocb)
 {
        struct afb_export *export = closure;
        struct json_object *result;
@@ -1846,7 +1846,7 @@ static struct json_object *api_describe_cb(void *closure)
                result = NULL;
                break;
        }
-       return result;
+       describecb(clocb, result);
 }
 
 static int api_service_start_cb(void *closure)
index e32c85a..a53f75a 100644 (file)
 
 #include "monitor-api.inc"
 
+
+static const char _verbosity_[] = "verbosity";
+static const char _apis_[] = "apis";
+
+static const char _debug_[] = "debug";
+static const char _info_[] = "info";
+static const char _notice_[] = "notice";
+static const char _warning_[] = "warning";
+static const char _error_[] = "error";
+
+
 static struct afb_apiset *target_set;
 
 int afb_monitor_init(struct afb_apiset *declare_set, struct afb_apiset *call_set)
@@ -48,12 +59,6 @@ int afb_monitor_init(struct afb_apiset *declare_set, struct afb_apiset *call_set
 **** Monitoring verbosity
 ******************************************************************************/
 
-static const char _debug_[] = "debug";
-static const char _info_[] = "info";
-static const char _notice_[] = "notice";
-static const char _warning_[] = "warning";
-static const char _error_[] = "error";
-
 /**
  * Translate verbosity indication to an integer value.
  * @param v the verbosity indication
@@ -231,72 +236,141 @@ static struct json_object *get_verbosity(struct json_object *spec)
 }
 
 /******************************************************************************
-**** Monitoring apis
+**** Manage namelist of api names
 ******************************************************************************/
 
-/**
- * get apis accordling to specification in 'spec'
- * @param resu the json object to build
- * @param spec specification of the verbosity to set
- */
-static void get_one_api(struct json_object *resu, const char *name, struct json_object *spec)
+struct namelist {
+       struct namelist *next;
+       json_object *data;
+       char name[];
+};
+
+static struct namelist *reverse_namelist(struct namelist *head)
 {
-       struct json_object *o;
+       struct namelist *previous, *next;
+
+       previous = NULL;
+       while(head) {
+               next = head->next;
+               head->next = previous;
+               previous = head;
+               head = next;
+       }
+       return previous;
+}
 
-       o = afb_apiset_describe(target_set, name);
-       if (o || afb_apiset_lookup(target_set, name, 1))
-               json_object_object_add(resu, name, o);
+static void add_one_name_to_namelist(struct namelist **head, const char *name, struct json_object *data)
+{
+       size_t length = strlen(name) + 1;
+       struct namelist *item = malloc(length + sizeof *item);
+       if (!item)
+               ERROR("out of memory");
+       else {
+               item->next = *head;
+               item->data = data;
+               memcpy(item->name, name, length);
+               *head = item;
+       }
 }
 
-/**
- * callback for getting verbosity of all apis
- * @param set the apiset
- * @param the name of the api to set
- * @param closure the json object to build
- */
-static void get_apis_of_all_cb(void *closure, struct afb_apiset *set, const char *name, int isalias)
+static void get_apis_namelist_of_all_cb(void *closure, struct afb_apiset *set, const char *name, int isalias)
 {
-       struct json_object *resu = closure;
-       get_one_api(resu, name, NULL);
+       struct namelist **head = closure;
+       add_one_name_to_namelist(head, name, NULL);
 }
 
 /**
- * get apis accordling to specification in 'spec'
- * @param resu the json object to build
- * @param spec specification of the verbosity to set
+ * get apis names as a list accordling to specification in 'spec'
+ * @param spec specification of the apis to get
  */
-static struct json_object *get_apis(struct json_object *spec)
+static struct namelist *get_apis_namelist(struct json_object *spec)
 {
        int i, n;
-       struct json_object *resu;
        struct json_object_iterator it, end;
+       struct namelist *head;
 
-       resu = json_object_new_object();
+       head = NULL;
        if (json_object_is_type(spec, json_type_object)) {
                it = json_object_iter_begin(spec);
                end = json_object_iter_end(spec);
                while (!json_object_iter_equal(&it, &end)) {
-                       get_one_api(resu, json_object_iter_peek_name(&it), json_object_iter_peek_value(&it));
+                       add_one_name_to_namelist(&head,
+                                                json_object_iter_peek_name(&it),
+                                                json_object_iter_peek_value(&it));
                        json_object_iter_next(&it);
                }
        } else if (json_object_is_type(spec, json_type_array)) {
                n = (int)json_object_array_length(spec);
                for (i = 0 ; i < n ; i++)
-                       get_one_api(resu, json_object_get_string(json_object_array_get_idx(spec, i)), NULL);
+                       add_one_name_to_namelist(&head,
+                                                json_object_get_string(
+                                                        json_object_array_get_idx(spec, i)),
+                                                NULL);
        } else if (json_object_is_type(spec, json_type_string)) {
-               get_one_api(resu, json_object_get_string(spec), NULL);
+               add_one_name_to_namelist(&head, json_object_get_string(spec), NULL);
        } else if (json_object_get_boolean(spec)) {
-               afb_apiset_enum(target_set, 1, get_apis_of_all_cb, resu);
+               afb_apiset_enum(target_set, 1, get_apis_namelist_of_all_cb, &head);
        }
-       return resu;
+       return reverse_namelist(head);
 }
 
 /******************************************************************************
-**** Implementation monitoring verbs
+**** Monitoring apis
 ******************************************************************************/
 
-static const char _verbosity_[] = "verbosity";
-static const char _apis_[] = "apis";
+struct desc_apis {
+       struct namelist *names;
+       struct json_object *resu;
+       struct json_object *apis;
+       afb_req_t req;
+};
+
+static void describe_first_api(struct desc_apis *desc);
+
+static void on_api_description(void *closure, struct json_object *apidesc)
+{
+       struct desc_apis *desc = closure;
+       struct namelist *head = desc->names;
+
+       if (apidesc || afb_apiset_lookup(target_set, head->name, 1))
+               json_object_object_add(desc->apis, head->name, apidesc);
+       desc->names = head->next;
+       free(head);
+       describe_first_api(desc);
+}
+
+static void describe_first_api(struct desc_apis *desc)
+{
+       struct namelist *head = desc->names;
+       if (head)
+               afb_apiset_describe(target_set, head->name, on_api_description, desc);
+       else {
+               afb_req_success(desc->req, desc->resu, NULL);
+               afb_req_unref(desc->req);
+               free(desc);
+       }
+}
+
+static void describe_apis(afb_req_t req, struct json_object *resu, struct json_object *spec)
+{
+       struct desc_apis *desc;
+
+       desc = malloc(sizeof *desc);
+       if (!desc)
+               afb_req_fail(req, "out-of-memory", NULL);
+       else {
+               desc->req = afb_req_addref(req);
+               desc->resu = resu;
+               desc->apis = json_object_new_object();
+               json_object_object_add(desc->resu, _apis_, desc->apis);
+               desc->names = get_apis_namelist(spec);
+               describe_first_api(desc);
+       }
+}
+
+/******************************************************************************
+**** Implementation monitoring verbs
+******************************************************************************/
 
 static void f_get(afb_req_t req)
 {
@@ -305,13 +379,23 @@ static void f_get(afb_req_t req)
        struct json_object *verbosity = NULL;
 
        wrap_json_unpack(afb_req_json(req), "{s?:o,s?:o}", _verbosity_, &verbosity, _apis_, &apis);
-       if (verbosity)
-               verbosity = get_verbosity(verbosity);
-       if (apis)
-               apis = get_apis(apis);
-
-       wrap_json_pack(&r, "{s:o*,s:o*}", _verbosity_, verbosity, _apis_, apis);
-       afb_req_success(req, r, NULL);
+       if (!verbosity && !apis)
+               afb_req_success(req, NULL, NULL);
+       else {
+               r = json_object_new_object();
+               if (!r)
+                       afb_req_fail(req, "out-of-memory", NULL);
+               else {
+                       if (verbosity) {
+                               verbosity = get_verbosity(verbosity);
+                               json_object_object_add(r, _verbosity_, verbosity);
+                       }
+                       if (!apis)
+                               afb_req_success(req, r, NULL);
+                       else
+                               describe_apis(req, r, apis);
+               }
+       }
 }
 
 static void f_set(afb_req_t req)
index 2a48745..9bd8ed6 100644 (file)
@@ -70,16 +70,6 @@ struct client_event
        int refcount;                   /**< a reference count */
 };
 
-/**
- * structure for recording describe requests on the client side
- */
-struct client_describe
-{
-       struct afb_stub_ws *stubws;     /**< the stub */
-       struct jobloop *jobloop;        /**< the jobloop to leave */
-       struct json_object *result;     /**< result */
-};
-
 /**
  * structure for jobs of describing
  */
@@ -280,38 +270,17 @@ static void client_api_call_cb(void * closure, struct afb_xreq *xreq)
        }
 }
 
-static void client_on_description_cb(void *closure, struct json_object *data)
-{
-       struct client_describe *desc = closure;
-
-       desc->result = data;
-       jobs_leave(desc->jobloop);
-}
-
-static void client_send_describe_cb(int signum, void *closure, struct jobloop *jobloop)
-{
-       struct client_describe *desc = closure;
-       struct afb_proto_ws *proto;
-
-       proto = client_get_proto(desc->stubws);
-       if (signum || proto == NULL)
-               jobs_leave(jobloop);
-       else {
-               desc->jobloop = jobloop;
-               afb_proto_ws_client_describe(proto, client_on_description_cb, desc);
-       }
-}
-
 /* get the description */
-static struct json_object *client_api_describe_cb(void * closure)
+static void client_api_describe_cb(void * closure, void (*describecb)(void *, struct json_object *), void *clocb)
 {
-       struct client_describe desc;
+       struct afb_stub_ws *stubws = closure;
+       struct afb_proto_ws *proto;
 
-       /* synchronous job: send the request and wait its result */
-       desc.stubws = closure;
-       desc.result = NULL;
-       jobs_enter(NULL, 0, client_send_describe_cb, &desc);
-       return desc.result;
+       proto = client_get_proto(stubws);
+       if (proto)
+               afb_proto_ws_client_describe(proto, describecb, clocb);
+       else
+               describecb(clocb, NULL);
 }
 
 /******************* server part: manage events **********************************/
@@ -546,42 +515,19 @@ out_of_memory:
        afb_proto_ws_call_unref(call);
 }
 
-static void server_describe_cb(int signum, void *closure)
+static void server_on_description_cb(void *closure, struct json_object *description)
 {
-       struct json_object *obj;
-       struct server_describe *desc = closure;
-
-       /* get the description if possible */
-       obj = !signum ? afb_apiset_describe(desc->stubws->apiset, desc->stubws->apiname) : NULL;
-
-       /* send it */
-       afb_proto_ws_describe_put(desc->describe, obj);
-       json_object_put(obj);
-       afb_stub_ws_unref(desc->stubws);
+       struct afb_proto_ws_describe *describe = closure;
+       afb_proto_ws_describe_put(describe, description);
+       json_object_put(description);
 }
 
-static void server_describe_job(int signum, void *closure)
-{
-       server_describe_cb(signum, closure);
-       free(closure);
-}
 
 static void server_on_describe_cb(void *closure, struct afb_proto_ws_describe *describe)
 {
-       struct server_describe *desc, sdesc;
        struct afb_stub_ws *stubws = closure;
 
-       /* allocate (if possible) and init */
-       desc = malloc(sizeof *desc);
-       if (desc == NULL)
-               desc = &sdesc;
-       desc->stubws = stubws;
-       desc->describe = describe;
-       afb_stub_ws_addref(stubws);
-
-       /* process */
-       if (desc == &sdesc || jobs_queue(NULL, 0, server_describe_job, desc) < 0)
-               jobs_call(NULL, 0, server_describe_cb, desc);
+       afb_apiset_describe(stubws->apiset, stubws->apiname, server_on_description_cb, describe);
 }
 
 /*****************************************************/