improves documentation
authorJosé Bollo <jose.bollo@iot.bzh>
Fri, 27 May 2016 21:43:24 +0000 (23:43 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Fri, 27 May 2016 21:43:24 +0000 (23:43 +0200)
Change-Id: Ia86f95ee852ca0cc419b2fe6e796621e358c8b29
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
doc/afb-plugin-writing.html
doc/afb-plugin-writing.md
include/afb/afb-plugin.h
include/afb/afb-req-itf.h

index 9873b62..20e2597 100644 (file)
@@ -612,12 +612,20 @@ failure replies.</p>
  * The status of the reply is automatically set to "success".
  * Its send the object 'obj' (can be NULL) with an
  * informationnal comment 'info (can also be NULL).
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 void afb_req_success(struct afb_req req, struct json_object *obj, const char *info);
 
 /*
  * Same as 'afb_req_success' but the 'info' is a formatting
  * string followed by arguments.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...);
 </code></pre>
@@ -633,16 +641,29 @@ void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *
  * Note that calling afb_req_fail("success", info) is equivalent
  * to call afb_req_success(NULL, info). Thus even if possible it
  * is strongly recommanded to NEVER use "success" for status.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 void afb_req_fail(struct afb_req req, const char *status, const char *info);
 
 /*
  * Same as 'afb_req_fail' but the 'info' is a formatting
  * string followed by arguments.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...);
 </code></pre>
 
+<blockquote><p>For conveniency, these functions call <strong>json_object_put</strong> to release the object <strong>obj</strong>
+that they send. Then <strong>obj</strong> can not be used after calling one of these reply functions.
+When it is not the expected behaviour, calling the function <strong>json_object_get</strong> on the object <strong>obj</strong>
+before cancels the effect of <strong>json_object_put</strong>.</p></blockquote>
+
 <a name="Getting.argument.of.invocation"></a>
 <h2>Getting argument of invocation</h2>
 
@@ -1245,10 +1266,19 @@ object that is transmitted with the event.</p>
  * Broadcasts widely the event of 'name' with the data 'object'.
  * 'object' can be NULL.
  * 'daemon' MUST be the daemon given in interface when activating the plugin.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'object'.
+ * Thus, in the case where 'object' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object);
 </code></pre>
 
+<blockquote><p>Be aware, as for reply functions, the <strong>object</strong> is automatically released using
+<strong>json_object_put</strong> by the function. Then call <strong>json_object_get</strong> before
+calling <strong>afb_daemon_broadcast_event</strong> to keep <strong>object</strong> available
+after the returning of the function.</p></blockquote>
+
 <p>In fact the event name received by the listener is prefixed with
 the name of the plugin. So when the change occurs after a move, the
 reason is <strong>move</strong> and then the clients receive the event <strong>tictactoe/move</strong>.</p>
@@ -1261,31 +1291,106 @@ Thus it is safe to compare event using a case sensitive comparison.</p></blockqu
 <a name="Writing.an.asynchronous.verb.implementation"></a>
 <h2>Writing an asynchronous verb implementation</h2>
 
-<p>/<em>
- * signals a change of the board
- </em>/
-static void changed(struct board <em>board, const char </em>reason)
+<p>The <em>tic-tac-toe</em> example allows two clients or more to share the same board.
+This is implemented by the verb <strong>join</strong> that illustrated partly the how to
+retrieve arguments.</p>
+
+<p>When two or more clients are sharing a same board, one of them can wait
+until the state of the board changes. (This coulded also be implemented using
+events because an even is generated each time the board changes).</p>
+
+<p>In this case, the reply to the wait is sent only when the board changes.
+See the diagram below:</p>
+
+<pre><code>CLIENT A       CLIENT B         TIC-TAC-TOE
+   |              |                  |
+   +--------------|-----------------&gt;| wait . . . . . . . .
+   |              |                  |                     .
+   :              :                  :                      .
+   :              :                  :                      .
+   |              |                  |                      .
+   |              +-----------------&gt;| move . . .           .
+   |              |                  |          V           .
+   |              |&lt;-----------------+ success of move      .
+   |              |                  |                    .
+   |&lt;-------------|------------------+ success of wait  &lt;
+</code></pre>
+
+<p>Here, this is an invocation of the plugin by an other client that
+unblock the suspended <em>wait</em> call.
+But in general, this will be a timer, a hardware event, the sync with
+a concurrent process or thread, &hellip;</p>
+
+<p>So the case is common, this is an asynchronous implementation.</p>
+
+<p>Here is the listing of the function <strong>wait</strong>:</p>
+
+<pre><code>static void wait(struct afb_req req)
 {
-    struct waiter <em>waiter, </em>next;
-    struct json_object *description;</p>
-
-<pre><code>/* get the description */
-description = describe(board);
-
-waiter = board-&gt;waiters;
-board-&gt;waiters = NULL;
-while (waiter != NULL) {
-        next = waiter-&gt;next;
-        afb_req_success(waiter-&gt;req, json_object_get(description), reason);
-        afb_req_unref(waiter-&gt;req);
-        free(waiter);
-        waiter = next;
+        struct board *board;
+        struct waiter *waiter;
+
+        /* retrieves the context for the session */
+        board = board_of_req(req);
+        INFO(afbitf, "method 'wait' called for boardid %d", board-&gt;id);
+
+        /* creates the waiter and enqueues it */
+        waiter = calloc(1, sizeof *waiter);
+        waiter-&gt;req = req;
+        waiter-&gt;next = board-&gt;waiters;
+        afb_req_addref(req);
+        board-&gt;waiters = waiter;
 }
+</code></pre>
+
+<p>After retrieving the board, the function adds a new waiter to the
+current list of waiters and returns without sending a reply.</p>
 
-afb_event_sender_push(afb_daemon_get_event_sender(afbitf-&gt;daemon), reason, description);
+<p>Before returning, it increases the reference count of the
+request <strong>req</strong> using the function <strong>afb_req_addref</strong>.</p>
+
+<blockquote><p>When the implentation of a verb returns without sending a reply,
+it <strong>MUST</strong> increment the reference count of the request
+using <strong>afb_req_addref</strong>. If it doesn&rsquo;t bad things can happen.</p></blockquote>
+
+<p>Later, when the board changes, it calls the function <strong>changed</strong>
+of <em>tic-tac-toe</em> with the reason of the change.</p>
+
+<p>Here is the full listing of the function <strong>changed</strong>:</p>
+
+<pre><code>/*
+ * signals a change of the board
+ */
+static void changed(struct board *board, const char *reason)
+{
+        struct waiter *waiter, *next;
+        struct json_object *description;
+
+        /* get the description */
+        description = describe(board);
+
+        waiter = board-&gt;waiters;
+        board-&gt;waiters = NULL;
+        while (waiter != NULL) {
+                next = waiter-&gt;next;
+                afb_req_success(waiter-&gt;req, json_object_get(description), reason);
+                afb_req_unref(waiter-&gt;req);
+                free(waiter);
+                waiter = next;
+        }
+
+        afb_event_sender_push(afb_daemon_get_event_sender(afbitf-&gt;daemon), reason, description);
+}
 </code></pre>
 
-<p>}</p>
+<p>The list of waiters is walked and a reply is sent to each waiter.
+After the sending the reply, the reference count of the request
+is decremented using <strong>afb_req_unref</strong> to allow its resources to be freed.</p>
+
+<blockquote><p>The reference count <strong>MUST</strong> be decremented using <strong>afb_req_unref</strong> because,
+otherwise, there is a leak of resources.
+It must be decremented <strong>AFTER</strong> the sending of the reply, because, otherwise,
+bad things may happen.</p></blockquote>
 
 <a name="How.to.build.a.plugin"></a>
 <h2>How to build a plugin</h2>
index 7861021..486b141 100644 (file)
@@ -501,12 +501,20 @@ The two functions to send a reply of kind "success" are
         * The status of the reply is automatically set to "success".
         * Its send the object 'obj' (can be NULL) with an
         * informationnal comment 'info (can also be NULL).
+        *
+        * For conveniency, the function calls 'json_object_put' for 'obj'.
+        * Thus, in the case where 'obj' should remain available after
+        * the function returns, the function 'json_object_get' shall be used.
         */
        void afb_req_success(struct afb_req req, struct json_object *obj, const char *info);
 
        /*
         * Same as 'afb_req_success' but the 'info' is a formatting
         * string followed by arguments.
+        *
+        * For conveniency, the function calls 'json_object_put' for 'obj'.
+        * Thus, in the case where 'obj' should remain available after
+        * the function returns, the function 'json_object_get' shall be used.
         */
        void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...);
 
@@ -521,15 +529,28 @@ The two functions to send a reply of kind "failure" are
         * Note that calling afb_req_fail("success", info) is equivalent
         * to call afb_req_success(NULL, info). Thus even if possible it
         * is strongly recommanded to NEVER use "success" for status.
+        *
+        * For conveniency, the function calls 'json_object_put' for 'obj'.
+        * Thus, in the case where 'obj' should remain available after
+        * the function returns, the function 'json_object_get' shall be used.
         */
        void afb_req_fail(struct afb_req req, const char *status, const char *info);
 
        /*
         * Same as 'afb_req_fail' but the 'info' is a formatting
         * string followed by arguments.
+        *
+        * For conveniency, the function calls 'json_object_put' for 'obj'.
+        * Thus, in the case where 'obj' should remain available after
+        * the function returns, the function 'json_object_get' shall be used.
         */
        void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...);
 
+> For conveniency, these functions call **json_object_put** to release the object **obj**
+> that they send. Then **obj** can not be used after calling one of these reply functions.
+> When it is not the expected behaviour, calling the function **json_object_get** on the object **obj**
+> before cancels the effect of **json_object_put**.
+
 Getting argument of invocation
 ------------------------------
 
@@ -968,9 +989,18 @@ The function **afb_daemon_broadcast_event** is defined as below:
         * Broadcasts widely the event of 'name' with the data 'object'.
         * 'object' can be NULL.
         * 'daemon' MUST be the daemon given in interface when activating the plugin.
+        *
+        * For conveniency, the function calls 'json_object_put' for 'object'.
+        * Thus, in the case where 'object' should remain available after
+        * the function returns, the function 'json_object_get' shall be used.
         */
        void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object);
 
+> Be aware, as for reply functions, the **object** is automatically released using
+> **json_object_put** by the function. Then call **json_object_get** before
+> calling **afb_daemon_broadcast_event** to keep **object** available
+> after the returning of the function.
+
 In fact the event name received by the listener is prefixed with
 the name of the plugin. So when the change occurs after a move, the
 reason is **move** and then the clients receive the event **tictactoe/move**.
@@ -981,32 +1011,107 @@ reason is **move** and then the clients receive the event **tictactoe/move**.
 > Thus it is safe to compare event using a case sensitive comparison.
 
 
+
 Writing an asynchronous verb implementation
 -------------------------------------------
 
-/*
- * signals a change of the board
- */
-static void changed(struct board *board, const char *reason)
-{
-       struct waiter *waiter, *next;
-       struct json_object *description;
-
-       /* get the description */
-       description = describe(board);
-
-       waiter = board->waiters;
-       board->waiters = NULL;
-       while (waiter != NULL) {
-               next = waiter->next;
-               afb_req_success(waiter->req, json_object_get(description), reason);
-               afb_req_unref(waiter->req);
-               free(waiter);
-               waiter = next;
+The *tic-tac-toe* example allows two clients or more to share the same board.
+This is implemented by the verb **join** that illustrated partly the how to
+retrieve arguments.
+
+When two or more clients are sharing a same board, one of them can wait
+until the state of the board changes. (This coulded also be implemented using
+events because an even is generated each time the board changes).
+
+In this case, the reply to the wait is sent only when the board changes.
+See the diagram below:
+
+       CLIENT A       CLIENT B         TIC-TAC-TOE
+          |              |                  |
+          +--------------|----------------->| wait . . . . . . . .
+          |              |                  |                     .
+          :              :                  :                      .
+          :              :                  :                      .
+          |              |                  |                      .
+          |              +----------------->| move . . .           .
+          |              |                  |          V           .
+          |              |<-----------------+ success of move      .
+          |              |                  |                    .
+          |<-------------|------------------+ success of wait  <
+
+Here, this is an invocation of the plugin by an other client that
+unblock the suspended *wait* call.
+But in general, this will be a timer, a hardware event, the sync with
+a concurrent process or thread, ...
+
+So the case is common, this is an asynchronous implementation.
+
+Here is the listing of the function **wait**:
+
+       static void wait(struct afb_req req)
+       {
+               struct board *board;
+               struct waiter *waiter;
+
+               /* retrieves the context for the session */
+               board = board_of_req(req);
+               INFO(afbitf, "method 'wait' called for boardid %d", board->id);
+
+               /* creates the waiter and enqueues it */
+               waiter = calloc(1, sizeof *waiter);
+               waiter->req = req;
+               waiter->next = board->waiters;
+               afb_req_addref(req);
+               board->waiters = waiter;
+       }
+
+After retrieving the board, the function adds a new waiter to the
+current list of waiters and returns without sending a reply.
+
+Before returning, it increases the reference count of the
+request **req** using the function **afb_req_addref**.
+
+> When the implentation of a verb returns without sending a reply,
+> it **MUST** increment the reference count of the request
+> using **afb_req_addref**. If it doesn't bad things can happen.
+
+Later, when the board changes, it calls the function **changed**
+of *tic-tac-toe* with the reason of the change.
+
+Here is the full listing of the function **changed**:
+
+       /*
+        * signals a change of the board
+        */
+       static void changed(struct board *board, const char *reason)
+       {
+               struct waiter *waiter, *next;
+               struct json_object *description;
+
+               /* get the description */
+               description = describe(board);
+
+               waiter = board->waiters;
+               board->waiters = NULL;
+               while (waiter != NULL) {
+                       next = waiter->next;
+                       afb_req_success(waiter->req, json_object_get(description), reason);
+                       afb_req_unref(waiter->req);
+                       free(waiter);
+                       waiter = next;
+               }
+
+               afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description);
        }
 
-       afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description);
-}
+The list of waiters is walked and a reply is sent to each waiter.
+After the sending the reply, the reference count of the request
+is decremented using **afb_req_unref** to allow its resources to be freed.
+
+> The reference count **MUST** be decremented using **afb_req_unref** because,
+> otherwise, there is a leak of resources.
+> It must be decremented **AFTER** the sending of the reply, because, otherwise,
+> bad things may happen.
 
 How to build a plugin
 ---------------------
index 3d16c7a..2b065b0 100644 (file)
@@ -212,6 +212,10 @@ static inline struct sd_bus *afb_daemon_get_system_bus(struct afb_daemon daemon)
  * Broadcasts widely the event of 'name' with the data 'object'.
  * 'object' can be NULL.
  * 'daemon' MUST be the daemon given in interface when activating the plugin.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'object'.
+ * Thus, in the case where 'object' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 static inline void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object)
 {
index 5767c85..f4fab55 100644 (file)
@@ -130,6 +130,10 @@ static inline struct json_object *afb_req_json(struct afb_req req)
  * The status of the reply is automatically set to "success".
  * Its send the object 'obj' (can be NULL) with an
  * informationnal comment 'info (can also be NULL).
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 static inline void afb_req_success(struct afb_req req, struct json_object *obj, const char *info)
 {
@@ -139,6 +143,10 @@ static inline void afb_req_success(struct afb_req req, struct json_object *obj,
 /*
  * Same as 'afb_req_success' but the 'info' is a formatting
  * string followed by arguments.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 static inline void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...)
 {
@@ -160,6 +168,10 @@ static inline void afb_req_success_f(struct afb_req req, struct json_object *obj
  * Note that calling afb_req_fail("success", info) is equivalent
  * to call afb_req_success(NULL, info). Thus even if possible it
  * is strongly recommanded to NEVER use "success" for status.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 static inline void afb_req_fail(struct afb_req req, const char *status, const char *info)
 {
@@ -169,6 +181,10 @@ static inline void afb_req_fail(struct afb_req req, const char *status, const ch
 /*
  * Same as 'afb_req_fail' but the 'info' is a formatting
  * string followed by arguments.
+ *
+ * For conveniency, the function calls 'json_object_put' for 'obj'.
+ * Thus, in the case where 'obj' should remain available after
+ * the function returns, the function 'json_object_get' shall be used.
  */
 static inline void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...)
 {