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

Attachment: pgp9SvVncHIMB.pgp
Description: PGP signature

Reply via email to