Best practices when implementing IPlugin methods

I had a little discussion here in the office about how Qt Creator's IPlugin methods actually should be used, with the result that IPlugin::shutdown has been renamed to IPlugin::aboutToShutdown (visible in the public repos about now), and leaving me with the feeling that I should write a little bit about "Best practices when implementing IPlugin methods". :)

IPlugin's initialization and shutdown methods are called in a specific order, and some of the methods were invented to provide hooks for specific things. Doing different things in these hooks might destroy their purpose.

Let's have a look at three plugins, A, B and C. B depends on A, C depends on B.
"B depends on A" can mean that B accesses API of A, or that B implements an interface provided by A (i.e. B extends functionality of A).
As a cutdown real-life example we could take A=CorePlugin, B=Locator (adds actions via Core::ActionManager API), C=ProjectExplorer (implements a Locator::ILocatorFilter for accessing all files from a project via Locator).

The following methods are called during initialization and shutdown in the following order:

constructor: A->B->C
initialize: A->B->C
extensionsInitialized: C->B->A

aboutToShutdown: A->B->C
destructor: C->B->A


constructor+initialize:
Construct and initialize your member objects.
Initialize: Add objects to the plugin manager's object pool (add[AutoReleased]Object), these are usually things that extend functionality of another plugin. Initialize has the additional advantage that command line parameters are passed on to the plugin, and that it can "safely fail" with a error message. Connect to signals of other plugins. Load settings.
In the initialize method, a plugin can be sure that the plugins it depends on have initialized their members.
Example:
Locator adds menu items and other actions through Core::ActionManager API.
ProjectExplorer creates an instance of Locator::ILocatorFilter and adds that to the plugin manager's object pool via addObject.

extensionsInitialized:
Do final initialization that needs other plugins to be fully initialized.
In the extensionsInitialized method, a plugin can be sure that all plugins that depend on it are completely initialized. E.g. Locator knows that ProjectExplorer has been constructed and ProjectExplorer::initialize and ProjectExplorer::extensionsInitialized have been called.
Example:
Locator retrieves all ILocatorFilter instances from the plugin manager's object pool (and when QTCREATORBUG-1147 has been implemented, it also registers the actions for the filters with Core::ActionManager ;) )

aboutToShutdown:
Do preparations before actually shutting down the plugin. Disconnect from signals that would lead to unnecessary things happening. Save settings here or in the destructor.
Note that this method is called in reverse destruction order. Never delete objects here, especially not if they might be used by other plugins.
Example:
CorePlugin and Find usually track focus changes. Since this is useless during shutdown they disconnect from QApplication::focusChanged(QWidget*,QWidget*) in their aboutToShutdown methods.

destructor:
Remove objects from plugin manager's object pool and destroy them. Destroy other members.
In the destructor, a plugin can be sure that plugins that depend on API provided by it, will already be destroyed and no longer access that API.


These are the mechanisms for initialization and shutdown provided by the plugin manager API. Qt Creator's core plugin provides a few more hooks that I want to mention here at least:

Core::ICore::coreAboutToOpen and Core::ICore::coreOpened signals.
Core::ICoreListener with its method bool Core::ICoreListener::coreAboutToClose(): Whenever the user requests a application quit, all ICoreListener instances are asked if closing the application should be allowed. This is for example used to ask the user what to do with modified files.
Core::ICore::coreAboutToClose signal just before the actual shutdown sequence begins.


Blog Topics:

Comments