Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically load LAMMPS plugins if they are in folders listed in LAMMPS_PLUGIN_PATH environment variable #3170

Merged
merged 4 commits into from Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 31 additions & 18 deletions doc/src/Developer_plugins.rst
Expand Up @@ -8,11 +8,20 @@ without recompiling LAMMPS. The functionality for this and the

Plugins use the operating system's capability to load dynamic shared
object (DSO) files in a way similar shared libraries and then reference
specific functions in those DSOs. Any DSO file with plugins has to include
an initialization function with a specific name, "lammpsplugin_init", that
has to follow specific rules described below. When loading the DSO with
the "plugin" command, this function is looked up and called and will then
register the contained plugin(s) with LAMMPS.
specific functions in those DSOs. Any DSO file with plugins has to
include an initialization function with a specific name,
"lammpsplugin_init", that has to follow specific rules described below.
When loading the DSO with the "plugin" command, this function is looked
up and called and will then register the contained plugin(s) with
LAMMPS.

When the environment variable ``LAMMPS_PLUGIN_PATH`` is set, then LAMMPS
will search the directory (or directories) listed in this path for files
with names that end in ``plugin.so`` (e.g. ``helloplugin.so``) and will
try to load the contained plugins automatically at start-up. For
plugins that are loaded this way, the behavior of LAMMPS should be
identical to a binary where the corresponding code was compiled in
statically as a package.

From the programmer perspective this can work because of the object
oriented design of LAMMPS where all pair style commands are derived from
Expand Down Expand Up @@ -65,19 +74,18 @@ Members of ``lammpsplugin_t``
* - handle
- Pointer to the open DSO file handle

Only one of the three alternate creator entries can be used at a time
and which of those is determined by the style of plugin. The
"creator.v1" element is for factory functions of supported styles
computing forces (i.e. command, pair, bond, angle, dihedral, or
improper styles) and the function takes as single argument the pointer
to the LAMMPS instance. The factory function is cast to the
``lammpsplugin_factory1`` type before assignment. The "creator.v2"
element is for factory functions creating an instance of a fix, compute,
or region style and takes three arguments: a pointer to the LAMMPS
instance, an integer with the length of the argument list and a ``char
**`` pointer to the list of arguments. The factory function pointer
needs to be cast to the ``lammpsplugin_factory2`` type before
assignment.
Only one of the two alternate creator entries can be used at a time and
which of those is determined by the style of plugin. The "creator.v1"
element is for factory functions of supported styles computing forces
(i.e. pair, bond, angle, dihedral, or improper styles) or command styles
and the function takes as single argument the pointer to the LAMMPS
instance. The factory function is cast to the ``lammpsplugin_factory1``
type before assignment. The "creator.v2" element is for factory
functions creating an instance of a fix, compute, or region style and
takes three arguments: a pointer to the LAMMPS instance, an integer with
the length of the argument list and a ``char **`` pointer to the list of
arguments. The factory function pointer needs to be cast to the
``lammpsplugin_factory2`` type before assignment.

Pair style example
^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -249,3 +257,8 @@ by ``#ifdef PAIR_CLASS`` is not needed, since the mapping of the class
name to the style name is done by the plugin registration function with
the information from the ``lammpsplugin_t`` struct. It may be included
in case the new code is intended to be later included in LAMMPS directly.

A plugin may be registered under an existing style name. In that case
the plugin will override the existing code. This can be used to modify
the behavior of existing styles or to debug new versions of them without
having to recompile/reinstall all of LAMMPS.
5 changes: 5 additions & 0 deletions doc/src/Packages_details.rst
Expand Up @@ -2154,6 +2154,11 @@ A :doc:`plugin <plugin>` command that can load and unload several
kind of styles in LAMMPS from shared object files at runtime without
having to recompile and relink LAMMPS.

When the environment variable ``LAMMPS_PLUGIN_PATH`` is set, then LAMMPS
will search the directory (or directories) listed in this path for files
with names that end in ``plugin.so`` (e.g. ``helloplugin.so``) and will
try to load the contained plugins automatically at start-up.

**Authors:** Axel Kohlmeyer (Temple U)

**Supporting info:**
Expand Down
9 changes: 9 additions & 0 deletions doc/src/plugin.rst
Expand Up @@ -56,6 +56,15 @@ styles and names.

The *clear* command will unload all currently loaded plugins.

.. admonition:: Automatic loading of plugins
:class: note

When the environment variable ``LAMMPS_PLUGIN_PATH`` is set, then
LAMMPS will search the directory (or directories) listed in this path
for files with names that end in ``plugin.so``
(e.g. ``helloplugin.so``) and will try to load the contained plugins
automatically at start-up.


Restrictions
""""""""""""
Expand Down
1 change: 1 addition & 0 deletions doc/utils/sphinx-config/false_positives.txt
Expand Up @@ -1926,6 +1926,7 @@ Mayoral
mbt
MBytes
mc
mcmoves
McLachlan
md
mdf
Expand Down
36 changes: 26 additions & 10 deletions src/PLUGIN/plugin.cpp
Expand Up @@ -34,6 +34,8 @@ static std::list<lammpsplugin_t> pluginlist;
// map for counting references to dso handles
static std::map<void *, int> dso_refcounter;

static bool verbose = true;

/* ---------------------------------------------------------------------- */

Plugin::Plugin(LAMMPS *lmp) : Command(lmp) {}
Expand Down Expand Up @@ -67,15 +69,26 @@ void Plugin::command(int narg, char **arg)
}
} else
error->all(FLERR, "Illegal plugin command");
#if 0
if (comm->me == 0)
error->warning(
FLERR, "LAMMPS must be built as a shared library for it to work.");
}

// auto-load DSOs from designated folder(s)
void plugin_auto_load(LAMMPS *lmp)
{
#if defined(LMP_PLUGIN)
for (const auto &plugin_dir : platform::list_pathenv("LAMMPS_PLUGIN_PATH")) {
verbose = false;
int count = 0;
for (const auto &file : platform::list_directory(plugin_dir)) {
if (utils::strmatch(file, "\\plugin.so$"))
count += plugin_load(platform::path_join(plugin_dir, file).c_str(), lmp);
}
if (lmp->comm->me == 0) utils::logmesg(lmp, "Loaded {} plugins from {}\n", count, plugin_dir);
}
#endif
}

// load DSO and call included registration function
void plugin_load(const char *file, LAMMPS *lmp)
int plugin_load(const char *file, LAMMPS *lmp)
{
#if defined(LMP_PLUGIN)
int me = lmp->comm->me;
Expand All @@ -86,7 +99,7 @@ void plugin_load(const char *file, LAMMPS *lmp)
void *dso = platform::dlopen(file);
if (dso == nullptr) {
if (me == 0) utils::logmesg(lmp, "Open of file {} failed: {}\n", file, platform::dlerror());
return;
return 0;
}

// look up lammpsplugin_init() function in DSO
Expand All @@ -100,14 +113,15 @@ void plugin_load(const char *file, LAMMPS *lmp)
if (me == 0)
utils::logmesg(lmp, "Plugin symbol lookup failure in file {}: {}\n", file,
platform::dlerror());
return;
return 0;
}

// call initializer function loaded from DSO and pass a pointer
// to the LAMMPS instance, the DSO handle (for reference counting)
// and plugin registration function pointer

(*(lammpsplugin_initfunc) (initfunc))((void *) lmp, dso, (void *) &plugin_register);
return 1;
#endif
}

Expand All @@ -129,13 +143,13 @@ void plugin_register(lammpsplugin_t *plugin, void *ptr)
// ignore load request if same plugin already loaded
int idx = plugin_find(plugin->style, plugin->name);
if (idx >= 0) {
if (me == 0)
if (verbose && (me == 0))
utils::logmesg(lmp, "Ignoring load of {} style {}: must unload existing {} plugin first\n",
plugin->style, plugin->name, plugin->name);
return;
}

if (me == 0) {
if (verbose && (me == 0)) {
utils::logmesg(lmp, "Loading plugin: {} by {}\n", plugin->info, plugin->author);
// print version info only if the versions of host and plugin don't match
if ((plugin->version) && (strcmp(plugin->version, lmp->version) != 0))
Expand Down Expand Up @@ -270,7 +284,7 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp)

// remove selected plugin from list of plugins

if (me == 0) utils::logmesg(lmp, "Unloading {} style {}\n", style, name);
if (verbose && (me == 0)) utils::logmesg(lmp, "Unloading {} style {}\n", style, name);
plugin_erase(style, name);

// remove style of given name from corresponding map
Expand Down Expand Up @@ -383,10 +397,12 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp)

void plugin_clear(LAMMPS *lmp)
{
verbose = false;
while (pluginlist.size() > 0) {
auto p = pluginlist.begin();
plugin_unload(p->style, p->name, lmp);
}
verbose = true;
}

/* --------------------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion src/PLUGIN/plugin.h
Expand Up @@ -31,7 +31,8 @@ class Plugin : public Command {
void command(int, char **) override;
};

void plugin_load(const char *, LAMMPS *);
void plugin_auto_load(LAMMPS *);
int plugin_load(const char *, LAMMPS *);
void plugin_register(lammpsplugin_t *, void *);

void plugin_unload(const char *, const char *, LAMMPS *);
Expand Down
5 changes: 5 additions & 0 deletions src/lammps.cpp
Expand Up @@ -824,6 +824,11 @@ void LAMMPS::create()
timer = new Timer(this);

python = new Python(this);

// auto-load plugins
#if defined(LMP_PLUGIN)
plugin_auto_load(this);
#endif
}

/* ----------------------------------------------------------------------
Expand Down