diff options
-rw-r--r-- | doc/afb-plugin-writing.html | 145 | ||||
-rw-r--r-- | doc/afb-plugin-writing.md | 147 | ||||
-rw-r--r-- | include/afb/afb-plugin.h | 4 | ||||
-rw-r--r-- | include/afb/afb-req-itf.h | 16 |
4 files changed, 271 insertions, 41 deletions
diff --git a/doc/afb-plugin-writing.html b/doc/afb-plugin-writing.html index 9873b626..20e25972 100644 --- a/doc/afb-plugin-writing.html +++ b/doc/afb-plugin-writing.html @@ -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 + | | | + +--------------|----------------->| wait . . . . . . . . + | | | . + : : : . + : : : . + | | | . + | +----------------->| move . . . . + | | | V . + | |<-----------------+ success of move . + | | | . + |<-------------|------------------+ success of wait < +</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, …</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->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; + 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; } +</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->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’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->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); +} </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> diff --git a/doc/afb-plugin-writing.md b/doc/afb-plugin-writing.md index 7861021b..486b141d 100644 --- a/doc/afb-plugin-writing.md +++ b/doc/afb-plugin-writing.md @@ -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 --------------------- diff --git a/include/afb/afb-plugin.h b/include/afb/afb-plugin.h index 3d16c7ad..2b065b08 100644 --- a/include/afb/afb-plugin.h +++ b/include/afb/afb-plugin.h @@ -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) { diff --git a/include/afb/afb-req-itf.h b/include/afb/afb-req-itf.h index 5767c853..f4fab551 100644 --- a/include/afb/afb-req-itf.h +++ b/include/afb/afb-req-itf.h @@ -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, ...) { |