0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-09 21:48:55 +02:00

ircd::mods: Improve/reorg internal interface related.

This commit is contained in:
Jason Volk 2019-07-13 16:38:00 -07:00
parent 55a70915a6
commit 84d1749f4d
2 changed files with 106 additions and 94 deletions

View file

@ -56,12 +56,6 @@ decltype(ircd::mods::mod::loaded)
ircd::mods::mod::loaded
{};
// Allows module to communicate static destruction is taking place when mapi::header
// destructs. If dlclose() returns without this being set, dlclose() lied about really
// unloading the module. That is considered a "stuck" module.
bool
ircd::mapi::static_destruction;
const char *const
ircd::mapi::import_section_names[]
{
@ -70,6 +64,104 @@ ircd::mapi::import_section_names[]
nullptr
};
// Allows module to communicate static destruction is taking place when mapi::header
// destructs. If dlclose() returns without this being set, dlclose() lied about really
// unloading the module. That is considered a "stuck" module.
bool
ircd::mapi::static_destruction;
bool
ircd::mods::unload(mod &mod)
{
if(!mod.handle.is_loaded())
return false;
if(mods::unloading(mod.name()))
return false;
// Mark this module in the unloading state.
mod.unloading.emplace_front(&mod);
log::debug
{
log, "Attempting unload module '%s' @ `%s'",
mod.name(),
mod.location()
};
// Call the user's unloading function here.
assert(mod.header);
assert(mod.header->meta);
if(mod.header->meta->fini)
mod.header->meta->fini();
// Save the children! dlclose() does not like to be called recursively during static
// destruction of a module. The mod ctor recorded all of the modules loaded while this
// module was loading so we can reverse the record and unload them here.
// Note: If the user loaded more modules from inside their module they will have to dtor them
// before 'static destruction' does. They can also do that by adding them to this vector.
std::for_each(rbegin(mod.children), rend(mod.children), []
(auto *const &ptr)
{
// Only trigger an unload if there is one reference remaining to the module,
// in addition to the reference created by invoking shared_from() right here.
if(shared_from(*ptr).use_count() <= 2)
unload(*ptr);
});
log::debug
{
log, "Attempting static unload for '%s' @ `%s'",
mod.name(),
mod.location()
};
mapi::static_destruction = false;
mod.handle.unload();
assert(!mod.handle.is_loaded());
mod.loaded.erase(mod.name());
mod.unloading.remove(&mod);
if(!mapi::static_destruction)
{
handle_stuck(mod);
return false;
}
else log::info
{
log, "Unloaded '%s'", mod.name()
};
return true;
}
void
ircd::mods::handle_stuck(mod &mod)
{
log::critical
{
log, "Module \"%s\" is stuck and failing to unload.",
mod.name()
};
}
void
ircd::mods::handle_ebadf(const string_view &what)
{
const auto pos(what.find("undefined symbol: "));
if(pos == std::string_view::npos)
return;
const auto msg(what.substr(pos));
const auto mangled(between(msg, ": ", ")"));
const auto demangled(demangle(mangled));
throw undefined_symbol
{
"undefined symbol '%s' (%s)",
demangled,
mangled
};
}
//
// mods::mod::mod
//
@ -225,7 +317,7 @@ catch(const boost::system::system_error &e)
ircd::mods::mod::~mod()
noexcept try
{
unload();
unload(*this);
}
catch(const std::exception &e)
{
@ -240,71 +332,6 @@ catch(const std::exception &e)
return;
}
bool
ircd::mods::mod::unload()
{
if(!handle.is_loaded())
return false;
if(mods::unloading(name()))
return false;
// Mark this module in the unloading state.
unloading.emplace_front(this);
log::debug
{
log, "Attempting unload module '%s' @ `%s'",
name(),
location()
};
// Call the user's unloading function here.
assert(header);
assert(header->meta);
if(header->meta->fini)
header->meta->fini();
// Save the children! dlclose() does not like to be called recursively during static
// destruction of a module. The mod ctor recorded all of the modules loaded while this
// module was loading so we can reverse the record and unload them here.
// Note: If the user loaded more modules from inside their module they will have to dtor them
// before 'static destruction' does. They can also do that by adding them to this vector.
std::for_each(rbegin(this->children), rend(this->children), []
(mod *const &ptr)
{
// Only trigger an unload if there is one reference remaining to the module,
// in addition to the reference created by invoking shared_from() right here.
if(shared_from(*ptr).use_count() <= 2)
ptr->unload();
});
log::debug
{
log, "Attempting static unload for '%s' @ `%s'",
name(),
location()
};
mapi::static_destruction = false;
handle.unload();
assert(!handle.is_loaded());
loaded.erase(name());
unloading.remove(this);
if(!mapi::static_destruction)
log::critical
{
"Module \"%s\" is stuck and failing to unload.", name()
};
else
log::info
{
log, "Unloaded '%s'", name()
};
return true;
}
ircd::string_view &
ircd::mods::mod::operator[](const string_view &key)
{
@ -320,24 +347,6 @@ const
return header->operator[](key);
}
void
ircd::mods::handle_ebadf(const string_view &what)
{
const auto pos(what.find("undefined symbol: "));
if(pos == std::string_view::npos)
return;
const auto msg(what.substr(pos));
const auto mangled(between(msg, ": ", ")"));
const auto demangled(demangle(mangled));
throw undefined_symbol
{
"undefined symbol '%s' (%s)",
demangled,
mangled
};
}
///////////////////////////////////////////////////////////////////////////////
//
// mods/mapi.h

View file

@ -10,12 +10,16 @@
namespace ircd::mods
{
extern const std::string prefix;
extern const std::string suffix;
struct mod;
template<class R, class F> R info(const string_view &, F&& closure);
void handle_ebadf(const string_view &what);
void handle_stuck(mod &);
bool unload(mod &);
extern const std::string prefix;
extern const std::string suffix;
}
/// Internal module representation. This object closely wraps the dlopen()
@ -45,6 +49,7 @@ struct ircd::mods::mod
const std::string _location;
mapi::header *header;
public:
// Metadata
const string_view &operator[](const string_view &s) const;
string_view &operator[](const string_view &s);
@ -55,8 +60,6 @@ struct ircd::mods::mod
auto &version() const { return header->version; }
auto &description() const { return (*this)["description"]; }
bool unload();
explicit mod(std::string path, const load_mode::type &);
mod(mod &&) = delete;