Hi all, As discussed in the last meeting, here is a document describing how I think plugins should be loaded, run and unloaded by the daemon. It will be integrated into the wiki once it has been agreed upon. I will follow up with a mail later this week describing how events are passed between plugins. But I reckon this is enough for now.
Read the text below and let me know what you think. Some of this is implemented in my test branch, so if you like you can check it out, build it, read the doxygen docs etc. Short howto: # svn co http://svn.licq.org/svn/branches/erijo-dev # cd erijo-dev && mkdir build && cd build # cmake .. && make # licq/src/licq testplugin/src # make doxy # <my browser> ../licq/doxy/html/index.html Now, go make some coffee and start reading :) When the daemon starts, it scans a number of directories (e.g. ~/.licq/plugins, /usr/lib/licq) looking for files matching the pattern liblicq*.so or licq*.so. All matching files are loaded with dlopen (using [1]). Once opened, the symbol licqGetPluginApiVersion is loaded (see [2]). This symbol, assumed to be a function returning the API version the plugin was built with, is executed. The return value is required to be the same as the one the daemon was built against. If not, the library is unloaded. Every time a change breaking the ABI is done, the API version number is incremented (see [3]). If the versions match, the daemon continues by loading the symbols licqCreatePluginFactory and licqDestroyPluginFactory (see [2]). The factory returned from licqCreatePluginFactory() (see the class IPluginFactory in [2]) is responsible for creating the plugins contained within the dynamic library. This means that one library may contain any number of plugins (IPluginFactory::getPluginCount()). See [4] for an implementation of plugin loading. Once the factory has been created (by the daemon calling licqCreatePluginFactory()), the daemon calls IPluginFactory::getPluginInformation(index) for all index in [0..IPluginFactory::getPluginCount() - 1], saving the returned TPluginInformation (see [5]) in memory. If a plugin with the same name already is in memory, the daemon discards the plugin with the lowest IntVersion. Once all plugins are loaded, the daemon will have a mapping from a plugin's name to its factory and the index within this factory (see [6]). When someone (e.g. the user or another plugin) wish to start a plugin, it uses the plugin's name (e.g. Qt-gui) and the daemon asks the factory to create the plugin (IPluginFactory::createPlugin() [2]). This function may be called several times with the same index and must return different instances each time (allocated on the heap) since the daemon will delete the plugin when it is no longer needed. On error, e.g. if there can only be one instance of a plugin running, createPlugin must return NULL. When the plugin has been created, the daemon will call IPlugin::init() (unfinished interface, see [7]) in which the plugin should read in its configuration and do any other kind of configuration that might fail. If init() returns false, the plugin will be deleted by the daemon. If init() succeeds, the plugin should return true. When init is done, the daemon will create a new thread and run IPlugin::run() in this new thread. When a plugin has started, the instance is identified by a TPluginId. This id is only used during runtime and should not be saved to disc since it is not guaranteed to be the same the next time the plugin is started. The plugin then sends and receives events (to be described in another document) until it is time to stop. A plugin can be asked to stop at the request of another plugin or because Licq is about to exit (before Licq exits, the daemon will stop all plugins). The daemon will send a stop message to the plugin and give it some time to exit cleanly. If the plugin has not exited (i.e. returned from IPlugin::run()) within this time, the thread will be killed. When the thread has stopped, the daemon will delete the plugin instance. Before the daemon exits, after stopping all plugins, licqDestroyPluginFactory() will be called for all factories and all dynamic libraries will be closed. All libraries containing plugins that can be created will be kept loaded until the daemon exits. This might seem unnecessary and look like a waste of memory, but I don't think it will be a problem. Correct me if I'm wrong, but my understanding is that only the parts of a library that are used are kept in memory. As long as no plugin is created, this will only be the code for IPluginFactory (perhaps a single page in memory). But if this is shown to be a problem, another scheme could be implemented where all TPluginInformation structs are cached on disc. The dynamic library is only loaded if it has changed and the plugin informations should be updated or if a plugin should be started that resides in the library. But IMHO, this is a route that should only be taken if it is necessary. [1] - http://trac.licq.org/browser/branches/erijo-dev/licq/src/utils/dynamiclibrary.h (and .cpp) [2] - http://trac.licq.org/browser/branches/erijo-dev/licq/licq/interface/pluginfactory.h [3] - http://trac.licq.org/browser/branches/erijo-dev/licq/licq/version.h [4] - http://trac.licq.org/browser/branches/erijo-dev/licq/src/plugin/pluginloader.h (and .cpp) [5] - http://trac.licq.org/browser/branches/erijo-dev/licq/licq/interface/plugininformation.h [6] - http://trac.licq.org/browser/branches/erijo-dev/licq/src/plugin/pluginprototype.h (and .cpp) [7] - http://trac.licq.org/browser/branches/erijo-dev/licq/licq/interface/plugin.h // Erik -- Oh, people can come up with statistics to prove anything. 14% of people know that. -- Homer Simpson Erik Johansson http://ejohansson.se
pgp9SvVncHIMB.pgp
Description: PGP signature
