Page MenuHomePhabricator

Find a solution for loading MITK modules which are not linked from anywhere
Closed, ResolvedPublic

Description

MITK modules (in general any shared library) which register some entities (e.g. object factories, micro services, etc.) during static initialisation of the library currently need to be explicitly linked from a library which is loaded at application or plug-in start-up in order to guarantee the registration of these entities.

This means we currently have artificial link-time dependencies by calling or instantiating some piece of code in the MITK module which needs to register stuff.

With increasing use of the C++ Micro Services library, this scenario will be encountered more frequently in the future, for example specific ToF Hardware devices will be implemented in individual MITK modules and registered as micro service in the modules activator (during static initialisation of the module). Explicit loading of such modules at runtime is needed to guarantee the registration of such module, since no other module has (needs to have) link-time dependencies on these "hardware device modules".

Note that this problem also exists in the CTK Plugin Framework (or for any OSGi or generally plug-in based system). In a OSGi system, this problem is usually solved by using some configuration data (provisioning) specifying the set of plug-ins for the running application.

In the case of the C++ Micro Services, I would propose the following solution:

After the micro services library called the activator of a module, it searches a sub-folder located relative to the current module location and named after the current module for additional libraries to load. All libraries in this sub-folder will then be loaded at runtime by the micro services library, which may in turn trigger loading of libraries in deeper nested directories. The idea of searching specifically named directories for modules (plug-ins) is also used by Qt itself for designer, sql, image formats, etc. plug-ins.

Pros:

  • Centralised solution, no changes to current modules needed
  • No external configuration data needed
  • External modules (e.g. ToF hardware device implementations) can be loaded in the same way

Cons:

  • Only one search location per module (if needed, we could create a configurable search path somehow)
  • Module link-dependencies to modules in sub-folders not allowed (unless one modifies the search path of the dynamic library loader)

Event Timeline

A sequence diagram showing the auto-load feature of modules.

The same auto-loading mechanism can be triggered when invoking an executable by supplying that executable with the proper micro services initialisation code such that Step 5 takes place when loading the executable. The module search path can be customised in the initialisation code.

MicroServices_Autoloading.jpg (476×1 px, 58 KB)

Example source tree layout:

Modules/

|---- MitkExt/
|---- ToFHardware/
|         |--------- ToFDevice1/ (links to ToFHardware)
|         |--------- ToFDevice2/ (links to ToFHardware)
|---- QmitkExt/
etc.

Example binary layout with sub-folders according to module names:

bin/

--- libMitkExt.so
--- libMitkToFHardware.so
--- libQmitkExt.so
--- MitkToFHardware/
----------- libMitkToFDevice1.so
----------- libMitkToFDevice2.so

Looks good to me.

A few remarks:

  • I think we need additional search locations from the beginning. There are a lot of projects where micro services defined in the public MITK will be extended. E.g. in Sascha's example: someone wants to add another ToF device in another project without modifying MITK source code and without copying files to the MITK build tree.
  • having multiple options like a plugins folder relative to the executable and environment variable for specifying additional plugin directories could lead to undesired application behaviour since old plugins could be loaded into a newer application, especially during development time. We should think of some mechanisms to lower the risk for that: maybe compare file times of the dlls (service implementations should be newer than service itself), enable a "safe mode" for the application via command line (no plugin loading at all).-
  • on Windows, qt uses library name suffixes for the debug/release problem. Should we adopt that or stick with <BUILD_TYPE> subdirectories?

(In reply to comment #3)

  • I think we need additional search locations from the beginning. There are

a lot of projects where micro services defined in the public MITK will be
extended. E.g. in Sascha's example: someone wants to add another ToF device
in another project without modifying MITK source code and without copying
files to the MITK build tree.

Agreed. We could automatically add the base directory of the running executable to the search list. This would solve probably most of our cases already. Additional search paths could be specified in an environment variable.

  • having multiple options like a plugins folder relative to the executable

and environment variable for specifying additional plugin directories could
lead to undesired application behaviour since old plugins could be loaded
into a newer application, especially during development time. We should
think of some mechanisms to lower the risk for that: maybe compare file
times of the dlls (service implementations should be newer than service
itself), enable a "safe mode" for the application via command line (no
plugin loading at all).-

Right. While I like the file timestamp idea, it might not always be applicable. If the service impl. module does not link to the module for which it should be auto-loaded, the time-stamp might be older than required but still okay. This could happen if the service module uses interfaces from another module, or the interfaces do not require a link-time dependency (although they generally should provide an out-of-line virtual destructor).

"Safe mode" sounds good to me. But I think we need to use an environment variable for this, since the executable might trigger auto-loading of modules during its initialization before we get a chance to parse the command line.

  • on Windows, qt uses library name suffixes for the debug/release problem.

Should we adopt that or stick with <BUILD_TYPE> subdirectories?

For the time being, I would stick with BUILD_TYPE subdirs. I already have a search heuristic to find modules in the appropriate BUILD_TYPE sub-directory.

Marco and I identified the following issues/todos:

  • Provide a "safe mode", enabled via an environment variable or command line argument.
  • Generate batch/shell scripts for external projects which just build modules to correctly set up the module search path and launch the mitkWorkbench executable.
  • We do not compile verification keys, version numbers, etc. into modules.
  • When loading a module at runtime, try to use all mechanisms provided by the OS library loader to ensure correct functioning of the module (e.g. have a look at RTLD_NOW).
  • Create one meta-target per module (if it has loadable add-on modules) which builds the module and its add-on modules. Plug-ins get a dependency to this meta-target by using a naming convention for the meta-target (related to the module target name).
  • Adapt the MITK_CREATE_MODULE macro to put the add-on modules in the proper sub-directories (need to provide the name of the main module to the macro) and add some meta-information for install support.
  • Adapt the MITK install macros to glob and install all add-on modules needed by modules which are required by the installed plug-ins.

I think you have this covered, but I'm not sure. A very important scenario for us would be to load Modules from MBI to MITK.

Your example would look like this:

MITK-Source/Modules/

|---- MitkExt/
|---- ToFHardware/
|         |--------- ToFDevice1/ (links to ToFHardware)
|         |--------- ToFDevice2/ (links to ToFHardware)
|---- QmitkExt/
etc.

MBI-Source/mbi/Modules/

|---- ToFHardwareMBI/
|         |--------- ToFDeviceMBI1/ (links to ToFHardware)
etc.

We definitely need this for our current work. I'm also not sure if this is important, but everything is used in a plugin (org.mitk.gui.qt.tofutil). Please let me know if this is not covered by your current plan :).

In your scenario, would you run the mitkWorkbench or MBIApp executable?

I think the MBIApp. In what way is that important?

New remote branch pushed: bug-13040-support-for-module-auto-loading

The pushed branch implements the following features:

  • Auto-loading of modules belonging to a certain "context" (sub-directory)
  • Configuration of auto-loading behaviour via static methods or environment variables ("safe mode" and adding auto-load paths)
  • Build system support for creating and installing "auto-load modules"
  • Automatic generation of "meta-targets" for each context, depending on all auto-load modules within that context

Not implemented:

  • Command line argument for "safe mode". This is currently hard to implement in a non-hackish way (use environment variable instead).
  • CMake macro for creating a batch/shell script for external projects contributing only modules to reuse the mitkWorkbench executable

The last point will be part of a new bug, related to moving to one central executable (mitkWorkbench) which is started from external projects via scripts to provide customizations and plug-in configurations.

I think we are good to go.

[9d035f]: Merge branch 'bug-13040-support-for-module-auto-loading'

Merged commits:

2012-11-01 17:20:59 Sascha Zelzer [af441f]
Fixed Apple specific issues with module auto-load paths.


2012-10-30 14:46:54 Sascha Zelzer [52eebc]
Added auto-load module meta targets and install support.


2012-10-30 14:32:35 Sascha Zelzer [79c503]
Enable module auto-loading and add the executable path to the search paths.


2012-10-30 14:31:13 Sascha Zelzer [25d8e0]
Support for environment variables controlling auto-loading of modules.


2012-10-29 09:40:40 Sascha Zelzer [bf8ce8]
Improved CppMicroServices CTest script. Added Travis support.


2012-10-30 14:59:59 Sascha Zelzer [3a4d17]
Added module auto-load feature.


2012-10-29 09:29:55 Sascha Zelzer [911b3e]
Added header file and cleaned up order of entries.


2012-10-29 09:29:13 Sascha Zelzer [a3f1ac]
Documentation fixes for CppMicroServices.


2012-10-29 09:24:57 Sascha Zelzer [61593d]
Proper support for static modules plus small fixes.


2012-10-28 23:20:32 Sascha Zelzer [147c5c]
Merge branches 'bug-13198-hide-baseclass-header' and 'bug-13140-serviceregistration-leak', remote-tracking branch 'origin/bug-13003-fix-microservices-member-function-listener' into bug-13040-support-for-module-auto-loading

[a92914]: Merge branch 'bug-13040-support-for-module-auto-loading'

Merged commits:

2012-11-02 16:47:48 Sascha Zelzer [6478fb]
COMP: Do not use CMake features from CMake > 2.8.4.