$darkmode
Public Member Functions | |
| Plugin (const std::string &plugin_path, const std::string &name="") | |
| Plugin (const PluginManifest &m, std::function< ModelFactory *()> fn, const std::string &name="") | |
| std::string | path () const |
| std::string | name () const |
| std::string | type () const |
| std::string | type_version () const |
| std::string | required_type_version () const |
| Schema | schema () const |
| bool | is_builtin () const |
| bool | is_type_known () const |
| bool | is_compatible () const |
| template<typename F > | |
| std::unique_ptr< F > | make () const |
Friends | |
| void | to_json (Json &j, const Plugin &p) |
|
explicit |
Construct a Plugin by loading a dynamic library from disk.
The following call is used to load the plugin:
dlopen(plugin_path, RTLD_GLOBAL | RTLD_DEEPBIND | RTLD_NOW)
Discussion
Currently, this opens a plugin with the dlopen() call (provided by glibc). This constrains official support to Linux.
There are several ways we can open a plugin. Ideally, we would use:
dlmopen(LM_ID_NEWLM, plugin_path, RTLD_GLOBAL | RTLD_NOW)
This would open each plugin within its own namespace, which prevents one plugin from affecting another plugin. However, glibc does not support the use of the RTLD_GLOBAL mode in dlmopen(), and there is currently only support for up to 16 namespaces. See the following RFC:
RFC: Treat RTLD_GLOBAL as unique to namespace when used with dlmopen https://patchwork.ozlabs.org/project/glibc/patch/55A73673.3060104@redhat.com/
Unfortunately, this RFC hasn't seen much activity since 2015, so it is unlikely to merged soon, and even if it was, we wouldn't be able to use it until all platforms and distributions we support have that version.
The alternative to using dlmopen() is to use the non-namespaced dlopen():
dlopen(plugin_path, RTLD_LOCAL | RTLD_NOW)
With the local scope as achieved with RTLD_LOCAL, we prevent plugins from interfering with each other, but there is still a major problem with this approach: dependencies of a plugin are prevented from sharing symbols, which leads to runtime errors. Fixing this requires RTLD_GLOBAL:
dlopen(plugin_path, RTLD_GLOBAL | RTLD_NOW)
This causes problems combining plugins that include conflicting versions of libraries though, so we need to add the RTLD_DEEPLINK flag to cause libraries to prefer local symbols during resolution.
Note: The use of RTLD_NOW as opposed to RTLD_LAZY makes loader errors occur when we load the plugin as opposed to when the plugin is invoked. This comes at a performance penalty as all plugins will be loaded regardless of whether they are used. However, this moves errors to a point before a simulation occurs, which is preferable. In the future, we may lazily load plugins and then load them again once it is clear they will partake in the simulation.
| cloe::Plugin::Plugin | ( | const PluginManifest & | m, |
| std::function< ModelFactory *()> | fn, | ||
| const std::string & | name = "" |
||
| ) |
Construct a Plugin from a Plugin-compatible type itself.
This does not involve dlopen().
| bool cloe::Plugin::is_builtin | ( | ) | const |
Return whether this plugin is builtin (as opposed to loaded from disk).
| bool cloe::Plugin::is_compatible | ( | ) | const |
Return whether this plugin is compatible with Cloe.
This should be checked before creating any objects with the factory function from the plugin. Deviation results in undefined behavior.
| bool cloe::Plugin::is_type_known | ( | ) | const |
Return whether this plugin type is known to Cloe.
|
inline |
Attempt to cast this component to a sub-type.
|
inline |
Return the given or intrinsic name of the plugin.
|
inline |
Return the path to the loaded dynamic library.
| std::string cloe::Plugin::required_type_version | ( | ) | const |
Return the version that the Cloe library expects the plugin to have.
| Schema cloe::Plugin::schema | ( | ) | const |
Return the schema of this plugin.
|
inline |
Return the plugin type.
|
inline |
Return the API version of the plugin.