Commit 34ee1cdb authored by Michał Kępień's avatar Michał Kępień
Browse files

[master] Extend hooks documentation

parent 6853af8f
......@@ -20,21 +20,19 @@
* code is reached. Current use is limited to libns unit tests and thus:
*
* - hook-related types and macros are not placed in libns header files,
* - hook-related code is compiled away unless --enable-developer is used,
* - hook-related code is compiled away unless --with-atf is used,
* - hook-related macro names are prefixed with "NS_".
*
* However, the implementation is pretty generic and could be repurposed for
* general use, e.g. as part of libisc, after some further customization.
*
* Hooks are created by inserting a macro into any function returning
* isc_result_t (NS_PROCESS_HOOK()) or void (NS_PROCESS_HOOK_VOID()). Each
* hook has an identifier, which is an integer that is an index into the hook
* table. In an attempt to keep things as simple as possible, current
* implementation:
*
* - uses hook tables which are statically-sized arrays only allowing a
* single callback to be invoked for each hook identifier,
* - only supports replacing whole hook tables.
* isc_result_t (NS_PROCESS_HOOK()) or void (NS_PROCESS_HOOK_VOID()). As both
* of these macros contain a return statement which is inlined into the
* function into which the hook is inserted, a hook callback is able to cause
* that function to return at hook insertion point. For functions returning
* isc_result_t, if a hook callback intends to cause a return at hook insertion
* point, it also has to set the value to be returned by the function.
*
* Hook callbacks are functions which:
*
......@@ -50,9 +48,17 @@
* - a pointer to isc_result_t which will be returned by the function
* into which the hook is inserted if the callback returns ISC_TRUE.
*
* Hook tables are arrays which consist of a number of tuples (one tuple per
* hook identifier), each of which determines the callback to be invoked when a
* given hook is processed and the data to be passed to that callback. In an
* attempt to keep things as simple as possible, current implementation uses
* hook tables which are statically-sized arrays only allowing a single
* callback to be invoked for each hook identifier.
*
* In order for a hook callback to be called for a given hook, a pointer to
* that callback (along with an optional pointer to callback-specific data) has
* to be inserted into the hook table entry for that hook.
* to be inserted into the relevant hook table entry for that hook. Replacing
* whole hook tables is also possible.
*
* Consider the following sample code:
*
......@@ -62,9 +68,13 @@
* isc_result_t
* foo_bar(void) {
* int val = 42;
*
* ...
*
* NS_PROCESS_HOOK(foo_hook_table, FOO_EXTRACT_VAL, &val);
*
* ...
*
* printf("This message may not be printed due to use of hooks.");
*
* return (ISC_R_SUCCESS);
......@@ -72,35 +82,75 @@
*
* isc_boolean_t
* cause_failure(void *hook_data, void *callback_data, isc_result_t *resultp) {
* int *valp = (int *)hook_data;
* isc_boolean_t *calledp = (isc_boolean_t *)callback_data;
*
* ...
*
* *resultp = ISC_R_FAILURE;
*
* return (ISC_TRUE);
* }
*
* isc_boolean_t
* examine_val(void *hook_data, void *callback_data, isc_result_t *resultp) {
* int *valp = (int *)hook_data;
* int *valcopyp = (int *)callback_data;
*
* UNUSED(resultp);
*
* ...
*
* return (ISC_FALSE);
* }
*
* void
* test_foo_bar(void) {
* isc_boolean_t foo_bar_called = ISC_FALSE;
* isc_boolean_t called = ISC_FALSE;
* int valcopy;
*
* ns_hook_t my_hooks[FOO_HOOKS_COUNT] = {
* [FOO_EXTRACT_VAL] = {
* .callback = cause_failure,
* .callback_data = &foo_bar_called,
* .callback_data = &called,
* },
* };
*
* foo_hook_table = my_hooks;
* foo_bar();
*
* {
* const ns_hook_t examine_hook = {
* .callback = examine_val,
* .callback_data = &valcopy,
* };
*
* my_hooks[FOO_EXTRACT_VAL] = examine_hook;
* }
* foo_bar();
*
* }
* ----------------------------------------------------------------------------
*
* When test_foo_bar() is called, the hook table is first replaced. Then
* When test_foo_bar() is called, "foo_hook_table" is set to "my_hooks". Then
* foo_bar() gets invoked. Once execution reaches the insertion point for hook
* FOO_EXTRACT_VAL, cause_failure() will be called with &val as hook_data and
* &foo_bar_called as callback_data. It can do whatever it pleases with these
* two values. Eventually, cause_failure() sets *resultp to ISC_R_FAILURE and
* FOO_EXTRACT_VAL, cause_failure() will be called with &val as "hook_data" and
* &called as "callback_data". It can do whatever it pleases with these two
* values. Eventually, cause_failure() sets *resultp to ISC_R_FAILURE and
* returns ISC_TRUE, which causes foo_bar() to return ISC_R_FAILURE and never
* execute the printf() call below hook insertion point.
*
* Execution then returns to test_foo_bar(). Unlike before the first call to
* foo_bar(), this time only a single hook ("examine_hook") is defined instead
* of a complete hook table. This hook is then subsequently inserted at index
* FOO_EXTRACT_VAL into the "my_hook" hook table. This causes the hook
* previously set at that index (the one calling cause_failure()) to be
* replaced with "examine_hook". Thus, when the second call to foo_bar() is
* subsequently made, examine_val() will be called with &val as "hook_data" and
* &valcopy as "callback_data". Contrary to cause_failure(), extract_val()
* returns ISC_FALSE, which means it does not access "resultp" and does not
* cause foo_bar() to return at hook insertion point. Thus, printf() will be
* called this time and foo_bar() will return ISC_R_SUCCESS.
*/
enum {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment