diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index 21af928550ae7612403f427d912f8493c4214a09..823ef1c932a9564d29e19ec923287651a4698cdc 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -248,6 +248,10 @@ static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel * static int unload_module(void) { + ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP); + ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER); + ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_BLINDTRANSFER); + return 0; } @@ -257,8 +261,8 @@ static int load_module(void) ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL); ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL); - /* Bump up our reference count so we can't be unloaded */ - ast_module_ref(ast_module_info->self); + /* This module cannot be unloaded until shutdown */ + ast_module_shutdown_ref(ast_module_info->self); return AST_MODULE_LOAD_SUCCESS; } diff --git a/include/asterisk/module.h b/include/asterisk/module.h index df9f241378e9a41a78a5549de39b78232abda4b2..e4c66965ef7f582f791b4bbb8ed8d14f63348c30 100644 --- a/include/asterisk/module.h +++ b/include/asterisk/module.h @@ -269,8 +269,31 @@ void __ast_module_user_hangup_all(struct ast_module *); #define ast_module_user_remove(user) __ast_module_user_remove(ast_module_info->self, user) #define ast_module_user_hangup_all() __ast_module_user_hangup_all(ast_module_info->self) -struct ast_module *ast_module_ref(struct ast_module *); -void ast_module_unref(struct ast_module *); +struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func); +void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func); +void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func); + +/*! + * \brief Hold a reference to the module + * \param mod Module to reference + * \return mod + * + * \note A module reference will prevent the module + * from being unloaded. + */ +#define ast_module_ref(mod) __ast_module_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__) +/*! + * \brief Prevent unload of the module before shutdown + * \param mod Module to hold + * + * \note This should not be balanced by a call to ast_module_unref. + */ +#define ast_module_shutdown_ref(mod) __ast_module_shutdown_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__) +/*! + * \brief Release a reference to the module + * \param mod Module to release + */ +#define ast_module_unref(mod) __ast_module_unref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__) #if defined(__cplusplus) || defined(c_plusplus) #define AST_MODULE_INFO(keystr, flags_to_set, desc, load_func, unload_func, reload_func, load_pri) \ diff --git a/main/loader.c b/main/loader.c index 24ab24bc8f18c2463caa6096aa8102762923380e..c4d438a3d9b5f6bf9bd0e5e8f952714f9c5343a9 100644 --- a/main/loader.c +++ b/main/loader.c @@ -92,12 +92,17 @@ static unsigned int embedding = 1; /* we always start out by registering embedde struct ast_module { const struct ast_module_info *info; +#ifdef REF_DEBUG + /* Used to get module references into REF_DEBUG logs */ + void *ref_debug; +#endif void *lib; /* the shared lib, or NULL if embedded */ int usecount; /* the number of 'users' currently in this module */ struct module_user_list users; /* the list of users in the module */ struct { unsigned int running:1; unsigned int declined:1; + unsigned int keepuntilshutdown:1; } flags; AST_LIST_ENTRY(ast_module) entry; char resource[0]; @@ -161,6 +166,9 @@ void ast_module_register(const struct ast_module_info *info) } mod->info = info; +#ifdef REF_DEBUG + mod->ref_debug = ao2_t_alloc(0, NULL, info->name); +#endif AST_LIST_HEAD_INIT(&mod->users); /* during startup, before the loader has been initialized, @@ -206,6 +214,9 @@ void ast_module_unregister(const struct ast_module_info *info) if (mod) { AST_LIST_HEAD_DESTROY(&mod->users); +#ifdef REF_DEBUG + ao2_cleanup(mod->ref_debug); +#endif ast_free(mod); } } @@ -225,6 +236,10 @@ struct ast_module_user *__ast_module_user_add(struct ast_module *mod, struct ast AST_LIST_INSERT_HEAD(&mod->users, u, entry); AST_LIST_UNLOCK(&mod->users); +#ifdef REF_DEBUG + ao2_ref(mod->ref_debug, +1); +#endif + ast_atomic_fetchadd_int(&mod->usecount, +1); ast_update_use_count(); @@ -249,6 +264,10 @@ void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u) return; } +#ifdef REF_DEBUG + ao2_ref(mod->ref_debug, -1); +#endif + ast_atomic_fetchadd_int(&mod->usecount, -1); ast_free(u); @@ -264,6 +283,11 @@ void __ast_module_user_hangup_all(struct ast_module *mod) if (u->chan) { ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD); } + +#ifdef REF_DEBUG + ao2_ref(mod->ref_debug, -1); +#endif + ast_atomic_fetchadd_int(&mod->usecount, -1); ast_free(u); } @@ -544,10 +568,22 @@ void ast_module_shutdown(void) mod->info->unload(); } AST_LIST_HEAD_DESTROY(&mod->users); +#ifdef REF_DEBUG + ao2_cleanup(mod->ref_debug); +#endif free(mod); somethingchanged = 1; } AST_LIST_TRAVERSE_SAFE_END; + if (!somethingchanged) { + AST_LIST_TRAVERSE(&module_list, mod, entry) { + if (mod->flags.keepuntilshutdown) { + ast_module_unref(mod); + mod->flags.keepuntilshutdown = 0; + somethingchanged = 1; + } + } + } } while (somethingchanged && !final); AST_LIST_UNLOCK(&module_list); @@ -1324,24 +1360,60 @@ int ast_loader_unregister(int (*v)(void)) return cur ? 0 : -1; } -struct ast_module *ast_module_ref(struct ast_module *mod) +struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func) { if (!mod) { return NULL; } +#ifdef REF_DEBUG + __ao2_ref_debug(mod->ref_debug, +1, "", file, line, func); +#endif + ast_atomic_fetchadd_int(&mod->usecount, +1); ast_update_use_count(); return mod; } -void ast_module_unref(struct ast_module *mod) +void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func) +{ + if (!mod->flags.keepuntilshutdown) { + __ast_module_ref(mod, file, line, func); + mod->flags.keepuntilshutdown = 1; + } +} + +void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func) { if (!mod) { return; } +#ifdef REF_DEBUG + __ao2_ref_debug(mod->ref_debug, -1, "", file, line, func); +#endif + ast_atomic_fetchadd_int(&mod->usecount, -1); ast_update_use_count(); } + + + +/* The following exists for ABI compatibility */ +#undef ast_module_ref +#undef ast_module_unref + +struct ast_module *ast_module_ref(struct ast_module *mod); +void ast_module_unref(struct ast_module *mod); + +struct ast_module *ast_module_ref(struct ast_module *mod) +{ + return __ast_module_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__); +} + +void ast_module_unref(struct ast_module *mod) +{ + __ast_module_unref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__); +} +