0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2025-03-16 22:41:46 +01:00

ircd::mods: Minor reorg: move internal section to top; cleanup terminate handler.

This commit is contained in:
Jason Volk 2018-10-24 12:15:42 -07:00
parent 96338208aa
commit 59adeb839c

View file

@ -30,6 +30,232 @@ ircd::mods::suffix
boost::dll::shared_library::suffix()
};
//
// mods::mod
//
decltype(ircd::mods::mod::loading)
ircd::mods::mod::loading
{};
decltype(ircd::mods::mod::unloading)
ircd::mods::mod::unloading
{};
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;
//
// mods::mod::mod
//
ircd::mods::mod::mod(const filesystem::path &path,
const load_mode::type &mode)
try
:path{path}
,mode{mode}
//,mangles{mods::mangles(path)}
,handle{[this, &path, &mode]
{
const auto ours([]
{
log::critical
{
log, "Error during the static construction of module (fatal) :%s",
what(std::current_exception())
};
});
const auto theirs
{
std::get_terminate()
};
const unwind reset{[this, &theirs]
{
assert(loading.front() == this);
loading.pop_front();
std::set_terminate(theirs);
}};
loading.emplace_front(this);
std::set_terminate(ours);
return boost::dll::shared_library{path, mode};
}()}
,_name
{
unpostfixed(handle.location().filename().string())
}
,_location
{
handle.location().string()
}
,header
{
&handle.get<mapi::header>(mapi::header_symbol_name)
}
{
log::debug
{
log, "Loaded static segment of '%s' @ `%s' with %zu symbols",
name(),
location(),
mangles.size()
};
if(unlikely(!header))
throw error
{
"Unexpected null header"
};
if(header->magic != mapi::MAGIC)
throw error
{
"Bad magic [%04x] need: [%04x]", header->magic, mapi::MAGIC
};
// Tell the module where to find us.
header->self = this;
// Set some basic metadata
auto &meta(header->meta);
meta["name"] = name();
meta["location"] = location();
if(!loading.empty())
{
const auto &m(mod::loading.front());
m->children.emplace_back(this);
log::debug
{
log, "Module '%s' recursively loaded by '%s'",
name(),
m->path.filename().string()
};
}
// Without init exception, the module is now considered loaded.
assert(!loaded.count(name()));
loaded.emplace(name(), this);
}
catch(const boost::system::system_error &e)
{
switch(e.code().value())
{
case boost::system::errc::bad_file_descriptor:
{
const string_view what(e.what());
const auto pos(what.find("undefined symbol: "));
if(pos == std::string_view::npos)
break;
const string_view msg(what.substr(pos));
const std::string mangled(between(msg, ": ", ")"));
const std::string demangled(demangle(mangled));
throw error
{
"undefined symbol: '%s' (%s)", demangled, mangled
};
}
default:
break;
}
throw error
{
"%s", string(e)
};
}
ircd::mods::mod::~mod()
noexcept try
{
unload();
}
catch(const std::exception &e)
{
log::critical
{
log, "Module @%p unload: %s",
(const void *)this,
e.what()
};
if(!ircd::debugmode)
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.
if(header->fini)
header->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());
log.critical("Module \"%s\" may result in undefined behavior if not fixed.", name());
}
else log::info
{
log, "Unloaded '%s'", name()
};
return true;
}
///////////////////////////////////////////////////////////////////////////////
//
// mods/mods.h (misc util)
@ -704,236 +930,3 @@ const
{
return std::find(begin(), end(), dir) != end();
}
///////////////////////////////////////////////////////////////////////////////
//
// (internal) mods.h
//
decltype(ircd::mods::mod::loading)
ircd::mods::mod::loading
{};
decltype(ircd::mods::mod::unloading)
ircd::mods::mod::unloading
{};
decltype(ircd::mods::mod::loaded)
ircd::mods::mod::loaded
{};
ircd::mods::mod::mod(const filesystem::path &path,
const load_mode::type &mode)
try
:path{path}
,mode{mode}
//,mangles{mods::mangles(path)}
,handle{[this, &path, &mode]
{
const auto theirs
{
std::get_terminate()
};
const auto ours([]
{
log::critical
{
log, "std::terminate() called during the static construction of a module."
};
if(std::current_exception()) try
{
std::rethrow_exception(std::current_exception());
}
catch(const std::exception &e)
{
log::error
{
log, "%s", e.what()
};
}
});
const unwind reset{[this, &theirs]
{
assert(loading.front() == this);
loading.pop_front();
std::set_terminate(theirs);
}};
loading.emplace_front(this);
std::set_terminate(ours);
return boost::dll::shared_library{path, mode};
}()}
,_name
{
unpostfixed(handle.location().filename().string())
}
,_location
{
handle.location().string()
}
,header
{
&handle.get<mapi::header>(mapi::header_symbol_name)
}
{
log::debug
{
log, "Loaded static segment of '%s' @ `%s' with %zu symbols",
name(),
location(),
mangles.size()
};
if(unlikely(!header))
throw error
{
"Unexpected null header"
};
if(header->magic != mapi::MAGIC)
throw error
{
"Bad magic [%04x] need: [%04x]", header->magic, mapi::MAGIC
};
// Tell the module where to find us.
header->self = this;
// Set some basic metadata
auto &meta(header->meta);
meta["name"] = name();
meta["location"] = location();
if(!loading.empty())
{
const auto &m(mod::loading.front());
m->children.emplace_back(this);
log::debug
{
log, "Module '%s' recursively loaded by '%s'",
name(),
m->path.filename().string()
};
}
// Without init exception, the module is now considered loaded.
assert(!loaded.count(name()));
loaded.emplace(name(), this);
}
catch(const boost::system::system_error &e)
{
switch(e.code().value())
{
case boost::system::errc::bad_file_descriptor:
{
const string_view what(e.what());
const auto pos(what.find("undefined symbol: "));
if(pos == std::string_view::npos)
break;
const string_view msg(what.substr(pos));
const std::string mangled(between(msg, ": ", ")"));
const std::string demangled(demangle(mangled));
throw error
{
"undefined symbol: '%s' (%s)", demangled, mangled
};
}
default:
break;
}
throw error
{
"%s", string(e)
};
}
// 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;
ircd::mods::mod::~mod()
noexcept try
{
unload();
}
catch(const std::exception &e)
{
log::critical
{
log, "Module @%p unload: %s",
(const void *)this,
e.what()
};
if(!ircd::debugmode)
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.
if(header->fini)
header->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());
log.critical("Module \"%s\" may result in undefined behavior if not fixed.", name());
}
else log::info
{
log, "Unloaded '%s'", name()
};
return true;
}