fix refcount semantics for module_load and have module_unload call module_unloaded on module_desc::unload errors
--- a/src/module.c Thu Apr 02 02:25:35 2009 +0300
+++ b/src/module.c Thu Apr 02 02:55:32 2009 +0300
@@ -238,8 +238,10 @@
goto error;
// ok
- if (module_ptr)
+ if (module_ptr) {
+ module->refcount++;
*module_ptr = module;
+ }
return SUCCESS;
@@ -304,8 +306,9 @@
{
err_t err;
- // sanity-check
- assert(!module->unloading);
+ // wrong state
+ if (module->unloading)
+ return ERR_MODULE_STATE;
// remove from modules list
TAILQ_REMOVE(&module->modules->list, module, modules_list);
@@ -315,8 +318,12 @@
// invoke the unload func, passing it our reference
// note that the module might not exist anymore after calling this
- if ((err = module->desc->unload(module->ctx, module)))
+ if ((err = module->desc->unload(module->ctx, module))) {
+ // mark it as "unloaded"
+ module_unloaded(module);
+
return err;
+ }
// ok
return SUCCESS;
@@ -364,7 +371,7 @@
TAILQ_REMOVE(&module->modules->list, module, modules_list);
// run the destroy hook
- if (module->desc->destroy)
+ if (module->desc && module->desc->destroy)
module->desc->destroy(module->ctx);
// unload the dl handle
--- a/src/module.h Thu Apr 02 02:25:35 2009 +0300
+++ b/src/module.h Thu Apr 02 02:55:32 2009 +0300
@@ -85,9 +85,14 @@
* primary reference is passed on to the module. Once the module has finished unloading, it may call module_put(),
* which may then call module_destroy(), if no other references were left.
*
+ * If the unload operation fails (returns an error code), then the module is considered as unloaded (as if
+ * module_unloaded() was called - don't call this if you return an error).
+ *
* Implementing this is mandatory.
*
* @param ctx the module's context pointer as returned by init
+ * @param module the hanging module reference, that must be passed to module_unloaded()
+ * @return error code
*/
err_t (*unload) (void *ctx, struct module *module);
@@ -162,6 +167,7 @@
ERR_MODULE_SYM, ///< invalid symbol
ERR_MODULE_INIT_FUNC, ///< invalid module_init_func_t
ERR_MODULE_CONF, ///< value error in configuration data
+ ERR_MODULE_STATE, ///< module in wrong state for operation
};
@@ -232,10 +238,13 @@
*
* If info->path is not given, the module will be searched for using the path set by modules_path().
*
+ * If module_ptr is given, a reference (that must be module_put'd) is returned, that must be returned using
+ * module_put().
+ *
* @param modules the module-loading context
- * @param module_ptr return the new module via this, if not NULL
+ * @param module_ptr retuturned new module struct, as a new reference, if not NULL.
* @param info the info required to identify and load the module
- * @param err return error info
+ * @param err returned error info
*/
err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *info, struct error_info *err);
@@ -263,6 +272,9 @@
* Unload a module. This removes the module from the modules_list, marks it as unloading, and then calls the module's
* \p unload function, passing it the primary reference. The module's unload code will then go about shutting down the
* module, and once that is done, it may module_put() the primary reference, which may then lead to module_destroy().
+ *
+ * This returns ERR_MODULE_STATE if the module is already being unloaded, or other errors from the module's own unload
+ * functionality.
*/
err_t module_unload (struct module *module);
--- a/src/nexus.c Thu Apr 02 02:25:35 2009 +0300
+++ b/src/nexus.c Thu Apr 02 02:55:32 2009 +0300
@@ -216,7 +216,6 @@
.name = NULL,
.path = NULL,
};
- struct module *module;
// parse the required fields
if ((info.name = strsep(&opt, ":")) == NULL)
@@ -232,7 +231,7 @@
// load it
log_info("loading module '%s' from path '%s'", info.name, info.path);
- if (module_load(nexus->modules, &module, &info, err))
+ if (module_load(nexus->modules, NULL, &info, err))
return ERROR_CODE(err);
// ok