summaryrefslogtreecommitdiffstats
path: root/doc/developer/hooks.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/developer/hooks.rst')
-rw-r--r--doc/developer/hooks.rst171
1 files changed, 171 insertions, 0 deletions
diff --git a/doc/developer/hooks.rst b/doc/developer/hooks.rst
new file mode 100644
index 0000000..b37a4ae
--- /dev/null
+++ b/doc/developer/hooks.rst
@@ -0,0 +1,171 @@
+.. highlight:: c
+
+Hooks
+=====
+
+Libfrr provides type-safe subscribable hook points where other pieces of
+code can add one or more callback functions. "type-safe" in this case
+applies to the function pointers used for subscriptions. The
+implementations checks (at compile-time) whether a callback to be added has
+the appropriate function signature (parameters) for the hook.
+
+Example:
+
+.. code-block:: c
+ :caption: mydaemon.h
+
+ #include "hook.h"
+ DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info));
+
+.. code-block:: c
+ :caption: mydaemon.c
+
+ #include "mydaemon.h"
+ DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info));
+ ...
+ hook_call(some_update_event, info);
+
+.. code-block:: c
+ :caption: mymodule.c
+
+ #include "mydaemon.h"
+ static int event_handler(struct eventinfo *info);
+ ...
+ hook_register(some_update_event, event_handler);
+
+Do not use parameter names starting with "hook", these can collide with
+names used by the hook code itself.
+
+
+Return values
+-------------
+
+Callbacks to be placed on hooks always return "int" for now; hook_call will
+sum up the return values from each called function. (The default is 0 if no
+callbacks are registered.)
+
+There are no pre-defined semantics for the value, in most cases it is
+ignored. For success/failure indication, 0 should be success, and
+handlers should make sure to only return 0 or 1 (not -1 or other values).
+
+There is no built-in way to abort executing a chain after a failure of one
+of the callbacks. If this is needed, the hook can use an extra
+``bool *aborted`` argument.
+
+
+Priorities
+----------
+
+Hooks support a "priority" value for ordering registered calls
+relative to each other. The priority is a signed integer where lower
+values are called earlier. There are also "Koohs", which is hooks with
+reverse priority ordering (for cleanup/deinit hooks, so you can use the
+same priority value).
+
+Recommended priority value ranges are:
+
+======================== ===================================================
+Range Usage
+------------------------ ---------------------------------------------------
+ -999 ... 0 ... 999 main executable / daemon, or library
+
+-1999 ... -1000 modules registering calls that should run before
+ the daemon's bits
+
+1000 ... 1999 modules' calls that should run after daemon's
+ (includes default value: 1000)
+======================== ===================================================
+
+Note: the default value is 1000, based on the following 2 expectations:
+
+- most hook_register() usage will be in loadable modules
+- usage of hook_register() in the daemon itself may need relative ordering
+ to itself, making an explicit value the expected case
+
+The priority value is passed as extra argument on hook_register_prio() /
+hook_register_arg_prio(). Whether a hook runs in reverse is determined
+solely by the code defining / calling the hook. (DECLARE_KOOH is actually
+the same thing as DECLARE_HOOK, it's just there to make it obvious.)
+
+
+Definition
+----------
+
+.. c:macro:: DECLARE_HOOK(name, arglist, passlist)
+.. c:macro:: DECLARE_KOOH(name, arglist, passlist)
+
+ :param name: Name of the hook to be defined
+ :param arglist: Function definition style parameter list in braces.
+ :param passlist: List of the same parameters without their types.
+
+ Note: the second and third macro args must be the hook function's
+ parameter list, with the same names for each parameter. The second
+ macro arg is with types (used for defining things), the third arg is
+ just the names (used for passing along parameters).
+
+ This macro must be placed in a header file; this header file must be
+ included to register a callback on the hook.
+
+ Examples:
+
+ .. code-block:: c
+
+ DECLARE_HOOK(foo, (), ());
+ DECLARE_HOOK(bar, (int arg), (arg));
+ DECLARE_HOOK(baz, (const void *x, in_addr_t y), (x, y));
+
+.. c:macro:: DEFINE_HOOK(name, arglist, passlist)
+
+ Implements an hook. Each ``DECLARE_HOOK`` must have be accompanied by
+ exactly one ``DEFINE_HOOK``, which needs to be placed in a source file.
+ **The hook can only be called from this source file.** This is intentional
+ to avoid overloading and/or misusing hooks for distinct purposes.
+
+ The compiled source file will include a global symbol with the name of the
+ hook prefixed by `_hook_`. Trying to register a callback for a hook that
+ doesn't exist will therefore result in a linker error, or a module
+ load-time error for dynamic modules.
+
+.. c:macro:: DEFINE_KOOH(name, arglist, passlist)
+
+ Same as ``DEFINE_HOOK``, but the sense of priorities / order of callbacks
+ is reversed. This should be used for cleanup hooks.
+
+.. c:function:: int hook_call(name, ...)
+
+ Calls the specified named hook. Parameters to the hook are passed right
+ after the hook name, e.g.:
+
+ .. code-block:: c
+
+ hook_call(foo);
+ hook_call(bar, 0);
+ hook_call(baz, NULL, INADDR_ANY);
+
+ Returns the sum of return values from all callbacks. The ``DEFINE_HOOK``
+ statement for the hook must be placed in the file before any ``hook_call``
+ use of the hook.
+
+
+Callback registration
+---------------------
+
+.. c:function:: void hook_register(name, int (*callback)(...))
+.. c:function:: void hook_register_prio(name, int priority, int (*callback)(...))
+.. c:function:: void hook_register_arg(name, int (*callback)(void *arg, ...), void *arg)
+.. c:function:: void hook_register_arg_prio(name, int priority, int (*callback)(void *arg, ...), void *arg)
+
+ Register a callback with an hook. If the caller needs to pass an extra
+ argument to the callback, the _arg variant can be used and the extra
+ parameter will be passed as first argument to the callback. There is no
+ typechecking for this argument.
+
+ The priority value is used as described above. The variants without a
+ priority parameter use 1000 as priority value.
+
+.. c:function:: void hook_unregister(name, int (*callback)(...))
+.. c:function:: void hook_unregister_arg(name, int (*callback)(void *arg, ...), void *arg)
+
+ Removes a previously registered callback from a hook. Note that there
+ is no _prio variant of these calls. The priority value is only used during
+ registration.