Pārlūkot izejas kodu

Added plugin framework stop support.

- The plugin framework can now be properly stopped.
- Many fixes and improvements (aligned with KF 3.2)
- Initial infrastructure for threaded plug-in start
Sascha Zelzer 14 gadi atpakaļ
vecāks
revīzija
62db2b8c83

+ 92 - 94
Libs/PluginFramework/ctkPlugin.cpp

@@ -22,6 +22,7 @@
 #include "ctkPlugin.h"
 
 #include "ctkPluginContext.h"
+#include "ctkPluginContext_p.h"
 #include "ctkPluginFrameworkUtil_p.h"
 #include "ctkPluginPrivate_p.h"
 #include "ctkPluginArchive_p.h"
@@ -119,7 +120,7 @@ void ctkPlugin::stop(const StopOptions& options)
 {
   Q_D(ctkPlugin);
 
-  const std::exception* savedException = 0;
+  const ctkRuntimeException* savedException = 0;
 
   //1:
   if (d->state == UNINSTALLED)
@@ -127,16 +128,14 @@ void ctkPlugin::stop(const StopOptions& options)
     throw ctkIllegalStateException("ctkPlugin is uninstalled");
   }
 
-  //2: If activating or deactivating, wait a litle
-  // We don't support threaded start/stop methods, so we don't need to wait
-  //waitOnActivation(fwCtx.packages, "Plugin::stop", false);
+  // 2: If an operation is in progress, wait a little
+  d->waitOnOperation(&d->operationLock, "Plugin::stop", false);
 
   //3:
   if ((options & STOP_TRANSIENT) == 0)
   {
     d->ignoreAutostartSetting();
   }
-  bool wasStarted = false;
 
   switch (d->state)
   {
@@ -148,28 +147,24 @@ void ctkPlugin::stop(const StopOptions& options)
     return;
 
   case ACTIVE:
-    wasStarted = true;
   case STARTING: // Lazy start...
-    try
-    {
-      d->stop0(wasStarted);
-    }
-    catch (const std::exception* exc)
-    {
-      savedException = exc;
-    }
+    savedException = d->stop0();
     break;
   };
 
-  if (savedException)
+  if (savedException != 0)
   {
     if (const ctkPluginException* pluginExc = dynamic_cast<const ctkPluginException*>(savedException))
     {
-      throw pluginExc;
+      ctkPluginException pe(*pluginExc);
+      delete savedException;
+      throw pe;
     }
     else
     {
-      throw dynamic_cast<const std::logic_error*>(savedException);
+      ctkRuntimeException re(*savedException);
+      delete savedException;
+      throw re;
     }
   }
 }
@@ -177,106 +172,109 @@ void ctkPlugin::stop(const StopOptions& options)
 //----------------------------------------------------------------------------
 void ctkPlugin::uninstall()
 {
-  bool wasResolved = false;
-
   Q_D(ctkPlugin);
-  if (d->archive)
   {
-    try
+    ctkPluginPrivate::Locker sync(&d->operationLock);
+
+    if (d->archive)
     {
-      d->archive->setStartLevel(-2); // Mark as uninstalled
+      try
+      {
+        d->archive->setStartLevel(-2); // Mark as uninstalled
+      }
+      catch (...)
+      { }
     }
-    catch (...)
-    { }
-  }
 
-  d->cachedHeaders = getHeaders();
-
-  switch (d->state)
-  {
-  case UNINSTALLED:
-    throw std::logic_error("Plugin is in UNINSTALLED state");
+    switch (d->state)
+    {
+    case UNINSTALLED:
+      throw ctkIllegalStateException("Plugin is in UNINSTALLED state");
 
-  case STARTING: // Lazy start
-  case ACTIVE:
-  case STOPPING:
-    try
+    case STARTING: // Lazy start
+    case ACTIVE:
+    case STOPPING:
     {
-      //TODO: If activating or deactivating, wait a litle
-      // we don't use mutliple threads to start plugins for now
-      //d->waitOnActivation(fwCtx.packages, "Bundle.uninstall", false);
-      if (d->state & (ACTIVE | STARTING))
+      const ctkRuntimeException* exception = 0;
+      try
       {
-        try
-        {
-          d->stop0(d->state == ACTIVE);
-        }
-        catch (const std::exception& exception)
+        d->waitOnOperation(&d->operationLock, "ctkPlugin::uninstall", true);
+        if (d->state & (ACTIVE | STARTING))
         {
-          // NYI! not call inside lock
-          d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), exception);
+          exception = d->stop0();
         }
       }
+      catch (const std::exception& e)
+      {
+        // Force to install
+        d->setStateInstalled(false);
+        d->operationLock.wakeAll();
+        exception = new ctkRuntimeException("Stopping plug-in failed", &e);
+      }
+      d->operation.fetchAndStoreOrdered(ctkPluginPrivate::UNINSTALLING);
+      if (exception != 0)
+      {
+        d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), *exception);
+        delete exception;
+      }
     }
-    catch (const std::exception& e)
-    {
-      d->deactivating = false;
-      //fwCtx.packages.notifyAll();
-      d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), e);
-    }
-    // Fall through
-  case RESOLVED:
-    wasResolved = true;
-    // Fall through
-  case INSTALLED:
-    d->fwCtx->plugins->remove(d->location);
-    d->pluginActivator = 0;
-
-    if (d->pluginDir.exists())
-    {
-      if (!ctk::removeDirRecursively(d->pluginDir.absolutePath()))
+      // Fall through
+    case RESOLVED:
+    case INSTALLED:
+      d->fwCtx->plugins->remove(d->location);
+      if (d->operation.fetchAndAddOrdered(0) != ctkPluginPrivate::UNINSTALLING)
       {
-        // Plugin dir is not deleted completely, make sure we mark
-        // it as uninstalled for next framework restart
-        if (d->archive)
+        try
         {
-          try
-          {
-            d->archive->setStartLevel(-2); // Mark as uninstalled
-          }
-          catch (const std::exception& e)
+          d->waitOnOperation(&d->operationLock, "Plugin::uninstall", true);
+          d->operation.fetchAndStoreOrdered(ctkPluginPrivate::UNINSTALLING);
+        }
+        catch (const ctkPluginException& pe)
+        {
+          // Make sure that the context is invalid
+          if (d->pluginContext != 0)
           {
-            // NYI! Generate FrameworkError if dir still exists!?
-            qDebug() << "Failed to mark plugin" <<  d->id
-                     << "as uninstalled," << d->pluginDir.absoluteFilePath()
-                     << "must be deleted manually:" << e.what();
+            d->pluginContext->d_func()->invalidate();
+            d->pluginContext.reset();
           }
+          d->operation.fetchAndStoreOrdered(ctkPluginPrivate::UNINSTALLING);
+          d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), pe);
         }
       }
-      d->pluginDir.setFile("");
-    }
-    if (d->archive)
-    {
-      d->archive->purge();
-    }
 
-    // id, location and headers survive after uninstall.
-    // TODO: UNRESOLVED must be sent out during installed state
-    // This needs to be reviewed. See OSGi bug #1374
-    d->state = INSTALLED;
-    d->modified();
+      d->state = INSTALLED;
+      // TODO: use thread
+      // bundleThread().bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this));
+      d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, d->q_func()));
+      d->cachedHeaders = getHeaders();
+      d->pluginActivator = 0;
+      d->state = UNINSTALLED;
 
-    // Broadcast events
-    if (wasResolved)
-    {
-      d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, d->q_ptr));
-    }
+      // Purge old archive
+      if (d->archive)
+      {
+        d->archive->purge();
+      }
+      d->operation.fetchAndStoreOrdered(ctkPluginPrivate::IDLE);
+
+      if (d->pluginDir.exists())
+      {
+        if (!ctk::removeDirRecursively(d->pluginDir.absolutePath()))
+        {
+          d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), std::runtime_error("Failed to delete plugin data"));
+        }
+        d->pluginDir.setFile("");
+      }
 
-    d->state = UNINSTALLED;
-    d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNINSTALLED, d->q_ptr));
+      // id, location and headers survive after uninstall.
 
-    break;
+      // There might be plug-in threads that are running start or stop
+      // operations. This will wake them and give them a chance to terminate.
+      d->operationLock.wakeAll();
+      break;
+    }
   }
+  d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNINSTALLED, d->q_ptr));
 }
 
 //----------------------------------------------------------------------------

+ 2 - 1
Libs/PluginFramework/ctkPlugin.h

@@ -492,7 +492,7 @@ public:
    *         plugin tries to change its own state.
    * @see #stop()
    */
-  void uninstall();
+  virtual void uninstall();
 
   /**
    * Returns this plugin's {@link ctkPluginContext}. The returned
@@ -718,6 +718,7 @@ public:
 protected:
 
   friend class ctkPluginFramework;
+  friend class ctkPluginFrameworkPrivate;
   friend class ctkPluginFrameworkContext;
   friend class ctkPlugins;
   friend class ctkServiceReferencePrivate;

+ 50 - 8
Libs/PluginFramework/ctkPluginFramework.cpp

@@ -44,8 +44,8 @@ void ctkPluginFramework::init()
 {
   Q_D(ctkPluginFramework);
 
-  QMutexLocker sync(&d->lock);
-  // waitOnActivation(d->lock, "Framework.init", true);
+  ctkPluginPrivate::Locker sync(&d->lock);
+  d->waitOnOperation(&d->lock, "Framework.init", true);
   switch (d->state)
   {
   case ctkPlugin::INSTALLED:
@@ -61,6 +61,32 @@ void ctkPluginFramework::init()
 }
 
 //----------------------------------------------------------------------------
+ctkPluginFrameworkEvent ctkPluginFramework::waitForStop(unsigned long timeout)
+{
+  Q_D(ctkPluginFramework);
+
+  ctkPluginPrivate::Locker sync(&d->operationLock);
+
+  // Already stopped?
+  if ((d->state & (INSTALLED | RESOLVED)) == 0)
+  {
+    d->stopEvent = ctkPluginFrameworkEvent();
+    d->lock.wait(timeout ? timeout : ULONG_MAX);
+
+    if (d->stopEvent.isNull())
+    {
+      return ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::WAIT_TIMEDOUT, this->d_func()->q_func());
+    }
+  }
+  else if (d->stopEvent.isNull())
+  {
+    // Return this if stop or update have not been called and framework is stopped.
+    d->stopEvent = ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::STOPPED, this->d_func()->q_func());
+  }
+  return d->stopEvent;
+}
+
+//----------------------------------------------------------------------------
 void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
 {
   Q_UNUSED(options);
@@ -68,9 +94,8 @@ void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
 
   QStringList pluginsToStart;
   {
-    QMutexLocker sync(&d->lock);
-    // TODO: parallel start
-    //waitOnActivation(lock, "ctkPluginFramework::start", true);
+    ctkPluginPrivate::Locker sync(&d->lock);
+    d->waitOnOperation(&d->lock, "ctkPluginFramework::start", true);
 
     switch (d->state)
     {
@@ -78,7 +103,7 @@ void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
     case RESOLVED:
       d->init();
     case STARTING:
-      d->activating = true;
+      d->operation.fetchAndStoreOrdered(ctkPluginPrivate::ACTIVATING);
       break;
     case ACTIVE:
       return;
@@ -112,15 +137,32 @@ void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
   }
 
   {
-    QMutexLocker sync(&d->lock);
+    ctkPluginPrivate::Locker sync(&d->lock);
     d->state = ACTIVE;
-    d->activating = false;
+    d->operation = ctkPluginPrivate::IDLE;
+    d->lock.wakeAll();
     d->fwCtx->listeners.emitFrameworkEvent(
         ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::STARTED, this->d_func()->q_func()));
   }
 }
 
 //----------------------------------------------------------------------------
+void ctkPluginFramework::stop(const StopOptions& options)
+{
+  Q_UNUSED(options)
+
+  Q_D(ctkPluginFramework);
+  d->shutdown(false);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFramework::uninstall()
+{
+  throw ctkPluginException("uninstall of System plugin is not allowed",
+                           ctkPluginException::INVALID_OPERATION);
+}
+
+//----------------------------------------------------------------------------
 QStringList ctkPluginFramework::getResourceList(const QString& path) const
 {
   QString resourcePath = QString(":/") + ctkPluginConstants::SYSTEM_PLUGIN_SYMBOLICNAME;

+ 83 - 4
Libs/PluginFramework/ctkPluginFramework.h

@@ -22,11 +22,10 @@
 #ifndef CTKPLUGINFRAMEWORK_H
 #define CTKPLUGINFRAMEWORK_H
 
-#include "ctkPlugin.h"
-
-
-#include "ctkPluginFrameworkExport.h"
+#include <ctkPluginFrameworkExport.h>
 
+#include "ctkPlugin.h"
+#include "ctkPluginFrameworkEvent.h"
 
 class ctkPluginFrameworkContext;
 class ctkPluginFrameworkPrivate;
@@ -76,6 +75,42 @@ public:
   void init();
 
   /**
+   * Wait until this %ctkPluginFramework has completely stopped. The <code>stop</code>
+   * and <code>update</code> methods perform an asynchronous
+   * stop of the Framework. This method can be used to wait until the
+   * asynchronous stop of this Framework has completed. This method will only
+   * wait if called when this Framework is in the {@link #STARTING},
+   * {@link #ACTIVE}, or {@link #STOPPING} states. Otherwise it will return
+   * immediately.
+   * <p>
+   * A Framework Event is returned to indicate why this Framework has stopped.
+   *
+   * @param timeout Maximum number of milliseconds to wait until this
+   *        Framework has completely stopped. A value of zero will wait
+   *        indefinitely.
+   * @return A Framework Event indicating the reason this method returned. The
+   *         following <code>ctkFrameworkEvent</code> types may be returned by
+   *         this method.
+   *         <ul>
+   *         <li>{@link ctkFrameworkEvent#STOPPED STOPPED} - This Framework has
+   *         been stopped. </li>
+   *
+   *         <li>{@link ctkFrameworkEvent#STOPPED_UPDATE STOPPED_UPDATE} - This
+   *         Framework has been updated which has shutdown and will now
+   *         restart.</li>
+   *
+   *         <li>{@link ctkFrameworkEvent#ERROR ERROR} - The Framework
+   *         encountered an error while shutting down or an error has occurred
+   *         which forced the framework to shutdown. </li>
+   *
+   *         <li> {@link ctkFrameworkEvent#WAIT_TIMEDOUT WAIT_TIMEDOUT} - This
+   *         method has timed out and returned before this Framework has
+   *         stopped.</li>
+   *         </ul>
+   */
+  ctkPluginFrameworkEvent waitForStop(unsigned long timeout);
+
+  /**
    * Start this %ctkPluginFramework.
    *
    * <p>
@@ -100,6 +135,50 @@ public:
   void start(const ctkPlugin::StartOptions& options = 0);
 
   /**
+   * Stop this %ctkPluginFramework.
+   *
+   * <p>
+   * The method returns immediately to the caller after initiating the
+   * following steps to be taken on another thread.
+   * <ol>
+   * <li>This Framework's state is set to {@link #STOPPING}.</li>
+   * <li>All installed plugins must be stopped without changing each plugin's
+   * persistent <i>autostart setting</i>. If this Framework implements the
+   * optional <i>Start Level Service Specification</i>, then the start level
+   * of this Framework is moved to start level zero (0), as described in the
+   * <i>Start Level Service Specification</i>. Any exceptions that occur
+   * during plugin stopping must be wrapped in a {@link ctkPluginException} and
+   * then published as a framework event of type {@link ctkPluginFrameworkEvent#ERROR}</li>
+   * <li>Unregister all services registered by this Framework.</li>
+   * <li>Event handling is disabled.</li>
+   * <li>This Framework's state is set to {@link #RESOLVED}.</li>
+   * <li>All resources held by this Framework are released. This includes
+   * threads, loaded libraries, open files, etc.</li>
+   * <li>Notify all threads that are waiting at {@link #waitForStop(long)
+   * waitForStop} that the stop operation has completed.</li>
+   * </ol>
+   * <p>
+   * After being stopped, this Framework may be discarded, initialized or
+   * started.
+   *
+   * @param options Ignored. There are no stop options for the Framework.
+   * @throws ctkPluginException If stopping this Framework could not be
+   *         initiated.
+   * @see "Start Level Service Specification"
+   */
+  void stop(const StopOptions& options = 0);
+
+  /**
+   * The %ctkPluginFramework cannot be uninstalled.
+   *
+   * <p>
+   * This method always throws a ctkPluginException.
+   *
+   * @throws ctkPluginException This Framework cannot be uninstalled.
+   */
+  void uninstall();
+
+  /**
    * @see ctkPlugin::getHeaders()
    */
   QHash<QString, QString> getHeaders();

+ 1 - 1
Libs/PluginFramework/ctkPluginFrameworkListeners.cpp

@@ -61,7 +61,7 @@ void ctkPluginFrameworkListeners::addServiceSlot(
   serviceSet.insert(sse);
   checkSimple(sse);
 
-  connect(receiver, SIGNAL(destroyed(QObject*)), this, SLOT(serviceListenerDestroyed(QObject*)));
+  connect(receiver, SIGNAL(destroyed(QObject*)), this, SLOT(serviceListenerDestroyed(QObject*)), Qt::DirectConnection);
 }
 
 //----------------------------------------------------------------------------

+ 158 - 1
Libs/PluginFramework/ctkPluginFrameworkPrivate.cpp

@@ -26,13 +26,17 @@
 #include "ctkPluginContext.h"
 #include "ctkPluginContext_p.h"
 #include "ctkPluginFrameworkContext_p.h"
+#include "ctkPluginFrameworkUtil_p.h"
+
+#include <QtConcurrentRun>
 
 //----------------------------------------------------------------------------
 ctkPluginFrameworkPrivate::ctkPluginFrameworkPrivate(QWeakPointer<ctkPlugin> qq, ctkPluginFrameworkContext* fw)
   : ctkPluginPrivate(qq, fw, 0, ctkPluginConstants::SYSTEM_PLUGIN_LOCATION,
                      ctkPluginConstants::SYSTEM_PLUGIN_SYMBOLICNAME,
                      // TODO: read version from the manifest resource
-                     ctkVersion(0, 9, 0))
+                     ctkVersion(0, 9, 0)),
+    shuttingDown(0)
 {
   systemHeaders.insert(ctkPluginConstants::PLUGIN_SYMBOLICNAME, symbolicName);
   systemHeaders.insert(ctkPluginConstants::PLUGIN_NAME, location);
@@ -58,3 +62,156 @@ void ctkPluginFrameworkPrivate::uninitSystemPlugin()
   this->pluginContext->d_func()->invalidate();
 }
 
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::shutdown(bool restart)
+{
+  Locker sync(&lock);
+
+  bool wasActive = false;
+  switch (state)
+  {
+  case ctkPlugin::INSTALLED:
+  case ctkPlugin::RESOLVED:
+    shutdownDone_unlocked(false);
+    break;
+  case ctkPlugin::ACTIVE:
+    wasActive = true;
+    // Fall through
+  case ctkPlugin::STARTING:
+    if (shuttingDown.fetchAndAddOrdered(0) == 0)
+    {
+      try
+      {
+        const bool wa = wasActive;
+        shuttingDown.fetchAndStoreOrdered(1);
+        QtConcurrent::run(this, &ctkPluginFrameworkPrivate::shutdown0, restart, wa);
+      }
+      catch (const std::exception& e)
+      {
+        systemShuttingdownDone(ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::ERROR, this->q_func(), e));
+      }
+    }
+    break;
+  case ctkPlugin::STOPPING:
+    // Shutdown already inprogress
+    break;
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::shutdown0(bool restart, bool wasActive)
+{
+  try
+  {
+    {
+      Locker sync(&lock);
+      waitOnOperation(&lock, QString("Framework::") + (restart ? "update" : "stop"), true);
+      operation = DEACTIVATING;
+      state = ctkPlugin::STOPPING;
+    }
+
+    fwCtx->listeners.emitPluginChanged(
+        ctkPluginEvent(ctkPluginEvent::STOPPING, this->q_func()));
+
+    if (wasActive)
+    {
+      stopAllPlugins();
+    }
+
+    {
+      Locker sync(&lock);
+      fwCtx->uninit();
+      shuttingDown.fetchAndStoreOrdered(0);
+      shutdownDone_unlocked(restart);
+    }
+
+    if (restart)
+    {
+      if (wasActive)
+      {
+        q_func().toStrongRef()->start();
+      }
+      else
+      {
+        init();
+      }
+    }
+  }
+  catch (const std::exception& e)
+  {
+    shuttingDown.fetchAndStoreOrdered(0);
+    systemShuttingdownDone(ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::ERROR, this->q_func(), e));
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::shutdownDone_unlocked(bool restart)
+{
+  ctkPluginFrameworkEvent::Type t = restart ? ctkPluginFrameworkEvent::STOPPED_UPDATE : ctkPluginFrameworkEvent::STOPPED;
+  systemShuttingdownDone_unlocked(ctkPluginFrameworkEvent(t, this->q_func()));
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::stopAllPlugins()
+{
+  // TODO start level
+//  if (fwCtx.startLevelController != null)
+//  {
+//    fwCtx.startLevelController.shutdown();
+//  }
+
+  // Stop all active plug-ins, in reverse plug-in ID order
+  // The list will be empty when the start level service is in use.
+  QList<QSharedPointer<ctkPlugin> > activePlugins = fwCtx->plugins->getActivePlugins();
+  qSort(activePlugins.begin(), activePlugins.end(), pluginIdLessThan);
+  QListIterator<QSharedPointer<ctkPlugin> > i(activePlugins);
+  i.toBack();
+  while(i.hasPrevious())
+  {
+    QSharedPointer<ctkPlugin> p = i.previous();
+    try
+    {
+      if (p->getState() & (ctkPlugin::ACTIVE | ctkPlugin::STARTING))
+      {
+        // Stop plugin without changing its autostart setting.
+        p->stop(ctkPlugin::STOP_TRANSIENT);
+      }
+    }
+    catch (const ctkPluginException& pe)
+    {
+      fwCtx->listeners.frameworkError(p, pe);
+    }
+  }
+
+  QList<QSharedPointer<ctkPlugin> > allPlugins = fwCtx->plugins->getPlugins();
+
+  // Set state to INSTALLED and purge any unrefreshed bundles
+  foreach (QSharedPointer<ctkPlugin> p, allPlugins)
+  {
+    if (p->getPluginId() != 0)
+    {
+      p->d_func()->setStateInstalled(false);
+      p->d_func()->purge();
+    }
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::systemShuttingdownDone(const ctkPluginFrameworkEvent& fe)
+{
+  Locker sync(&lock);
+  systemShuttingdownDone_unlocked(fe);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::systemShuttingdownDone_unlocked(const ctkPluginFrameworkEvent& fe)
+{
+
+  if (state != ctkPlugin::INSTALLED)
+  {
+    state = ctkPlugin::RESOLVED;
+    operation.fetchAndStoreOrdered(IDLE);
+    lock.wakeAll();
+  }
+  stopEvent = fe;
+}

+ 75 - 2
Libs/PluginFramework/ctkPluginFrameworkPrivate_p.h

@@ -27,7 +27,6 @@
 
 #include <QMutex>
 
-
 class ctkPluginFrameworkContext;
 
 /**
@@ -37,7 +36,22 @@ class ctkPluginFrameworkPrivate : public ctkPluginPrivate
 {
 public:
 
-  QMutex lock;
+  /**
+   * The event to return to callers waiting in ctkPluginFramework::waitForStop()
+   * when the framework has been stopped.
+   */
+  ctkPluginFrameworkEvent stopEvent;
+
+  /**
+   * The flag indicating that the thread that performs shutdown of this
+   * framework instance is running.
+   */
+  QAtomicInt shuttingDown;
+
+  /**
+   * Lock object
+   */
+  LockObject lock;
 
   ctkPluginFrameworkPrivate(QWeakPointer<ctkPlugin> qq, ctkPluginFrameworkContext* fw);
 
@@ -47,8 +61,67 @@ public:
 
   void uninitSystemPlugin();
 
+  /**
+   * This method starts a thread that stop this Framework,
+   * stopping all started plug-ins.
+   *
+   * <p>If the framework is not started, this method does nothing.
+   * If the framework is started, this method will:
+   * <ol>
+   * <li>Set the state of the ctkPluginFrameworkContext to <i>inactive</i>.</li>
+   * <li>Suspended all started plug-ins as described in the
+   * {@link ctkPlugin#stop()} method except that the persistent
+   * state of the plug-in will continue to be started.
+   * Reports any exceptions that occur during stopping using
+   * <code>ctkPluginFramework</code>s.</li>
+   * <li>Disable event handling.</li>
+   * </ol></p>
+   *
+   */
+  void shutdown(bool restart);
+
   QHash<QString, QString> systemHeaders;
 
+private:
+
+  /**
+   * Stop this FrameworkContext, suspending all started contexts.
+   * This method suspends all started contexts so that they can be
+   * automatically restarted when this FrameworkContext is next launched.
+   *
+   * <p>If the framework is not started, this method does nothing.
+   * If the framework is started, this method will:
+   * <ol>
+   * <li>Set the state of the FrameworkContext to <i>inactive</i>.</li>
+   * <li>Stop all started bundles as described in the
+   * {@link Bundle#stop(int)} method except that the persistent
+   * state of the bundle will continue to be started.
+   * Reports any exceptions that occur during stopping using
+   * <code>FrameworkErrorEvents</code>.</li>
+   * <li>Disable event handling.</li>
+   * </ol>
+   * </p>
+   *
+   */
+  void shutdown0(bool restart, bool wasActive);
+
+  /**
+   * Tell system plugin shutdown finished.
+   */
+  void shutdownDone_unlocked(bool restart);
+
+  /**
+   * Stop and unresolve all plug-ins.
+   */
+  void stopAllPlugins();
+
+  /**
+   * Shutting down is done.
+   */
+  void systemShuttingdownDone(const ctkPluginFrameworkEvent& fe);
+
+  void systemShuttingdownDone_unlocked(const ctkPluginFrameworkEvent& fe);
+
 };
 
 

+ 4 - 0
Libs/PluginFramework/ctkPluginFrameworkUtil.cpp

@@ -410,3 +410,7 @@ bool ctkPluginFrameworkUtil::patSubstr(const QString& s, int si, const QString&
   }
 }
 
+bool pluginIdLessThan(const QSharedPointer<ctkPlugin>& p1, const QSharedPointer<ctkPlugin>& p2)
+{
+  return p1->getPluginId() < p2->getPluginId();
+}

+ 6 - 0
Libs/PluginFramework/ctkPluginFrameworkUtil_p.h

@@ -78,6 +78,12 @@ private:
    *
    */
   static bool patSubstr(const QString& s, int si, const QString& pat, int pi);
+
 };
 
+#include <QSharedPointer>
+class ctkPlugin;
+
+bool pluginIdLessThan(const QSharedPointer<ctkPlugin>& p1, const QSharedPointer<ctkPlugin>& p2);
+
 #endif // CTKPLUGINFRAMEWORKUTIL_P_H

+ 255 - 84
Libs/PluginFramework/ctkPluginPrivate.cpp

@@ -36,6 +36,48 @@
 const ctkPlugin::States ctkPluginPrivate::RESOLVED_FLAGS = ctkPlugin::RESOLVED | ctkPlugin::STARTING | ctkPlugin::ACTIVE | ctkPlugin::STOPPING;
 
 //----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::lock()
+{
+  m_Lock.lock();
+}
+
+//----------------------------------------------------------------------------
+bool ctkPluginPrivate::LockObject::tryLock()
+{
+  return m_Lock.tryLock();
+}
+
+//----------------------------------------------------------------------------
+bool ctkPluginPrivate::LockObject::tryLock(int timeout)
+{
+  return m_Lock.tryLock(timeout);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::unlock()
+{
+  m_Lock.unlock();
+}
+
+//----------------------------------------------------------------------------
+bool ctkPluginPrivate::LockObject::wait(unsigned long time)
+{
+  return m_Condition.wait(&m_Lock, time);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::wakeAll()
+{
+  m_Condition.wakeAll();
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::wakeOne()
+{
+  m_Condition.wakeOne();
+}
+
+//----------------------------------------------------------------------------
 ctkPluginPrivate::ctkPluginPrivate(
     QWeakPointer<ctkPlugin> qq,
     ctkPluginFrameworkContext* fw,
@@ -43,7 +85,7 @@ ctkPluginPrivate::ctkPluginPrivate(
       : q_ptr(qq), fwCtx(fw), id(pa->getPluginId()),
       location(pa->getPluginLocation().toString()), state(ctkPlugin::INSTALLED),
       archive(pa), pluginContext(0), pluginActivator(0), pluginLoader(pa->getLibLocation()),
-      eagerActivation(false), activating(false), deactivating(false)
+      resolveFailException(0), eagerActivation(false), wasStarted(false)
 {
   //TODO
   //checkCertificates(pa);
@@ -111,9 +153,9 @@ ctkPluginPrivate::ctkPluginPrivate(QWeakPointer<ctkPlugin> qq,
                                    ctkPluginFrameworkContext* fw,
                                    long id, const QString& loc, const QString& sym, const ctkVersion& ver)
                                      : q_ptr(qq), fwCtx(fw), id(id), location(loc), symbolicName(sym), version(ver),
-                                     state(ctkPlugin::INSTALLED), archive(0), pluginContext(0),
-                                     pluginActivator(0), eagerActivation(false), activating(false),
-                                     deactivating(false)
+                                       state(ctkPlugin::INSTALLED), archive(0), pluginContext(0),
+                                       pluginActivator(0), resolveFailException(0),
+                                       eagerActivation(false), wasStarted(false)
 {
   modified();
 }
@@ -132,21 +174,26 @@ ctkPlugin::State ctkPluginPrivate::getUpdatedState()
   {
     try
     {
+      // NYI, fix double locking
+      Locker sync(&operationLock);
       if (state == ctkPlugin::INSTALLED)
       {
         fwCtx->resolvePlugin(this);
         state = ctkPlugin::RESOLVED;
+        operation.fetchAndStoreOrdered(RESOLVING);
+        // TODO plugin threading
+        //bundleThread().bundleChanged(new BundleEvent(BundleEvent.RESOLVED, this));
         fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::RESOLVED, this->q_func()));
+        operation.fetchAndStoreOrdered(IDLE);
       }
-
     }
     catch (const ctkPluginException& pe)
     {
+      if (resolveFailException) delete resolveFailException;
+      resolveFailException = new ctkPluginException(pe);
       this->fwCtx->listeners.frameworkError(this->q_func(), pe);
-      throw;
     }
   }
-
   return state;
 }
 
@@ -157,6 +204,44 @@ QFileInfo ctkPluginPrivate::getDataRoot()
 }
 
 //----------------------------------------------------------------------------
+void ctkPluginPrivate::setStateInstalled(bool sendEvent)
+{
+  Locker sync(&operationLock);
+
+  // Make sure that the context is invalid
+  if (pluginContext != 0)
+  {
+    pluginContext->d_func()->invalidate();
+    pluginContext.reset();
+  }
+  state = ctkPlugin::INSTALLED;
+  if (sendEvent)
+  {
+    operation.fetchAndStoreOrdered(UNRESOLVING);
+    // TODO: plugin thread
+    //bundleThread().bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this));
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, this->q_func()));
+  }
+  operation.fetchAndStoreOrdered(IDLE);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::purge()
+{
+  if (state == ctkPlugin::UNINSTALLED)
+  {
+    fwCtx->plugins->remove(location);
+  }
+//  Vector fix = oldGenerations;
+//  if (fix != null) {
+//    oldGenerations = null;
+//    for (Iterator i = fix.iterator(); i.hasNext();) {
+//      ((BundleGeneration)i.next()).purge(true);
+//    }
+//  }
+}
+
+//----------------------------------------------------------------------------
 void ctkPluginPrivate::setAutostartSetting(const ctkPlugin::StartOptions& setting) {
   try
   {
@@ -241,19 +326,22 @@ void ctkPluginPrivate::checkManifestHeaders()
 //----------------------------------------------------------------------------
 void ctkPluginPrivate::finalizeActivation()
 {
+  Locker sync(&operationLock);
+
+  // 4: Resolve plugin (if needed)
   switch (getUpdatedState())
   {
   case ctkPlugin::INSTALLED:
-    // we shouldn't be here, getUpdatedState should have thrown
-    // an exception during resolving the plugin
-    throw ctkPluginException("Internal error: expected exception on plugin resolve not thrown!");
+    Q_ASSERT_X(resolveFailException != 0, Q_FUNC_INFO, "no resolveFailException");
+    throw ctkPluginException(*resolveFailException);
   case ctkPlugin::STARTING:
-    if (activating) return; // finalization already in progress.
+    if (operation.fetchAndAddOrdered(0) == ACTIVATING) return; // finalization already in progress.
     // Lazy activation; fall through to RESOLVED.
   case ctkPlugin::RESOLVED:
+  {
     //6:
     state = ctkPlugin::STARTING;
-    activating = true;
+    operation.fetchAndStoreOrdered(ACTIVATING);
     if (fwCtx->debug.lazy_activation)
     {
       qDebug() << "activating #" << this->id;
@@ -263,33 +351,24 @@ void ctkPluginPrivate::finalizeActivation()
     {
       pluginContext.reset(new ctkPluginContext(this));
     }
-    try
-    {
-      // start dependencies
-      startDependencies();
-      //TODO maybe call this in its own thread
-      start0();
-    }
-    catch (...)
+    // start dependencies
+    startDependencies();
+    //TODO plugin threading
+    //ctkRuntimeException* e = bundleThread().callStart0(this);
+    ctkRuntimeException* e = start0();
+    operation.fetchAndStoreOrdered(IDLE);
+    operationLock.wakeAll();
+    if (e)
     {
-      //8:
-      state = ctkPlugin::STOPPING;
-      // NYI, call outside lock
-      fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, this->q_func()));
-      removePluginResources();
-      pluginContext.reset();
-
-      state = ctkPlugin::RESOLVED;
-      // NYI, call outside lock
-      fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, this->q_func()));
-      activating = false;
-      throw;
+      ctkRuntimeException re(*e);
+      delete e;
+      throw re;
     }
-    activating = false;
     break;
-      case ctkPlugin::ACTIVE:
+  }
+  case ctkPlugin::ACTIVE:
     break;
-      case ctkPlugin::STOPPING:
+  case ctkPlugin::STOPPING:
     // This happens if start is called from inside the ctkPluginActivator::stop method.
     // Don't allow it.
     throw ctkPluginException("start called from ctkPluginActivator::stop",
@@ -300,69 +379,133 @@ void ctkPluginPrivate::finalizeActivation()
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginPrivate::stop0(bool wasStarted)
+const ctkRuntimeException* ctkPluginPrivate::stop0()
 {
-  //5:
+  wasStarted = state == ctkPlugin::ACTIVE;
+  // 5:
   state = ctkPlugin::STOPPING;
-  deactivating = true;
-  //6-13:
-  ctkPluginActivator* activator = 0;
-  const std::exception* savedException = 0;
-  try
+  operation.fetchAndStoreOrdered(DEACTIVATING);
+  // 6-13:
+  // TODO plugin threading
+  //const ctkRuntimeException* savedException = pluginThread().callStop1(this);
+  const ctkRuntimeException* savedException = stop1();
+  if (state != ctkPlugin::UNINSTALLED)
   {
-    //6:
-    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, q_func()));
+    state = ctkPlugin::RESOLVED;
+    // TODO plugin threading
+    //bundleThread().bundleChanged(new BundleEvent(BundleEvent.STOPPED, this));
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, this->q_func()));
 
-    //7:
-    if (wasStarted && pluginActivator)
+    operationLock.wakeAll();
+    operation.fetchAndStoreOrdered(IDLE);
+  }
+  return savedException;
+}
+
+//----------------------------------------------------------------------------
+const ctkRuntimeException* ctkPluginPrivate::stop1()
+{
+  ctkPluginActivator* activator = 0;
+  const ctkRuntimeException* res = 0;
+
+  //6:
+  fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, q_func()));
+
+  //7:
+  if (wasStarted && pluginActivator)
+  {
+    try
     {
-      try
-      {
-        pluginActivator->stop(pluginContext.data());
-      }
-      catch (const std::exception& e)
+      pluginActivator->stop(pluginContext.data());
+      if (state != ctkPlugin::STOPPING)
       {
-        savedException = new ctkPluginException("ctkPlugin::stop: PluginActivator stop failed",
-                                                ctkPluginException::ACTIVATOR_ERROR, &e);
+        if (state == ctkPlugin::UNINSTALLED)
+        {
+          return new ctkIllegalStateException("Plug-in is uninstalled");
+        }
+        else
+        {
+          return new ctkIllegalStateException("Plug-in changed state because of refresh during stop");
+        }
       }
-      if (state == ctkPlugin::UNINSTALLED)
-      {
-        throw std::logic_error("Plugin is uninstalled");
-      }
-      activator = pluginActivator;
-      pluginActivator = 0;
     }
+    catch (const std::exception& e)
+    {
+      res = new ctkPluginException("ctkPlugin::stop: PluginActivator stop failed",
+                                              ctkPluginException::ACTIVATOR_ERROR, &e);
+    }
+    activator = pluginActivator;
+    pluginActivator = 0;
+  }
 
-    // Call hooks after we've called PluginActivator::stop(), but before we've cleared all resources
-    // TODO service listener hooks
-    //fwCtx->listeners.serviceListeners.hooksBundleStopped(this);
-
+  if (operation.fetchAndAddOrdered(0) == DEACTIVATING)
+  {
+    // Call hooks after we've called PluginActivator::stop(), but before we've
+    // cleared all resources
     if (pluginContext)
     {
+      // TODO service listener hooks
+      //fwCtx->listeners.serviceListeners.hooksBundleStopped(this);
+
+      //8-10:
+      removePluginResources();
       pluginContext->d_func()->invalidate();
       pluginContext.reset();
     }
-    //8-10:
-    removePluginResources();
-  }
-  catch (const std::exception* exc)
-  {
-    savedException = exc;
   }
 
   delete activator;
 
-  if (state != ctkPlugin::UNINSTALLED)
-  {
-    state = ctkPlugin::RESOLVED;
-    deactivating = false;
-    //fwCtx.packages.notifyAll();
-    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, q_func()));
-  }
+  return res;
+}
 
-  if (savedException)
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::waitOnOperation(LockObject* lock, const QString& src, bool longWait)
+{
+  if (operation.fetchAndAddOrdered(0) != IDLE)
   {
-    throw savedException;
+    qint64 left = longWait ? 20000 : 500;
+    QDateTime waitUntil = QDateTime::currentDateTime().addMSecs(left);
+    do
+    {
+      lock->wait(left);
+      if (operation.fetchAndAddOrdered(0) == IDLE)
+      {
+        return;
+      }
+      left = QDateTime::currentDateTime().msecsTo(waitUntil);
+    } while (left > 0);
+
+    QString op;
+    switch (operation.fetchAndAddOrdered(0))
+    {
+    case IDLE:
+      // Should not happen!
+      return;
+    case ACTIVATING:
+      op = "start";
+      break;
+    case DEACTIVATING:
+      op = "stop";
+      break;
+    case RESOLVING:
+      op = "resolve";
+      break;
+    case UNINSTALLING:
+      op = "uninstall";
+      break;
+    case UNRESOLVING:
+      op = "unresolve";
+      break;
+    case UPDATING:
+      op = "update";
+      break;
+    default:
+      op = "unknown operation";
+      break;
+    }
+    throw ctkPluginException(src + " called during " + op + " of plug-in",
+                             ctkPluginException::STATECHANGE_ERROR);
   }
 }
 
@@ -426,14 +569,18 @@ void ctkPluginPrivate::startDependencies()
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginPrivate::start0()
+ctkPluginException* ctkPluginPrivate::start0()
 {
+  ctkPluginException* res = 0;
+
   fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STARTING, this->q_func()));
 
+  ctkPluginException::Type error_type = ctkPluginException::MANIFEST_ERROR;
   try {
     pluginLoader.load();
     if (!pluginLoader.isLoaded())
     {
+      error_type = ctkPluginException::ACTIVATOR_ERROR;
       throw ctkPluginException(QString("Loading plugin %1 failed: %2").arg(pluginLoader.fileName()).arg(pluginLoader.errorString()),
                                ctkPluginException::ACTIVATOR_ERROR);
     }
@@ -447,15 +594,23 @@ void ctkPluginPrivate::start0()
 
     pluginActivator->start(pluginContext.data());
 
-    if (ctkPlugin::UNINSTALLED == state)
+    if (state != ctkPlugin::STARTING)
     {
-      throw ctkPluginException("ctkPlugin uninstalled during start()", ctkPluginException::STATECHANGE_ERROR);
+      error_type = ctkPluginException::STATECHANGE_ERROR;
+      if (ctkPlugin::UNINSTALLED == state)
+      {
+        throw ctkPluginException("ctkPlugin uninstalled during start()", ctkPluginException::STATECHANGE_ERROR);
+      }
+      else
+      {
+        throw ctkPluginException("ctkPlugin changed state because of refresh during start()", ctkPluginException::STATECHANGE_ERROR);
+      }
     }
     state = ctkPlugin::ACTIVE;
   }
   catch (const std::exception& e)
   {
-    throw ctkPluginException("ctkPlugin start failed", ctkPluginException::ACTIVATOR_ERROR, &e);
+    res = new ctkPluginException("ctkPlugin start failed", error_type, &e);
   }
 
   if (fwCtx->debug.lazy_activation)
@@ -463,8 +618,24 @@ void ctkPluginPrivate::start0()
     qDebug() << "activating #" << id << "completed.";
   }
 
-  //10:
-  fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STARTED, this->q_func()));
+  if (res == 0)
+  {
+    //10:
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STARTED, this->q_func()));
+  }
+  else if (operation.fetchAndAddOrdered(0) == ACTIVATING)
+  {
+    // 8:
+    state = ctkPlugin::STOPPING;
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, this->q_func()));
+    removePluginResources();
+    pluginContext->d_func()->invalidate();
+    pluginContext.reset();
+    state = ctkPlugin::RESOLVED;
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, this->q_func()));
+  }
+
+  return res;
 }
 
 //----------------------------------------------------------------------------

+ 82 - 12
Libs/PluginFramework/ctkPluginPrivate_p.h

@@ -30,6 +30,8 @@
 #include <QPluginLoader>
 #include <QDateTime>
 #include <QFileInfo>
+#include <QMutex>
+#include <QWaitCondition>
 
 
 class ctkPluginActivator;
@@ -45,12 +47,43 @@ protected:
 
   const QWeakPointer<ctkPlugin> q_ptr;
 
+  class LockObject
+  {
+  public:
+
+    LockObject() {};
+
+    void lock();
+    bool tryLock();
+    bool tryLock(int timeout);
+    void unlock();
+
+    bool wait(unsigned long time = ULONG_MAX);
+    void wakeAll();
+    void wakeOne();
+
+  private:
+
+    Q_DISABLE_COPY(LockObject)
+
+    QMutex m_Lock;
+    QWaitCondition m_Condition;
+  };
+
 public:
 
   inline QWeakPointer<ctkPlugin> q_func() { return q_ptr; }
   inline QWeakPointer<const ctkPlugin> q_func() const { return q_ptr; }
   friend class ctkPlugin;
 
+  struct Locker
+  {
+    Locker(LockObject* obj) : m_Obj(obj) { m_Obj->lock(); }
+    ~Locker() { m_Obj->unlock(); }
+  private:
+      LockObject* m_Obj;
+  };
+
   /**
    * Construct a new plugin based on a ctkPluginArchive.
    *
@@ -94,6 +127,17 @@ public:
   QFileInfo getDataRoot();
 
   /**
+   * Set state to INSTALLED. We assume that the plug-in is resolved
+   * when entering this method.
+   */
+  void setStateInstalled(bool sendEvent);
+
+  /**
+   * Purge any old files associated with this plug-in.
+   */
+  void purge();
+
+  /**
    * Save the autostart setting to the persistent plugin storage.
    *
    * @param setting The autostart options to save.
@@ -109,10 +153,24 @@ public:
    */
   void finalizeActivation();
 
+  const ctkRuntimeException* stop0();
+
   /**
-   * Performs the actual stopping.
+   * Stop code that is executed in the pluginThread without holding the
+   * operationLock.
    */
-  void stop0(bool wasStarted);
+  const ctkRuntimeException* stop1();
+
+  /**
+   * Wait for an ongoing operation to finish.
+   *
+   * @param lock QMutex used for locking.
+   * @param src Caller to include in exception message.
+   * @param longWait True, if we should wait extra long before aborting.
+   * @throws ctkPluginException if the ongoing (de-)activation does not finish
+   *         within reasonable time.
+   */
+  void waitOnOperation(LockObject* lock, const QString& src, bool longWait);
 
   /**
    *
@@ -193,25 +251,37 @@ public:
   QHash<QString, QString> cachedRawHeaders;
 
   /**
-   * True when this plugin has its activation policy
-   * set to "eager"
+   * Type of operation in progress. Blocks bundle calls during activator and
+   * listener calls
    */
-  bool eagerActivation;
+  QAtomicInt operation;
+  static const int IDLE = 0;
+  static const int ACTIVATING = 1;
+  static const int DEACTIVATING = 2;
+  static const int RESOLVING = 3;
+  static const int UNINSTALLING = 4;
+  static const int UNRESOLVING = 5;
+  static const int UPDATING = 6;
 
-  /** True during the finalization of an activation. */
-  bool activating;
+  LockObject operationLock;
 
-  /** True during the state change from active to resolved. */
-  bool deactivating;
+  /** Saved exception of resolve failure. */
+  ctkPluginException* resolveFailException;
 
-  /** Saved exception of resolve failure */
-  //ctkPluginException resolveFailException;
+  /**
+   * True when this plugin has its activation policy
+   * set to "eager"
+   */
+  bool eagerActivation;
 
   /** List of ctkRequirePlugin entries. */
   QList<ctkRequirePlugin*> require;
 
 private:
 
+  /** Rember if plugin was started */
+  bool wasStarted;
+
   /**
    * Check manifest and cache certain manifest headers as variables.
    */
@@ -219,7 +289,7 @@ private:
 
   // This could potentially be run in its own thread,
   // parallelizing plugin activations
-  void start0();
+  ctkPluginException* start0();
 
   void startDependencies();