From: Michał Kępień Date: Fri, 20 Oct 2017 12:41:29 +0000 (+0200) Subject: [master] Extend hooks documentation X-Git-Tag: v9.12.0b2~90 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=34ee1cdb56c17783de7fdb3920f61bedd5b57691;p=thirdparty%2Fbind9.git [master] Extend hooks documentation --- diff --git a/lib/ns/hooks.h b/lib/ns/hooks.h index 36d148bdfd8..382cadbecce 100644 --- a/lib/ns/hooks.h +++ b/lib/ns/hooks.h @@ -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 {