From b81bab801d1a39cce7254b0c056d991412ec4331 Mon Sep 17 00:00:00 2001
From: José Bollo Sample Post File
+
+
+In that case, the argument named **file** has its value and its
+path defined and not NULL.
+
+The value is the name of the file as it was
+set by the HTTP client and is generally the filename on the
+client side.
+
+The path is the path of the file saved on the temporary local storage
+area of the application. This is a randomly generated and unic filename
+not linked in any way with the original filename on the client.
+
+The plugin can use the file at the given path the way that it wants:
+read, write, remove, copy, rename...
+But when the reply is sent and the query is terminated, the file at
+this path is destroyed if it still exist.
+
+### Arguments as a JSON object
+
+Plugins can get all the arguments as one single object.
+This feature is provided by the function **afb_req_json**
+that is defined as below:
+
+ /*
+ * Gets from the request 'req' the json object hashing the arguments.
+ * The returned object must not be released using 'json_object_put'.
+ */
+ struct json_object *afb_req_json(struct afb_req req);
+
+It returns a json object. This object depends on how the request was
+made:
+
+- For HTTP requests, this is an object whose keys are the names of the
+arguments and whose values are either a string for common arguments or
+an object like { "file": "...", "path": "..." }
+
+- For WebSockets requests, the returned object is the object
+given by the client transparently transported.
+
+> In fact, for Websockets requests, the function **afb_req_value**
+> can be seen as a shortcut to
+> ***json_object_get_string(json_object_object_get(afb_req_json(req), name))***
+
+Initialisation of the plugin and declaration of verbs
+-----------------------------------------------------
+
+To be active, the verbs of the plugin should be declared to
+afb-daemon. And even more, the plugin itself must be recorded.
+
+The mechanism for doing this is very simple: when afb-need starts,
+it loads the plugins that are listed in its argument or configuration.
+
+Loading a plugin follows the following steps:
+
+1. It loads the plugin using *dlopen*.
+
+2. It searchs for the symbol named **pluginAfbV1Register** using *dlsym*.
+This symbol is assumed to be the exported initialisation function of the plugin.
+
+3. It build an interface object for the plugin.
+
+4. It calls the found function **pluginAfbV1Register** and pass it the pointer
+to its interface.
+
+5. The function **pluginAfbV1Register** setup the plugin, initialize it.
+
+6. The function **pluginAfbV1Register** returns the pointer to a structure
+that describes the plugin: its version, its name (prefix or API name), and the
+list of its verbs.
+
+7. Afb-daemon checks that the returned version and name can be managed.
+If it can manage it, the plugin and its verbs are recorded and can be used
+when afb-daemon finishes it initialisation.
+
+Here is the listing of the function **pluginAfbV1Register** of the plugin
+*tic-tac-toe*:
+
+ /*
+ * activation function for registering the plugin called by afb-daemon
+ */
+ const struct AFB_plugin *pluginAfbV1Register(const struct AFB_interface *itf)
+ {
+ afbitf = itf; // records the interface for accessing afb-daemon
+ return &plugin_description; // returns the description of the plugin
+ }
+
+This is a very small function because the *tic-tac-toe* plugin doesn't have initialisation step.
+It merely record the daemon's interface and returns its descritption.
+
+The variable **afbitf** is a variable global to the plugin. It records the
+interface to afb-daemon and is used for logging and pushing events.
+Here is its declaration:
+
+ /*
+ * the interface to afb-daemon
+ */
+ const struct AFB_interface *afbitf;
+
+The description of the plugin is defined as below.
+
+ /*
+ * array of the verbs exported to afb-daemon
+ */
+ static const struct AFB_verb_desc_v1 plugin_verbs[] = {
+ /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */
+ { .name= "new", .session= AFB_SESSION_NONE, .callback= new, .info= "Starts a new game" },
+ { .name= "play", .session= AFB_SESSION_NONE, .callback= play, .info= "Tells the server to play" },
+ { .name= "move", .session= AFB_SESSION_NONE, .callback= move, .info= "Tells the client move" },
+ { .name= "board", .session= AFB_SESSION_NONE, .callback= board, .info= "Get the current board" },
+ { .name= "level", .session= AFB_SESSION_NONE, .callback= level, .info= "Set the server level" },
+ { .name= "join", .session= AFB_SESSION_CHECK,.callback= join, .info= "Join a board" },
+ { .name= "undo", .session= AFB_SESSION_NONE, .callback= undo, .info= "Undo the last move" },
+ { .name= "wait", .session= AFB_SESSION_NONE, .callback= wait, .info= "Wait for a change" },
+ { .name= NULL } /* marker for end of the array */
+ };
+
+ /*
+ * description of the plugin for afb-daemon
+ */
+ static const struct AFB_plugin plugin_description =
+ {
+ /* description conforms to VERSION 1 */
+ .type= AFB_PLUGIN_VERSION_1,
+ .v1= { /* fills the v1 field of the union when AFB_PLUGIN_VERSION_1 */
+ .prefix= "tictactoe", /* the API name (or plugin name or prefix) */
+ .info= "Sample tac-tac-toe game", /* short description of of the plugin */
+ .verbs = plugin_verbs /* the array describing the verbs of the API */
+ }
+ };
+
+The structure **plugin_description** describes the plugin.
+It declares the type and version of the plugin, its name, a description
+and a list of its verbs.
+
+The list of verbs is an array of structures describing the verbs and terminated by a marker:
+a verb whose name is NULL.
+
+The description of the verbs for this version is made of 4 fields:
+
+- the name of the verbs,
+
+- the session management flags,
+
+- the implementation function to be call for the verb,
+
+- a short description.
+
+The structure describing verbs is defined as follows:
+
+ /*
+ * Description of one verb of the API provided by the plugin
+ * This enumeration is valid for plugins of type 1
+ */
+ struct AFB_verb_desc_v1
+ {
+ const char *name; /* name of the verb */
+ enum AFB_session_v1 session; /* authorisation and session requirements of the verb */
+ void (*callback)(struct afb_req req); /* callback function implementing the verb */
+ const char *info; /* textual description of the verb */
+ };
+
+For technical reasons, the enumeration **enum AFB_session_v1** is not exactly an
+enumeration but the wrapper of constant definitions that can be mixed using bitwise or
+(the C operator |).
+
+The constants that can bit mixed are:
+
+Constant name | Meaning
+-------------------------|-------------------------------------------------------------
+**AFB_SESSION_CREATE** | Equals to AFB_SESSION_LOA_EQ_0|AFB_SESSION_RENEW
+**AFB_SESSION_CLOSE** | Closes the session after the reply and set the LOA to 0
+**AFB_SESSION_RENEW** | Refreshes the token of authentification
+**AFB_SESSION_CHECK** | Just requires the token authentification
+**AFB_SESSION_LOA_LE_0** | Requires the current LOA to be lesser then or equal to 0
+**AFB_SESSION_LOA_LE_1** | Requires the current LOA to be lesser then or equal to 1
+**AFB_SESSION_LOA_LE_2** | Requires the current LOA to be lesser then or equal to 2
+**AFB_SESSION_LOA_LE_3** | Requires the current LOA to be lesser then or equal to 3
+**AFB_SESSION_LOA_GE_0** | Requires the current LOA to be greater then or equal to 0
+**AFB_SESSION_LOA_GE_1** | Requires the current LOA to be greater then or equal to 1
+**AFB_SESSION_LOA_GE_2** | Requires the current LOA to be greater then or equal to 2
+**AFB_SESSION_LOA_GE_3** | Requires the current LOA to be greater then or equal to 3
+**AFB_SESSION_LOA_EQ_0** | Requires the current LOA to be equal to 0
+**AFB_SESSION_LOA_EQ_1** | Requires the current LOA to be equal to 1
+**AFB_SESSION_LOA_EQ_2** | Requires the current LOA to be equal to 2
+**AFB_SESSION_LOA_EQ_3** | Requires the current LOA to be equal to 3
+
+If any of this flags is set, afb-daemon requires the token authentification
+as if the flag **AFB_SESSION_CHECK** had been set.
+
+The special value **AFB_SESSION_NONE** is zero and can be used to avoid any check.
+
+> Note that **AFB_SESSION_CREATE** and **AFB_SESSION_CLOSE** might be removed in later versions.
+
+Sending messages to the log system
+----------------------------------
+
+Afb-daemon provides 4 levels of verbosity and 5 verbs for logging messages.
+
+The verbosity is managed. Options allow the change the verbosity of afb-daemon
+and the verbosity of the plugins can be set plugin by plugin.
+
+The verbs for logging messages are defined as macros that test the
+verbosity level and that call the real logging function only if the
+message must be output. This avoid evaluation of arguments of the
+formatting messages if the message must not be output.
+
+### Verbs for logging messages
+
+The 5 logging verbs are:
+
+Macro | Verbosity | Meaning | syslog level
+--------|:---------:|-----------------------------------|:-----------:
+ERROR | 0 | Error conditions | 3
+WARNING | 1 | Warning conditions | 4
+NOTICE | 1 | Normal but significant condition | 5
+INFO | 2 | Informational | 6
+DEBUG | 3 | Debug-level messages | 7
+
+You can note that the 2 verbs **WARNING** and **INFO** have the same level
+of verbosity. But they don't have the same *syslog level*. It means that
+they are output with a different level on the logging system.
+
+All of these verbs have the same signature:
+
+ void ERROR(const struct AFB_interface *afbitf, const char *message, ...);
+
+The first argument **afbitf** is the interface to afb daemon that the
+plugin received at its initialisation when **pluginAfbV1Register** was called.
+
+The second argument **message** is a formatting string compatible with printf/sprintf.
+
+The remaining arguments are arguments of the formating message like for printf.
+
+### Managing verbosity
+
+Depending on the level of verbosity, the messages are output or not.
+The following table explains what messages will be output depending
+ont the verbosity level.
+
+Level of verbosity | Outputed macro
+:-----------------:|--------------------------
+ 0 | ERROR
+ 1 | ERROR + WARNING + NOTICE
+ 2 | ERROR + WARNING + NOTICE + INFO
+ 3 | ERROR + WARNING + NOTICE + INFO + DEBUG
+
+### Output format and destination
+
+The syslog level is used for forging a prefix to the message.
+The prefixes are:
+
+syslog level | prefix
+:-----------:|---------------
+ 0 | <0> EMERGENCY
+ 1 | <1> ALERT
+ 2 | <2> CRITICAL
+ 3 | <3> ERROR
+ 4 | <4> WARNING
+ 5 | <5> NOTICE
+ 6 | <6> INFO
+ 7 | <7> DEBUG
+
+
+The message is issued to the standard error.
+The final destination of the message depends on how the systemd service
+was configured through the variable **StandardError**: It can be
+journal, syslog or kmsg. (See man sd-daemon).
+
+Sending events
+--------------
+
+
+Writing an asynchronous verb implementation
+-------------------------------------------
+
+
+How to build a plugin
+---------------------
+
+Afb-daemon provides a *pkg-config* configuration file that can be
+queried by the name **afb-daemon**.
+This configuration file provides data that should be used
+for compiling plugins. Examples:
+
+ $ pkg-config --cflags afb-daemon
+ $ pkg-config --libs afb-daemon
+
+### Example for cmake meta build system
+
+This example is the extract for building the plugin *afm-main* using *CMAKE*.
+
+ pkg_check_modules(afb afb-daemon)
+ if(afb_FOUND)
+ message(STATUS "Creation afm-main-plugin for AFB-DAEMON")
+ add_library(afm-main-plugin MODULE afm-main-plugin.c)
+ target_compile_options(afm-main-plugin PRIVATE ${afb_CFLAGS})
+ target_include_directories(afm-main-plugin PRIVATE ${afb_INCLUDE_DIRS})
+ target_link_libraries(afm-main-plugin utils ${afb_LIBRARIES})
+ set_target_properties(afm-main-plugin PROPERTIES
+ PREFIX ""
+ LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/afm-main-plugin.export-map"
+ )
+ install(TARGETS afm-main-plugin LIBRARY DESTINATION ${plugin_dir})
+ else()
+ message(STATUS "Not creating the plugin for AFB-DAEMON")
+ endif()
+
+Let now describe some of these lines.
+
+ pkg_check_modules(afb afb-daemon)
+
+This first lines searches to the *pkg-config* configuration file for
+**afb-daemon**. Resulting data are stored in the following variables:
+
+Variable | Meaning
+------------------|------------------------------------------------
+afb_FOUND | Set to 1 if afb-daemon plugin development files exist
+afb_LIBRARIES | Only the libraries (w/o the '-l') for compiling afb-daemon plugins
+afb_LIBRARY_DIRS | The paths of the libraries (w/o the '-L') for compiling afb-daemon plugins
+afb_LDFLAGS | All required linker flags for compiling afb-daemon plugins
+afb_INCLUDE_DIRS | The '-I' preprocessor flags (w/o the '-I') for compiling afb-daemon plugins
+afb_CFLAGS | All required cflags for compiling afb-daemon plugins
+
+If development files are found, the plugin can be added to the set of
+target to build.
+
+ add_library(afm-main-plugin MODULE afm-main-plugin.c)
+
+This line asks to create a shared library having only the
+source file afm-main-plugin.c (that is compiled).
+The default name of the created shared object is
+**libafm-main-plugin.so**.
+
+ set_target_properties(afm-main-plugin PROPERTIES
+ PREFIX ""
+ LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/afm-main-plugin.export-map"
+ )
+
+This lines are doing two things:
+
+1. It renames the built library from **libafm-main-plugin.so** to **afm-main-plugin.so**
+by removing the implicitely added prefix *lib*. This step is not mandatory
+at all because afb-daemon doesn't check names of files when loading it.
+The only convention that use afb-daemon is that extension is **.so**
+but this convention is used only when afb-daemon discovers plugin
+from a directory hierarchy.
+
+2. It applies a version script at link to only export the conventional name
+of the entry point: **pluginAfbV1Register**. See below. By default, the linker
+that creates the shared object exports all the public symbols (C functions that
+are not **static**).
+
+Next line are:
+
+ target_include_directories(afm-main-plugin PRIVATE ${afb_INCLUDE_DIRS})
+ target_link_libraries(afm-main-plugin utils ${afb_LIBRARIES})
+
+As you can see it uses the variables computed by ***pkg_check_modules(afb afb-daemon)***
+to configure the compiler and the linker.
+
+### Exporting the function pluginAfbV1Register
+
+The function **pluginAfbV1Register** must be exported. This can be achieved
+using a version script when linking. Here is the version script that is
+used for *tic-tac-toe* (plugins/samples/export.map).
+
+ { global: pluginAfbV1Register; local: *; };
+
+This sample [version script](https://sourceware.org/binutils/docs-2.26/ld/VERSION.html#VERSION)
+exports as global the symbol *pluginAfbV1Register* and hides any
+other symbols.
+
+This version script is added to the link options using the
+option **--version-script=export.map** is given directly to the
+linker or using th option **-Wl,--version-script=export.map**
+when the option is given to the C compiler.
+
+### Building within yocto
+
+Adding a dependency to afb-daemon is enough. See below:
+
+ DEPENDS += " afb-daemon "
+
--
cgit 1.2.3-korg
From d96d0533b8326570db57d13b8f808bc62d1a7fa4 Mon Sep 17 00:00:00 2001
From: José Bollo Frequently Asked Question about AFB-DAEMON
diff --git a/doc/FAQ.md b/doc/FAQ.md
index 93647311..61105823 100644
--- a/doc/FAQ.md
+++ b/doc/FAQ.md
@@ -1,7 +1,7 @@
Frequently Asked Question about AFB-DAEMON
==========================================
version: 1
- Date: 26 mai 2016
+ Date: 27 mai 2016
Author: José Bollo
TABLE-OF-CONTENT-HERE
diff --git a/doc/afb-daemon-vocabulary.html b/doc/afb-daemon-vocabulary.html
index 096f5076..fadd1dee 100644
--- a/doc/afb-daemon-vocabulary.html
+++ b/doc/afb-daemon-vocabulary.html
@@ -8,7 +8,7 @@
version: 1
-Date: 26 mai 2016
+Date: 27 mai 2016
Author: José Bollo
Vocabulary for AFB-DAEMON
diff --git a/doc/afb-daemon-vocabulary.md b/doc/afb-daemon-vocabulary.md
index 71771947..8427b736 100644
--- a/doc/afb-daemon-vocabulary.md
+++ b/doc/afb-daemon-vocabulary.md
@@ -1,7 +1,7 @@
Vocabulary for AFB-DAEMON
=========================
version: 1
- Date: 26 mai 2016
+ Date: 27 mai 2016
Author: José Bollo
TABLE-OF-CONTENT-HERE
diff --git a/doc/afb-plugin-writing.html b/doc/afb-plugin-writing.html
index 9a98ffe9..9873b626 100644
--- a/doc/afb-plugin-writing.html
+++ b/doc/afb-plugin-writing.html
@@ -37,6 +37,8 @@ Author: José Bollo
version: 1
-Date: 26 mai 2016
+Date: 27 mai 2016
Author: José Bollo
This plugin is named tictactoe.
+ +Afb-daemon provides a configuration file for pkg-config. +Typing the command
+ +pkg-config --cflags afb-daemon
+
+
+will print the flags to use for compiling, like this:
+ +$ pkg-config --cflags afb-daemon
+-I/opt/local/include -I/usr/include/json-c
+
+
+For linking, you should use
+ +$ pkg-config --libs afb-daemon
+-ljson-c
+
+
+As you see, afb-daemon automatically includes dependency to json-c. +This is done through the Requires keyword of pkg-config +because almost all plugin will use json-c.
+ +If this behaviour is a problem, let us know.
+ +Internally, afb-daemon uses libsystemd for its event loop +and for its binding to D-Bus. +Plugins developpers are encouraged to also use this library. +But it is a matter of choice. +Thus there is no dependency to libsystemd.
+ ++ + +Afb-daemon provides no library for plugins. +The functions that the plugin need to have are given +to the plugin at runtime through pointer using read-only +memory.
The plugin tictactoe has the following lines for its includes:
+ +#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <json-c/json.h>
+#include <afb/afb-plugin.h>
+
+
+The header afb/afb-plugin.h includes all the features that a plugin +needs except two foreign header that must be included by the plugin +if it needs it:
+ +The tictactoe plugin does not use systemd features so it is not included.
+ +When including afb/afb-plugin.h, the macro _GNU_SOURCE must be +defined.
+Afb-daemon provides a configuration file for pkg-config. -Typing the command
- -pkg-config --cflags afb-daemon
-
-
-will print the flags to use for compiling, like this:
- -$ pkg-config --cflags afb-daemon
--I/opt/local/include -I/usr/include/json-c
-
-
-For linking, you should use
- -$ pkg-config --libs afb-daemon
--ljson-c
-
-
-As you see, afb-daemon automatically includes dependency to json-c. -This is done through the Requires keyword of pkg-config.
- -If this behaviour is a problem, let us know.
- - -The plugin tictactoe has the following lines for its includes:
- -#define _GNU_SOURCE
-#include <stdio.h>
-#include <string.h>
-#include <json-c/json.h>
-#include <afb/afb-plugin.h>
-
-
-The header afb/afb-plugin.h includes all the features that a plugin -needs except two foreign header that must be included by the plugin -if it needs it:
- -The tictactoe plugin does not use systemd features so it is not included.
- -When including afb/afb-plugin.h, the macro _GNU_SOURCE must be -defined.
-Since version 0.5, plugins can broadcast events to any potential listener. +This kind of bradcast is not targeted. Event targeted will come in a future +version of afb-daemon.
+ +The plugin tic-tac-toe broadcasts events when the board changes. +This is done in the function changed:
+ +/*
+ * signals a change of the board
+ */
+static void changed(struct board *board, const char *reason)
+{
+ ...
+ struct json_object *description;
+
+ /* get the description */
+ description = describe(board);
+
+ ...
+
+ afb_daemon_broadcast_event(afbitf->daemon, reason, description);
+}
+
+
+The description of the changed board is pushed via the daemon interface.
+ +Within the plugin tic-tac-toe, the reason indicates the origin of +the change. For the function afb_daemon_broadcast_event, the second +parameter is the name of the broadcasted event. The third argument is the +object that is transmitted with the event.
+ +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.
+ */
+void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object);
+
+
+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.
+ ++Note that nothing is said about the case sensitivity of event names. +However, the event is always prefixed with the name that the plugin +declared, with the same case, followed with a slash /. +Thus it is safe to compare event using a case sensitive comparison.
/ + * 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);
+
+
+}
++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.
+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.
@@ -1261,31 +1291,106 @@ Thus it is safe to compare event using a case sensitive comparison./ - * signals a change of the board - / -static void changed(struct board board, const char reason) +
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 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;
+ 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.
-afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description);
+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);
+}
-}
+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/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, ...)
{
--
cgit 1.2.3-korg