| 87 | === Tracking === |
| 88 | |
| 89 | Apache makes an obvious but significant assumption about module code. It assumes your code knows the module to which it belongs. So the code for mod_cgi KNOWS it's part of mod_cgi, and, as an example, its response handler can ask for the mod_cgi configuration structure appropriately. So Apache provides no infrastructure for asking it what module code it's currently executing -- it assumes you know who you are. |
| 90 | |
| 91 | This is a big problem for mod_parrot, which provides SHARED hooks to support multiple HLL modules. The same hook is called regardless of which HLL module is currently in scope, and Apache won't tell you which module it is. This comes into play in three places: |
| 92 | |
| 93 | * hook registration |
| 94 | * metahandler callbacks |
| 95 | * cleanup handlers |
| 96 | |
| 97 | The single function {{{register_meta_hooks}}} is called to register an HLL module's hooks with Apache. Since this function is called for every HLL module, we can maintain state within the function by using a static variable. In particular, that variable can be an index into the global module array in the mod_parrot server config. And since it's only run once at startup, we can initialize it to zero at the first invocation: |
| 98 | |
| 99 | {{{ |
| 100 | /* initialize the index to 0 for the first module only */ |
| 101 | static int module_index = 0; |
| 102 | }}} |
| 103 | |
| 104 | and retrieve the module structure: |
| 105 | {{{ |
| 106 | mpcfg = ap_get_module_config(our_server->module_config, &parrot_module); |
| 107 | modp = ((module **)mpcfg->module_array->elts)[module_index]; |
| 108 | }}} |
| 109 | |
| 110 | and finally increment the index for the next call: |
| 111 | {{{ |
| 112 | /* prepare for the next module */ |
| 113 | module_index++; |
| 114 | }}} |
| 115 | |
| 116 | Metahandler callbacks use a similar indexing scheme, but the situation is more complex. Not every HLL module registers every hook, so each hook must have its own module array. This also means that a separate index must be used for each phase. And finally, unlike registering the hooks, the actual hooks can be called more than once, so we must somehow reset the index for each phase. |
| 117 | |
| 118 | The per-hook module array is maintained in the mod_parrot server config: |
| 119 | |
| 120 | {{{ |
| 121 | apr_array_header_t *handler_modules[MP_HOOK_LAST]; |
| 122 | }}} |
| 123 | |
| 124 | The index is maintained in the context ({{{module_index}}}). We do this because a context is bound to the current connection and can maintain the index while not interfering with indexes from other connections. The code to actually retrieve the next indexed module is complex, so it's been refactored to a single macro, {{{NEXT_HANDLER_MODULE}}}, which mod_parrot calls for each metahandler. It takes a single argument, the hook: |
| 125 | |
| 126 | {{{ |
| 127 | /* get next module in line */ |
| 128 | modp = NEXT_HANDLER_MODULE(MP_HOOK_PRE_CONNECTION); |
| 129 | }}} |
| 130 | |
| 131 | The only remaining problem is how to reset the index for each connection. mod_parrot registers a hook for every Apache phase with the APR_HOOK_REALLY_FIRST flag so the mod_parrot hook always runs first. It does this to reset the index before any other modules can run. Here's the actualy mod_parrot pre_connection handler, which does nothing but reset the index: |
| 132 | |
| 133 | {{{ |
| 134 | static int modparrot_pre_connection_handler(conn_rec *c, void *csd) |
| 135 | { |
| 136 | modparrot_context *ctxp; |
| 137 | |
| 138 | /* initialize context */ |
| 139 | if (!(ctxp = init_ctx(c->base_server, c))) { |
| 140 | MPLOG_ERROR(c->base_server, "context initialization failed"); |
| 141 | return HTTP_INTERNAL_SERVER_ERROR; |
| 142 | } |
| 143 | |
| 144 | /* we're REALLY_FIRST, so reset the module index */ |
| 145 | ctxp->module_index = -1; |
| 146 | |
| 147 | /* clean up */ |
| 148 | release_ctx(ctxp); |
| 149 | |
| 150 | /* we only do setup */ |
| 151 | return DECLINED; |
| 152 | } |
| 153 | }}} |
| 154 | |