Selaa lähdekoodia

Merge branch 'plugin-fw-performance-improvements'

Sascha Zelzer 12 vuotta sitten
vanhempi
commit
6fef28fc13
48 muutettua tiedostoa jossa 1478 lisäystä ja 465 poistoa
  1. 1 0
      Libs/Core/CMakeLists.txt
  2. 155 0
      Libs/Core/ctkHighPrecisionTimer.h
  3. 3 2
      Libs/PluginFramework/CMakeLists.txt
  4. 3 0
      Libs/PluginFramework/Testing/CMakeLists.txt
  5. 0 140
      Libs/PluginFramework/Testing/Cpp/ctkPluginFrameworkTestMain.cpp
  6. 4 1
      Libs/PluginFramework/Testing/Cpp/ctkPluginFrameworkTestRunner.cpp
  7. 42 0
      Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/CMakeLists.txt
  8. 187 0
      Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminPerfTestSuite.cpp
  9. 87 0
      Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminPerfTestSuite_p.h
  10. 82 0
      Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminTestPerfActivator.cpp
  11. 48 0
      Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminTestPerfActivator_p.h
  12. 10 0
      Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/manifest_headers.cmake
  13. 9 0
      Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/target_libraries.cmake
  14. 6 2
      Libs/PluginFramework/Testing/org.commontk.eventadmintest/CMakeLists.txt
  15. 1 1
      Libs/PluginFramework/Testing/org.commontk.eventadmintest/ctkEAScenario4TestSuite.cpp
  16. 9 2
      Libs/PluginFramework/Testing/org.commontk.eventadmintest/ctkEventAdminTestActivator.cpp
  17. 58 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/CMakeLists.txt
  18. 221 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkPerfRegistryTestSuite.cpp
  19. 117 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkPerfRegistryTestSuite_p.h
  20. 21 17
      Libs/PluginFramework/ctkDictionary.cpp
  21. 48 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkTestPerfActivator_p.h
  22. 61 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkTestPerfMain.cpp
  23. 10 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/manifest_headers.cmake
  24. 9 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/target_libraries.cmake
  25. 1 1
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest/ctkPluginFrameworkTestSuite.cpp
  26. 0 95
      Libs/PluginFramework/ctkCaseInsensitiveString.cpp
  27. 0 123
      Libs/PluginFramework/ctkCaseInsensitiveString.h
  28. 3 29
      Libs/PluginFramework/ctkDictionary.h
  29. 5 3
      Libs/PluginFramework/ctkLDAPExpr.cpp
  30. 7 7
      Libs/PluginFramework/ctkLDAPExpr_p.h
  31. 5 2
      Libs/PluginFramework/ctkPluginFrameworkListeners.cpp
  32. 1 0
      Libs/PluginFramework/ctkPluginFramework_global.h
  33. 88 0
      Libs/PluginFramework/ctkServiceProperties.cpp
  34. 54 0
      Libs/PluginFramework/ctkServiceProperties_p.h
  35. 1 6
      Libs/PluginFramework/ctkServiceReference.cpp
  36. 1 1
      Libs/PluginFramework/ctkServiceReference_p.cpp
  37. 2 2
      Libs/PluginFramework/ctkServiceReference_p.h
  38. 1 1
      Libs/PluginFramework/ctkServiceRegistration.h
  39. 2 2
      Libs/PluginFramework/ctkServiceRegistration_p.h
  40. 9 2
      Libs/PluginFramework/ctkServiceSlotEntry.cpp
  41. 2 0
      Libs/PluginFramework/ctkServiceSlotEntry_p.h
  42. 1 6
      Libs/PluginFramework/service/event/ctkEvent.cpp
  43. 4 0
      Plugins/org.commontk.eventadmin/CMakeLists.txt
  44. 18 1
      Plugins/org.commontk.eventadmin/Testing/Cpp/CMakeLists.txt
  45. 70 0
      Plugins/org.commontk.eventadmin/Testing/Cpp/ctkEventAdminImplPerfTestMain.cpp
  46. 4 9
      Plugins/org.commontk.eventadmin/dispatch/ctkEALinkedQueue.cpp
  47. 1 1
      Plugins/org.commontk.eventadmin/dispatch/ctkEASignalPublisher_p.h
  48. 6 9
      Plugins/org.commontk.eventadmin/util/ctkEACyclicBarrier.cpp

+ 1 - 0
Libs/Core/CMakeLists.txt

@@ -52,6 +52,7 @@ set(KIT_SRCS
   ctkErrorLogStreamMessageHandler.h
   ctkException.cpp
   ctkException.h
+  ctkHighPrecisionTimer.h
   ctkLogger.cpp
   ctkLogger.h
   ctkHistogram.cpp

+ 155 - 0
Libs/Core/ctkHighPrecisionTimer.h

@@ -0,0 +1,155 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#ifndef CTKHIGHPRECISIONTIMER_H
+#define CTKHIGHPRECISIONTIMER_H
+
+
+#include <qglobal.h>
+
+#ifdef Q_OS_UNIX
+#include <time.h>
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <windows.h>
+#else
+#include <QTime>
+#endif
+
+#include "ctkException.h"
+
+/**
+ * \ingroup Core
+ *
+ *
+ * @brief A fast and high precision timer.
+ *
+ * This class provides a fast and high precision timer depending on
+ * platform specific API. It can be used as a QTime replacement for
+ * runtime measurements with a minimal performance overhead.
+ */
+class ctkHighPrecisionTimer {
+
+public:
+
+  inline ctkHighPrecisionTimer();
+
+  inline void start();
+
+  inline qint64 elapsedMilli();
+
+  inline qint64 elapsedMicro();
+
+private:
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+  timespec startTime;
+#elif defined(Q_OS_WIN)
+  LARGE_INTEGER timerFrequency;
+  LARGE_INTEGER startTime;
+#else
+  QTime startTime;
+#endif
+};
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+
+inline ctkHighPrecisionTimer::ctkHighPrecisionTimer()
+{
+  startTime.tv_nsec = 0;
+  startTime.tv_sec = 0;
+}
+
+inline void ctkHighPrecisionTimer::start()
+{
+  clock_gettime(CLOCK_MONOTONIC, &startTime);
+}
+
+inline qint64 ctkHighPrecisionTimer::elapsedMilli()
+{
+  timespec current;
+  clock_gettime(CLOCK_MONOTONIC, &current);
+  return (static_cast<qint64>(current.tv_sec)*1000 + current.tv_nsec/1000/1000) -
+      (static_cast<qint64>(startTime.tv_sec)*1000 + startTime.tv_nsec/1000/1000);
+}
+
+inline qint64 ctkHighPrecisionTimer::elapsedMicro()
+{
+  timespec current;
+  clock_gettime(CLOCK_MONOTONIC, &current);
+  return (static_cast<qint64>(current.tv_sec)*1000*1000 + current.tv_nsec/1000) -
+      (static_cast<qint64>(startTime.tv_sec)*1000*1000 + startTime.tv_nsec/1000);
+}
+
+#elif defined(Q_OS_WIN)
+
+inline ctkHighPrecisionTimer::ctkHighPrecisionTimer()
+{
+  if (!QueryPerformanceFrequency(&timerFrequency))
+    throw ctkRuntimeException("QueryPerformanceFrequency() failed");
+}
+
+inline void ctkHighPrecisionTimer::start()
+{
+  //DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
+  QueryPerformanceCounter(&startTime);
+  //SetThreadAffinityMask(GetCurrentThread(), oldmask);
+}
+
+inline qint64 ctkHighPrecisionTimer::elapsedMilli()
+{
+  LARGE_INTEGER current;
+  QueryPerformanceCounter(&current);
+  return (current.QuadPart - startTime.QuadPart) / (timerFrequency.QuadPart / 1000);
+}
+
+inline qint64 ctkHighPrecisionTimer::elapsedMicro()
+{
+  LARGE_INTEGER current;
+  QueryPerformanceCounter(&current);
+  return (current.QuadPart - startTime.QuadPart) / (timerFrequency.QuadPart / (1000*1000));
+}
+
+#else
+
+inline ctkHighPrecisionTimer::ctkHighPrecisionTimer()
+  : startTime(QTime::currentTime())
+{
+}
+
+inline void ctkHighPrecisionTimer::start()
+{
+  startTime = QTime::currentTime();
+}
+
+inline qint64 ctkHighPrecisionTimer::elapsedMilli()
+{
+  return startTime.elapsed();
+}
+
+inline qint64 ctkHighPrecisionTimer::elapsedMicro()
+{
+  return startTime.elapsed() * 1000;
+}
+
+#endif
+
+#endif // CTKHIGHPRECISIONTIMER_H

+ 3 - 2
Libs/PluginFramework/CMakeLists.txt

@@ -9,8 +9,7 @@ set(KIT_export_directive "CTK_PLUGINFW_EXPORT")
 
 # Source files
 set(KIT_SRCS
-  ctkCaseInsensitiveString.cpp
-  ctkDictionary.cpp
+  ctkDictionary.h
   ctkLDAPExpr.cpp
   ctkLDAPExpr_p.h
   ctkLDAPSearchFilter.cpp
@@ -62,6 +61,8 @@ set(KIT_SRCS
   ctkServiceEvent.cpp
   ctkServiceException.cpp
   ctkServiceFactory.h
+  ctkServiceProperties_p.h
+  ctkServiceProperties.cpp
   ctkServiceReference.cpp
   ctkServiceReference_p.cpp
   ctkServiceRegistration.cpp

+ 3 - 0
Libs/PluginFramework/Testing/CMakeLists.txt

@@ -23,6 +23,9 @@ set(metatypetest_plugins
 add_subdirectory(org.commontk.pluginfwtest)
 add_subdirectory(FrameworkTestPlugins)
 
+add_subdirectory(org.commontk.pluginfwtest.perf)
+add_subdirectory(org.commontk.eventadmintest.perf)
+
 add_subdirectory(org.commontk.configadmintest)
 add_subdirectory(org.commontk.eventadmintest)
 

+ 0 - 140
Libs/PluginFramework/Testing/Cpp/ctkPluginFrameworkTestMain.cpp

@@ -1,140 +0,0 @@
-/*=============================================================================
-
-  Library: CTK
-
-  Copyright (c) 2010 German Cancer Research Center,
-    Division of Medical and Biological Informatics
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-
-=============================================================================*/
-
-#include <QCoreApplication>
-#include <QDirIterator>
-#include <QTest>
-#include <QThread>
-#include <QDebug>
-
-#include <ctkPluginContext.h>
-#include <ctkPluginConstants.h>
-#include <ctkPluginFrameworkFactory.h>
-#include <ctkPluginFramework.h>
-#include <ctkPluginException.h>
-#include <ctkServiceReference.h>
-
-#include "ctkTestSuiteInterface.h"
-
-class TestRunner : public QThread
-{
-public:
-
-  TestRunner(ctkPluginContext* context, long testPluginId, int argc, char** argv)
-    : context(context), testPluginId(testPluginId), argc(argc), argv(argv)
-  {
-
-  }
-
-  void run()
-  {
-    // start the main test plugin which registers the test suites (QObject classes)
-    QSharedPointer<ctkPlugin> fwTest = context->getPlugin(testPluginId);
-    fwTest->start();
-
-    QList<ctkServiceReference> refs = context->getServiceReferences<ctkTestSuiteInterface>();
-
-    int result = 0;
-    foreach(ctkServiceReference ref, refs)
-    {
-      result += QTest::qExec(context->getService(ref), argc, argv);
-      if (result > 0) break;
-    }
-
-    if (result > 0) QCoreApplication::exit(result);
-  }
-
-private:
-
-  ctkPluginContext* context;
-  long testPluginId;
-  int argc;
-  char** argv;
-};
-
-int main(int argc, char** argv)
-{
-  QCoreApplication app(argc, argv);
-
-  app.setOrganizationName("CTK");
-  app.setOrganizationDomain("commontk.org");
-  app.setApplicationName("ctkPluginFrameworkCppTests");
-
-  QString pluginDir;
-#ifdef CMAKE_INTDIR
-  pluginDir = qApp->applicationDirPath() + "/../test_plugins/" CMAKE_INTDIR "/";
-#else
-  pluginDir = qApp->applicationDirPath() + "/test_plugins/";
-#endif
-
-  QApplication::addLibraryPath(pluginDir);
-
-  ctkProperties fwProps;
-  fwProps.insert(ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN, ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
-  fwProps.insert("pluginfw.testDir", pluginDir);
-  ctkPluginFrameworkFactory fwFactory(fwProps);
-  QSharedPointer<ctkPluginFramework> framework = fwFactory.getFramework();
-  framework->start();
-
-  ctkPluginContext* context = framework->getPluginContext();
-
-  long fwTestPluginId = -1;
-  QStringList libFilter;
-  libFilter << "*.dll" << "*.so" << "*.dylib";
-  QDirIterator dirIter(pluginDir, libFilter, QDir::Files);
-  while(dirIter.hasNext())
-  {
-    dirIter.next();
-    if (dirIter.fileName().contains("org_commontk_pluginfwtest"))
-    {
-      try
-      {
-        fwTestPluginId = context->installPlugin(QUrl::fromLocalFile(dirIter.filePath()).toString())->getPluginId();
-        break;
-      }
-      catch (const ctkPluginException& e)
-      {
-        qCritical() << e.what();
-      }
-    }
-  }
-
-  if (fwTestPluginId < 0)
-  {
-    qCritical() << "Could not find the plugin framework test plugin: org.commontk.pluginfwtest";
-  }
-
-//  QList<ctkServiceReference> refs = context->getServiceReferences("ctkTestSuiteInterface");
-
-//  int result = 0;
-//  foreach(ctkServiceReference ref, refs)
-//  {
-//    result = QTest::qExec(context->getService(ref), argc, argv);
-//  }
-
-//  return result;
-
-  TestRunner runner(context, fwTestPluginId, argc, argv);
-  runner.connect(&runner, SIGNAL(finished()), &app, SLOT(quit()));
-  runner.start();
-
-  return app.exec();
-}

+ 4 - 1
Libs/PluginFramework/Testing/Cpp/ctkPluginFrameworkTestRunner.cpp

@@ -165,7 +165,9 @@ public:
     while(dirIter.hasNext())
     {
       dirIter.next();
-      if (dirIter.fileName().contains(name))
+      QString fileName = dirIter.fileName().mid(3); // strip the "lib" prefix
+      fileName.truncate(fileName.lastIndexOf('.')); // remove the suffix
+      if (fileName == name)
       {
         try
         {
@@ -253,6 +255,7 @@ void ctkPluginFrameworkTestRunner::init(const ctkProperties& fwProps)
   foreach(ctkPluginFrameworkTestRunnerPrivate::InstallCandPair candidate,
           d->installCandidates)
   {
+    qDebug() << "Installing" << candidate.first << "," << candidate.second;
     d->installPlugin(candidate.first, candidate.second);
   }
 }

+ 42 - 0
Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/CMakeLists.txt

@@ -0,0 +1,42 @@
+project(org_commontk_eventadmintest_perf)
+
+set(PLUGIN_export_directive "org_commontk_eventadmintest_perf_EXPORT")
+
+set(PLUGIN_SRCS
+  ctkEventAdminTestPerfActivator_p.h
+  ctkEventAdminTestPerfActivator.cpp
+  ctkEventAdminPerfTestSuite_p.h
+  ctkEventAdminPerfTestSuite.cpp
+)
+
+set(PLUGIN_MOC_SRCS
+  ctkEventAdminTestPerfActivator_p.h
+  ctkEventAdminPerfTestSuite_p.h
+)
+
+set(PLUGIN_UI_FORMS
+
+)
+
+set(PLUGIN_resources
+
+)
+
+ctkFunctionGetTargetLibraries(PLUGIN_target_libraries)
+
+if(UNIX)
+  list(APPEND PLUGIN_target_libraries rt)
+endif()
+
+ctkMacroBuildPlugin(
+  NAME ${PROJECT_NAME}
+  EXPORT_DIRECTIVE ${PLUGIN_export_directive}
+  SRCS ${PLUGIN_SRCS}
+  MOC_SRCS ${PLUGIN_MOC_SRCS}
+  UI_FORMS ${PLUGIN_UI_FORMS}
+  RESOURCES ${PLUGIN_resources}
+  TARGET_LIBRARIES ${PLUGIN_target_libraries}
+  TEST_PLUGIN
+)
+
+set(eventadmin_perftest ${PROJECT_NAME} CACHE INTERNAL "Target name for the plugin containing performance tests for EventAdmin implementations")

+ 187 - 0
Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminPerfTestSuite.cpp

@@ -0,0 +1,187 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+
+#include "ctkEventAdminPerfTestSuite_p.h"
+
+#include <ctkPluginContext.h>
+#include <ctkServiceEvent.h>
+
+#include <service/event/ctkEventAdmin.h>
+#include <service/event/ctkEventConstants.h>
+
+#include <QTest>
+#include <QDebug>
+
+
+//----------------------------------------------------------------------------
+TestEventHandler::TestEventHandler(int& counter)
+  : counter(counter)
+{}
+
+//----------------------------------------------------------------------------
+void TestEventHandler::handleEvent(const ctkEvent& )
+{
+  counter++;
+}
+
+//----------------------------------------------------------------------------
+ctkEventAdminPerfTestSuite::ctkEventAdminPerfTestSuite(ctkPluginContext *context, int pluginId)
+  : pc(context)
+  , pluginId(pluginId)
+  , nSendEvents(400)
+  , nHandlers(40)
+  , nEvent1Handled(0)
+  , nEvent2Handled(0)
+  , eventAdmin(0)
+{
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::addHandlers()
+{
+  qDebug() << "Adding" << nHandlers << "event handlers";
+  for (int i = 0; i < nHandlers; ++i)
+  {
+    TestEventHandler* h1 = new TestEventHandler(nEvent1Handled);
+    handlers.push_back(h1);
+    ctkDictionary props1;
+    props1.insert(ctkEventConstants::EVENT_TOPIC, "org/bla/1");
+    handlerRegistrations.push_back(pc->registerService<ctkEventHandler>(h1, props1));
+
+
+    TestEventHandler* h2 = new TestEventHandler(nEvent2Handled);
+    handlers.push_back(h2);
+    ctkDictionary props2;
+    props2.insert(ctkEventConstants::EVENT_TOPIC, "org/bla/*");
+    handlerRegistrations.push_back(pc->registerService<ctkEventHandler>(h2, props2));
+
+    TestEventHandler* h3 = new TestEventHandler(nEvent2Handled);
+    handlers.push_back(h3);
+    ctkDictionary props3;
+    props3.insert(ctkEventConstants::EVENT_TOPIC, "org/bla/*");
+    props3.insert(ctkEventConstants::EVENT_FILTER, "(name=bla)");
+    handlerRegistrations.push_back(pc->registerService<ctkEventHandler>(h3, props3));
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::removeHandlers()
+{
+  foreach(ctkServiceRegistration sr, handlerRegistrations)
+  {
+    sr.unregister();
+  }
+  handlerRegistrations.clear();
+  qDeleteAll(handlers);
+  handlers.clear();
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::sendEvents()
+{
+  ctkEvent event1("org/bla/1");
+  for (int i = 0; i < nSendEvents; ++i)
+  {
+    eventAdmin->sendEvent(event1);
+  }
+
+  for (int i = 0; i < nSendEvents; ++i)
+  {
+    ctkDictionary props;
+    props.insert("name", "bla");
+    props.insert("level", i);
+    ctkEvent event2("org/bla/2", props);
+    eventAdmin->sendEvent(event2);
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::postEvents()
+{
+  ctkEvent event1("org/bla/1");
+  for (int i = 0; i < nSendEvents; ++i)
+  {
+    eventAdmin->postEvent(event1);
+  }
+
+  for (int i = 0; i < nSendEvents; ++i)
+  {
+    ctkDictionary props;
+    props.insert("name", "bla");
+    props.insert("level", i);
+    ctkEvent event2("org/bla/2", props);
+    eventAdmin->postEvent(event2);
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::initTestCase()
+{
+  nEvent1Handled = 0;
+  nEvent2Handled = 0;
+
+  pc->getPlugin(pluginId)->start();
+  ctkServiceReference reference = pc->getServiceReference<ctkEventAdmin>();
+  QVERIFY(reference);
+  eventAdmin = pc->getService<ctkEventAdmin>(reference);
+  QVERIFY(eventAdmin);
+
+  addHandlers();
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::testSendEvents()
+{
+  QTime t;
+  t.start();
+  sendEvents();
+  int ms = t.elapsed();
+  QCOMPARE(nEvent1Handled, nSendEvents * nHandlers);
+  QCOMPARE(nEvent2Handled, nSendEvents * nHandlers * 3);
+  qDebug() << "Sending" << 2*nSendEvents << "synchronous events took" << ms << "ms";
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::testPostEvents()
+{
+  QTime t;
+  t.start();
+  postEvents();
+  int ms = t.elapsed();
+  qDebug() << "Sending" << 2*nSendEvents << "asynchronous events took" << ms << "ms";
+  // wait a little for the asynchronous handling of events
+  QTest::qWait(10000);
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminPerfTestSuite::cleanupTestCase()
+{
+  try
+  {
+    removeHandlers();
+    pc->getPlugin(pluginId)->stop();
+  }
+  catch (const ctkInvalidArgumentException&)
+  {
+    qDebug() << "NOooooo!!!";
+  }
+}

+ 87 - 0
Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminPerfTestSuite_p.h

@@ -0,0 +1,87 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+
+#ifndef CTKEAPERFTESTSUITE_P_H
+#define CTKEAPERFTESTSUITE_P_H
+
+#include "ctkTestSuiteInterface.h"
+
+#include <service/event/ctkEventHandler.h>
+#include <ctkServiceRegistration.h>
+
+#include <QDebug>
+
+struct ctkEventAdmin;
+
+class ctkEventAdminPerfTestSuite : public QObject, public ctkTestSuiteInterface
+{
+  Q_OBJECT
+  Q_INTERFACES(ctkTestSuiteInterface)
+
+private:
+
+  ctkPluginContext* pc;
+  int pluginId;
+
+  int nSendEvents;
+  int nHandlers;
+
+  int nEvent1Handled;
+  int nEvent2Handled;
+
+  ctkEventAdmin* eventAdmin;
+
+  QList<ctkEventHandler*> handlers;
+  QList<ctkServiceRegistration> handlerRegistrations;
+
+public:
+
+  ctkEventAdminPerfTestSuite(ctkPluginContext* context, int pluginId);
+
+private:
+
+  void addHandlers();
+  void removeHandlers();
+
+  void sendEvents();
+  void postEvents();
+
+private Q_SLOTS:
+
+  void initTestCase();
+  void testSendEvents();
+  void testPostEvents();
+  void cleanupTestCase();
+};
+
+class TestEventHandler : public QObject, public ctkEventHandler
+{
+  Q_OBJECT
+  Q_INTERFACES(ctkEventHandler)
+private:
+  int& counter;
+public:
+  TestEventHandler(int& counter);
+  void handleEvent(const ctkEvent& );
+};
+
+#endif // CTKEAPERFTESTSUITE_P_H

+ 82 - 0
Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminTestPerfActivator.cpp

@@ -0,0 +1,82 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#include "ctkEventAdminTestPerfActivator_p.h"
+
+#include "ctkEventAdminPerfTestSuite_p.h"
+
+#include <QtPlugin>
+
+
+//----------------------------------------------------------------------------
+ctkEventAdminTestPerfActivator::ctkEventAdminTestPerfActivator()
+  : perfTestSuite(0)
+{
+
+}
+
+//----------------------------------------------------------------------------
+ctkEventAdminTestPerfActivator::~ctkEventAdminTestPerfActivator()
+{
+  delete perfTestSuite;
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminTestPerfActivator::start(ctkPluginContext* context)
+{
+  QString symbolicName = context->getProperty("event.impl").toString();
+  if (symbolicName.isEmpty())
+  {
+    throw ctkRuntimeException("Framework property 'event.impl' containing the symbolic "
+                              "name of the EventAdmin implementation not found!");
+  }
+
+  long eventPluginId = -1;
+  foreach(QSharedPointer<ctkPlugin> p, context->getPlugins())
+  {
+    if (p->getSymbolicName() == symbolicName)
+    {
+      eventPluginId = p->getPluginId();
+      break;
+    }
+  }
+
+  if (eventPluginId < 0)
+  {
+    QString msg = QString("The EventAdmin implementation '%1' is not installed.")
+        .arg(symbolicName);
+    throw ctkRuntimeException(msg);
+  }
+
+  perfTestSuite = new ctkEventAdminPerfTestSuite(context, eventPluginId);
+  context->registerService<ctkTestSuiteInterface>(perfTestSuite);
+}
+
+//----------------------------------------------------------------------------
+void ctkEventAdminTestPerfActivator::stop(ctkPluginContext* context)
+{
+  Q_UNUSED(context);
+
+  delete perfTestSuite;
+  perfTestSuite = 0;
+}
+
+Q_EXPORT_PLUGIN2(org_commontk_eventadmintest_perf, ctkEventAdminTestPerfActivator)

+ 48 - 0
Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/ctkEventAdminTestPerfActivator_p.h

@@ -0,0 +1,48 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+
+#ifndef CTKEVENTADMINTESTPERFACTIVATOR_H
+#define CTKEVENTADMINTESTPERFACTIVATOR_H
+
+#include <ctkPluginActivator.h>
+
+
+class ctkEventAdminTestPerfActivator : public QObject,
+    public ctkPluginActivator
+{
+  Q_OBJECT
+  Q_INTERFACES(ctkPluginActivator)
+
+public:
+
+  ctkEventAdminTestPerfActivator();
+  ~ctkEventAdminTestPerfActivator();
+
+  void start(ctkPluginContext* context);
+  void stop(ctkPluginContext* context);
+
+private:
+
+  QObject* perfTestSuite;
+};
+
+#endif // CTKEVENTADMINTESTPERFACTIVATOR_H

+ 10 - 0
Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/manifest_headers.cmake

@@ -0,0 +1,10 @@
+set(Plugin-ActivationPolicy "eager")
+set(Plugin-Name "Event Admin Performance Test Suite")
+set(Plugin-Version "0.9")
+set(Plugin-Description "Test the performance of the event admin")
+set(Plugin-Vendor "CommonTK")
+set(Plugin-DocURL "http://www.commontk.org")
+set(Plugin-ContactAddress "http://www.commontk.org")
+set(Plugin-Category "test")
+set(Plugin-Copyright "German Cancer Research Center, Division of Medical and Biological Informatics")
+set(Plugin-License "http://www.apache.org/licenses/LICENSE-2.0.html")

+ 9 - 0
Libs/PluginFramework/Testing/org.commontk.eventadmintest.perf/target_libraries.cmake

@@ -0,0 +1,9 @@
+#
+# See CMake/ctkFunctionGetTargetLibraries.cmake
+# 
+# This file should list the libraries required to build the current CTK plugin.
+# 
+
+set(target_libraries
+  CTKPluginFramework
+  )

+ 6 - 2
Libs/PluginFramework/Testing/org.commontk.eventadmintest/CMakeLists.txt

@@ -27,15 +27,19 @@ set(PLUGIN_MOC_SRCS
 )
 
 set(PLUGIN_UI_FORMS
-  
+
 )
 
 set(PLUGIN_resources
-  
+
 )
 
 ctkFunctionGetTargetLibraries(PLUGIN_target_libraries)
 
+if(UNIX)
+  list(APPEND PLUGIN_target_libraries rt)
+endif()
+
 ctkMacroBuildPlugin(
   NAME ${PROJECT_NAME}
   EXPORT_DIRECTIVE ${PLUGIN_export_directive}

+ 1 - 1
Libs/PluginFramework/Testing/org.commontk.eventadmintest/ctkEAScenario4TestSuite.cpp

@@ -234,7 +234,7 @@ void ctkEAScenario4TestSuite::initTestCase()
   eventConsumers.push_back(new ctkEAScenario4EventConsumer(pluginContext, scenario4_topics1,
                                                            1, 1, scenario4_filter1));
   eventConsumers.push_back(new ctkEAScenario4EventConsumer(pluginContext, scenario4_topics1,
-                                                           2, 2, scenario4_filter2));
+                                                           1, 1, scenario4_filter2));
   eventConsumers.push_back(new ctkEAScenario4EventConsumer(pluginContext, scenario4_topics1,
                                                            0, 0, scenario4_filter3));
   eventConsumers.push_back(new ctkEAScenario4EventConsumer(pluginContext, scenario4_topics1,

+ 9 - 2
Libs/PluginFramework/Testing/org.commontk.eventadmintest/ctkEventAdminTestActivator.cpp

@@ -35,8 +35,13 @@
 
 //----------------------------------------------------------------------------
 ctkEventAdminTestActivator::ctkEventAdminTestActivator()
-  : topicWildcardTestSuite(0), topicWildcardTestSuiteSS(0),
-    scenario1TestSuite(0), scenario1TestSuiteSS(0), scenario2TestSuite(0)
+  : topicWildcardTestSuite(0)
+  , topicWildcardTestSuiteSS(0)
+  , scenario1TestSuite(0)
+  , scenario1TestSuiteSS(0)
+  , scenario2TestSuite(0)
+  , scenario3TestSuite(0)
+  , scenario4TestSuite(0)
 {
 
 }
@@ -49,6 +54,8 @@ ctkEventAdminTestActivator::~ctkEventAdminTestActivator()
   delete scenario1TestSuite;
   delete scenario1TestSuiteSS;
   delete scenario2TestSuite;
+  delete scenario3TestSuite;
+  delete scenario4TestSuite;
 }
 
 //----------------------------------------------------------------------------

+ 58 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/CMakeLists.txt

@@ -0,0 +1,58 @@
+project(org_commontk_pluginfwtest_perf)
+
+set(PLUGIN_export_directive "org_commontk_pluginfwtest_perf_EXPORT")
+
+set(PLUGIN_SRCS
+  ctkPluginFrameworkTestPerfActivator_p.h
+  ctkPluginFrameworkTestPerfActivator.cpp
+  ctkPluginFrameworkPerfRegistryTestSuite_p.h
+  ctkPluginFrameworkPerfRegistryTestSuite.cpp
+)
+
+set(PLUGIN_MOC_SRCS
+  ctkPluginFrameworkTestPerfActivator_p.h
+  ctkPluginFrameworkPerfRegistryTestSuite_p.h
+)
+
+set(PLUGIN_UI_FORMS
+  
+)
+
+set(PLUGIN_resources
+  
+)
+
+ctkFunctionGetTargetLibraries(PLUGIN_target_libraries)
+
+if(UNIX)
+  list(APPEND PLUGIN_target_libraries rt)
+endif()
+
+ctkMacroBuildPlugin(
+  NAME ${PROJECT_NAME}
+  EXPORT_DIRECTIVE ${PLUGIN_export_directive}
+  SRCS ${PLUGIN_SRCS}
+  MOC_SRCS ${PLUGIN_MOC_SRCS}
+  UI_FORMS ${PLUGIN_UI_FORMS}
+  RESOURCES ${PLUGIN_resources}
+  TARGET_LIBRARIES ${PLUGIN_target_libraries}
+  TEST_PLUGIN
+)
+
+# =========== Build the test executable ===============
+set(SRCS
+  ctkPluginFrameworkTestPerfMain.cpp
+)
+
+set(test_executable ${PROJECT_NAME}CppTests)
+
+add_executable(${test_executable} ${SRCS})
+target_link_libraries(${test_executable}
+  ${fw_lib}
+  ${fwtestutil_lib}
+)
+
+add_dependencies(${test_executable} ${PROJECT_NAME})
+
+add_test(${PROJECT_NAME}Tests ${CPP_TEST_PATH}/${test_executable})
+set_property(TEST ${PROJECT_NAME}Tests PROPERTY LABELS ${PROJECT_NAME})

+ 221 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkPerfRegistryTestSuite.cpp

@@ -0,0 +1,221 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+
+#include "ctkPluginFrameworkPerfRegistryTestSuite_p.h"
+
+#include <ctkPluginContext.h>
+#include <ctkHighPrecisionTimer.h>
+
+#undef REGISTERED
+#include <ctkServiceEvent.h>
+
+#include <QTest>
+#include <QDebug>
+
+//----------------------------------------------------------------------------
+ctkPluginFrameworkPerfRegistryTestSuite::ctkPluginFrameworkPerfRegistryTestSuite(ctkPluginContext* context)
+  : QObject(0)
+  , pc(context)
+  , nListeners(100)
+  , nServices(1000)
+  , nRegistered(0)
+  , nUnregistering(0)
+  , nModified(0)
+{
+  this->setObjectName("ctkPluginFrameworkPerfRegistryTestSuite");
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::initTestCase()
+{
+  qDebug() << "Initialize event counters";
+
+  nRegistered    = 0;
+  nUnregistering = 0;
+  nModified      = 0;
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::cleanupTestCase()
+{
+  qDebug() << "Remove all service listeners";
+
+  for(int i = 0; i < listeners.size(); i++)
+  {
+    try
+    {
+      ctkServiceListener* l = listeners[i];
+      pc->disconnectServiceListener(l, "serviceChanged");
+    }
+    catch (const ctkException& e)
+    {
+      qDebug() << e.printStackTrace();
+    }
+  }
+  listeners.clear();
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::testAddListeners()
+{
+  addListeners(nListeners);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::addListeners(int n)
+{
+  log() << "adding" << n << "service listeners";
+  for(int i = 0; i < n; i++)
+  {
+    ctkServiceListener* l = new ctkServiceListener(this);
+    try
+    {
+      listeners.push_back(l);
+      pc->connectServiceListener(l, "serviceChanged", "(perf.service.value>=0)");
+    }
+    catch (const ctkException& e)
+    {
+      qDebug() << e.printStackTrace();
+    }
+  }
+  log() << "listener count=" << listeners.size();
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::testRegisterServices()
+{
+  qDebug() << "Register services, and check that we get #of services ("
+           << nServices << ") * #of listeners (" << nListeners << ")  REGISTERED events";
+
+  log() << "registering" << nServices << "services, listener count=" << listeners.size();
+
+  ctkHighPrecisionTimer t;
+  t.start();
+  registerServices(nServices);
+  int ms = t.elapsedMilli();
+  log() << "register took" << ms << "ms";
+  QVERIFY2(nServices * listeners.size() == nRegistered,
+           "# REGISTERED events must be same as # of registered services  * # of listeners");
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::registerServices(int n)
+{
+  QString pid("my.service.%1");
+
+  for(int i = 0; i < n; i++)
+  {
+    ctkDictionary props;
+    props.insert("service.pid", pid.arg(i));
+    props.insert("perf.service.value", i+1);
+
+    QObject* service = new PerfTestService();
+    services.push_back(service);
+    ctkServiceRegistration reg =
+        pc->registerService<IPerfTestService>(service, props);
+    regs.push_back(reg);
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::testModifyServices()
+{
+  qDebug() << "Modify all services, and check that we get #of services ("
+           << nServices << ") * #of listeners (" << nListeners << ")  MODIFIED "
+              << " events";
+
+  ctkHighPrecisionTimer t;
+  t.start();
+  modifyServices();
+  int ms = t.elapsedMilli();
+  log() << "modify took" << ms << "ms";
+  QVERIFY2(nServices * listeners.size() == nModified,
+           "# MODIFIED events must be same as # of modified services  * # of listeners");
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::modifyServices()
+{
+  log() << "modifying " << regs.size() << "services, listener count=" << listeners.size();
+
+  for(int i = 0; i < regs.size(); i++)
+  {
+    ctkServiceRegistration reg = regs[i];
+    ctkDictionary props;
+    props.insert("perf.service.value", i * 2);
+    reg.setProperties(props);
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::testUnregisterServices()
+{
+  qDebug() << "Unregister all services, and check that we get #of services ("
+           << nServices << ") * #of listeners (" << nListeners
+           << ")  UNREGISTERING events";
+
+  ctkHighPrecisionTimer t;
+  t.start();
+  unregisterServices();
+  int ms = t.elapsedMilli();
+  log() <<  "unregister took " << ms << "ms";
+  QVERIFY2(nServices * listeners.size() == nUnregistering, "# UNREGISTERING events must be same as # of (un)registered services * # of listeners");
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPerfRegistryTestSuite::unregisterServices()
+{
+  log() << "unregistering " << regs.size() << " services, listener count="
+        << listeners.size();
+  for(int i = 0; i < regs.size(); i++)
+  {
+    ctkServiceRegistration reg = regs[i];
+    reg.unregister();
+  }
+  regs.clear();
+}
+
+
+//----------------------------------------------------------------------------
+ctkServiceListener::ctkServiceListener(ctkPluginFrameworkPerfRegistryTestSuite* ts)
+  : ts(ts)
+{
+}
+
+//----------------------------------------------------------------------------
+void ctkServiceListener::serviceChanged(const ctkServiceEvent& ev)
+{
+  switch(ev.getType())
+  {
+  case ctkServiceEvent::REGISTERED:
+    ts->nRegistered++;
+    break;
+  case ctkServiceEvent::UNREGISTERING:
+    ts->nUnregistering++;
+    break;
+  case ctkServiceEvent::MODIFIED:
+    ts->nModified++;
+    break;
+  default:
+    break;
+  }
+}

+ 117 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkPerfRegistryTestSuite_p.h

@@ -0,0 +1,117 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+
+#ifndef CTKPLUGINFRAMEWORKPERFREGISTRYTESTSUITE_P_H
+#define CTKPLUGINFRAMEWORKPERFREGISTRYTESTSUITE_P_H
+
+#include "ctkTestSuiteInterface.h"
+#include "ctkServiceRegistration.h"
+
+#include <QDebug>
+
+class ctkPluginContext;
+class ctkServiceEvent;
+
+class ctkServiceListener;
+
+class ctkPluginFrameworkPerfRegistryTestSuite : public QObject, public ctkTestSuiteInterface
+{
+  Q_OBJECT
+  Q_INTERFACES(ctkTestSuiteInterface)
+
+private:
+
+  ctkPluginContext* pc;
+
+  int nListeners;
+  int nServices;
+
+  int nRegistered;
+  int nUnregistering;
+  int nModified;
+
+  QList<ctkServiceRegistration> regs;
+  QList<ctkServiceListener*> listeners;
+  QList<QObject*> services;
+
+public:
+
+  ctkPluginFrameworkPerfRegistryTestSuite(ctkPluginContext* context);
+
+  QDebug log()
+  {
+    return qDebug() << "registry_perf:";
+  }
+
+private:
+
+  friend class ctkServiceListener;
+
+  void addListeners(int n);
+  void registerServices(int n);
+  void modifyServices();
+  void unregisterServices();
+
+private Q_SLOTS:
+
+  void initTestCase();
+  void cleanupTestCase();
+
+  void testAddListeners();
+  void testRegisterServices();
+
+  void testModifyServices();
+  void testUnregisterServices();
+};
+
+class ctkServiceListener : public QObject
+{
+  Q_OBJECT
+
+private:
+
+  ctkPluginFrameworkPerfRegistryTestSuite* ts;
+
+public:
+
+  ctkServiceListener(ctkPluginFrameworkPerfRegistryTestSuite* ts);
+
+protected Q_SLOTS:
+
+  void serviceChanged(const ctkServiceEvent& ev);
+};
+
+struct IPerfTestService
+{
+  virtual ~IPerfTestService() {}
+};
+
+Q_DECLARE_INTERFACE(IPerfTestService, "org.commontk.test.PerfTestService")
+
+class PerfTestService : public QObject, public IPerfTestService
+{
+  Q_OBJECT
+  Q_INTERFACES(IPerfTestService)
+};
+
+
+#endif // CTKPLUGINFRAMEWORKPERFREGISTRYTESTSUITE_P_H

+ 21 - 17
Libs/PluginFramework/ctkDictionary.cpp

@@ -19,36 +19,40 @@
 
 =============================================================================*/
 
+#include "ctkPluginFrameworkTestPerfActivator_p.h"
 
-#include "ctkDictionary.h"
+#include "ctkPluginFrameworkPerfRegistryTestSuite_p.h"
+
+#include <QtPlugin>
 
-#include <ctkException.h>
 
 //----------------------------------------------------------------------------
-ctkDictionary::ctkDictionary()
+ctkPluginFrameworkTestPerfActivator::ctkPluginFrameworkTestPerfActivator()
+  : perfTestSuite(0)
 {
 
 }
 
 //----------------------------------------------------------------------------
-ctkDictionary::ctkDictionary(const ctkDictionary& other)
-  : Super(other)
+ctkPluginFrameworkTestPerfActivator::~ctkPluginFrameworkTestPerfActivator()
 {
+  delete perfTestSuite;
+}
 
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkTestPerfActivator::start(ctkPluginContext* context)
+{
+  perfTestSuite = new ctkPluginFrameworkPerfRegistryTestSuite(context);
+  context->registerService<ctkTestSuiteInterface>(perfTestSuite);
 }
 
 //----------------------------------------------------------------------------
-ctkDictionary::ctkDictionary(const ctkProperties& properties)
+void ctkPluginFrameworkTestPerfActivator::stop(ctkPluginContext* context)
 {
-  ctkProperties::ConstIterator end = properties.end();
-  for (ctkProperties::ConstIterator it = properties.begin(); it != end; ++it)
-  {
-    if (this->contains(it.key()))
-    {
-      QString msg("ctkProperties object contains case variants of the key: ");
-      msg += it.key();
-      throw ctkInvalidArgumentException(msg);
-    }
-    this->insert(it.key(), it.value());
-  }
+  Q_UNUSED(context);
+
+  delete perfTestSuite;
+  perfTestSuite = 0;
 }
+
+Q_EXPORT_PLUGIN2(org_commontk_pluginfwtest_perf, ctkPluginFrameworkTestPerfActivator)

+ 48 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkTestPerfActivator_p.h

@@ -0,0 +1,48 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+
+#ifndef CTKPLUGINFRAMEWORKTESTPERFACTIVATOR_H
+#define CTKPLUGINFRAMEWORKTESTPERFACTIVATOR_H
+
+#include <ctkPluginActivator.h>
+
+
+class ctkPluginFrameworkTestPerfActivator : public QObject,
+    public ctkPluginActivator
+{
+  Q_OBJECT
+  Q_INTERFACES(ctkPluginActivator)
+
+public:
+
+  ctkPluginFrameworkTestPerfActivator();
+  ~ctkPluginFrameworkTestPerfActivator();
+
+  void start(ctkPluginContext* context);
+  void stop(ctkPluginContext* context);
+
+private:
+
+  QObject* perfTestSuite;
+};
+
+#endif // CTKPLUGINFRAMEWORKTESTPERFACTIVATOR_H

+ 61 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/ctkPluginFrameworkTestPerfMain.cpp

@@ -0,0 +1,61 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#include <QCoreApplication>
+
+
+#include <ctkPluginConstants.h>
+
+#include "ctkPluginFrameworkTestRunner.h"
+
+
+int main(int argc, char** argv)
+{
+  QCoreApplication app(argc, argv);
+
+  ctkPluginFrameworkTestRunner testRunner;
+
+  app.setOrganizationName("CTK");
+  app.setOrganizationDomain("commontk.org");
+  app.setApplicationName("ctkPluginFrameworkCppTests");
+
+  QString pluginDir;
+#ifdef CMAKE_INTDIR
+  pluginDir = qApp->applicationDirPath() + "/../test_plugins/" CMAKE_INTDIR "/";
+#else
+  pluginDir = qApp->applicationDirPath() + "/test_plugins/";
+#endif
+
+  testRunner.addPluginPath(pluginDir, false);
+  testRunner.addPlugin(pluginDir, "org_commontk_pluginfwtest_perf");
+  testRunner.startPluginOnRun("org.commontk.pluginfwtest.perf");
+
+  ctkProperties fwProps;
+  fwProps.insert(ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN, ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
+  fwProps.insert("pluginfw.testDir", pluginDir);
+
+#if defined(Q_CC_GNU) && ((__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 5)))
+  fwProps.insert(ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS, QVariant::fromValue<QLibrary::LoadHints>(QLibrary::ExportExternalSymbolsHint));
+#endif
+
+  testRunner.init(fwProps);
+  return testRunner.run(argc, argv);
+}

+ 10 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/manifest_headers.cmake

@@ -0,0 +1,10 @@
+set(Plugin-ActivationPolicy "eager")
+set(Plugin-Name "Framework Registry Performance Test Suite")
+set(Plugin-Version "0.9")
+set(Plugin-Description "Test the performance of the service registry")
+set(Plugin-Vendor "CommonTK")
+set(Plugin-DocURL "http://www.commontk.org")
+set(Plugin-ContactAddress "http://www.commontk.org")
+set(Plugin-Category "test")
+set(Plugin-Copyright "German Cancer Research Center, Division of Medical and Biological Informatics")
+set(Plugin-License "http://www.apache.org/licenses/LICENSE-2.0.html")

+ 9 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest.perf/target_libraries.cmake

@@ -0,0 +1,9 @@
+#
+# See CMake/ctkFunctionGetTargetLibraries.cmake
+# 
+# This file should list the libraries required to build the current CTK plugin.
+# 
+
+set(target_libraries
+  CTKPluginFramework
+  )

+ 1 - 1
Libs/PluginFramework/Testing/org.commontk.pluginfwtest/ctkPluginFrameworkTestSuite.cpp

@@ -516,7 +516,7 @@ void ctkPluginFrameworkTestSuite::frame070a()
   {
     pA->update(urk);
   }
-  catch (const ctkPluginException& pe)
+  catch (const ctkPluginException& /*pe*/)
   {
     QFAIL("framework test plug-in, update without new plug-in source :FRAME070A:FAIL");
   }

+ 0 - 95
Libs/PluginFramework/ctkCaseInsensitiveString.cpp

@@ -1,95 +0,0 @@
-/*=============================================================================
-
-  Library: CTK
-
-  Copyright (c) German Cancer Research Center,
-    Division of Medical and Biological Informatics
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-
-=============================================================================*/
-
-
-#include "ctkCaseInsensitiveString.h"
-
-#include <QHash>  // for qHash(const QString&)
-
-//----------------------------------------------------------------------------
-ctkCaseInsensitiveString::ctkCaseInsensitiveString()
-{
-}
-
-//----------------------------------------------------------------------------
-ctkCaseInsensitiveString::ctkCaseInsensitiveString(const char* str)
-  : str(str)
-{
-}
-
-//----------------------------------------------------------------------------
-ctkCaseInsensitiveString::ctkCaseInsensitiveString(const QString& str)
-  : str(str)
-{
-}
-
-//----------------------------------------------------------------------------
-ctkCaseInsensitiveString::ctkCaseInsensitiveString(const ctkCaseInsensitiveString& str)
-  : str(str.str)
-{
-}
-
-//----------------------------------------------------------------------------
-ctkCaseInsensitiveString& ctkCaseInsensitiveString::operator=(const ctkCaseInsensitiveString& str)
-{
-  this->str = str.str;
-  return *this;
-}
-
-//----------------------------------------------------------------------------
-bool ctkCaseInsensitiveString::operator==(const ctkCaseInsensitiveString& str) const
-{
-  return this->str.toLower() == str.str.toLower();
-}
-
-//----------------------------------------------------------------------------
-bool ctkCaseInsensitiveString::operator<(const ctkCaseInsensitiveString& str) const
-{
-  return this->str.toLower() < str.str.toLower();
-}
-
-//----------------------------------------------------------------------------
-ctkCaseInsensitiveString::operator QString() const
-{
-  return this->str;
-}
-
-//----------------------------------------------------------------------------
-uint qHash(const ctkCaseInsensitiveString& str)
-{
-  return qHash(QString(str).toLower());
-}
-
-//----------------------------------------------------------------------------
-QDataStream& operator<<(QDataStream &out, const ctkCaseInsensitiveString& str)
-{
-  out << QString(str);
-  return out;
-}
-
-//----------------------------------------------------------------------------
-QDataStream& operator>>(QDataStream &in, ctkCaseInsensitiveString& str)
-{
-  QString inStr;
-  in >> inStr;
-  str = inStr;
-  return in;
-}

+ 0 - 123
Libs/PluginFramework/ctkCaseInsensitiveString.h

@@ -1,123 +0,0 @@
-/*=============================================================================
-
-  Library: CTK
-
-  Copyright (c) German Cancer Research Center,
-    Division of Medical and Biological Informatics
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-
-=============================================================================*/
-
-
-#ifndef CTKCASEINSENSITIVESTRING_P_H
-#define CTKCASEINSENSITIVESTRING_P_H
-
-#include <QString>
-
-#include <ctkPluginFrameworkExport.h>
-
-/**
- * \ingroup PluginFramework
- *
- * ctkCaseInsensitiveString wraps a QString and can be
- * used in Qt container classes as a key type representing
- * case insensitive strings. However, case is preserved when
- * retrieving the actual QString.
- */
-class CTK_PLUGINFW_EXPORT ctkCaseInsensitiveString
-{
-
-public:
-
-  /**
-   * Wraps a null QString.
-   */
-  ctkCaseInsensitiveString();
-
-  /**
-   * Wraps the given character sequence.
-   *
-   * @param str The characters to be wrapped by this ctkCaseInsensitiveString
-   */
-  ctkCaseInsensitiveString(const char* str);
-
-  /**
-   * Wraps the given QString.
-   *
-   * @param str The QString to be wrapped by this ctkCaseInsensitiveString
-   */
-  ctkCaseInsensitiveString(const QString& str);
-
-  /**
-   * Copy constructor.
-   *
-   * @param str The ctkCaseInsensitiveString instance to copy.
-   */
-  ctkCaseInsensitiveString(const ctkCaseInsensitiveString& str);
-
-  /**
-   * Assignment operator.
-   *
-   * @param str The ctkCaseInsensitiveString instance which should be
-   *        assigned.
-   */
-  ctkCaseInsensitiveString& operator=(const ctkCaseInsensitiveString& str);
-
-  /**
-   * String comparison ignoring case.
-   *
-   * @param str The string with which to compare this instance.
-   * @return <code>true</code> if both strings are equal after being
-   *         converted to lower case strings, <code>false</code> otherwise.
-   */
-  bool operator==(const ctkCaseInsensitiveString& str) const;
-
-  /**
-   * Less than operator ignoring case.
-   *
-   * @param str The string with which to compare this instance.
-   * @return <code>true</code> if the lower case variant of the
-   *         current string is lexicographically less then
-   *         the lower case variant of <code>str</code>, <code>false</code>
-   *         otherwise.
-   */
-  bool operator<(const ctkCaseInsensitiveString& str) const;
-
-  /**
-   * Converts this ctkCaseInsensitiveString instance to a QString,
-   * preserving the original case.
-   */
-  operator QString() const;
-
-private:
-
-  QString str;
-};
-
-/**
- * \ingroup PluginFramework
- * @{
- *
- * Returns a hash value for the lower case string.
- *
- * @param str The string to be hashed.
- */
-uint CTK_PLUGINFW_EXPORT qHash(const ctkCaseInsensitiveString& str);
-
-CTK_PLUGINFW_EXPORT QDataStream& operator<<(QDataStream &out, const ctkCaseInsensitiveString& str);
-CTK_PLUGINFW_EXPORT QDataStream& operator>>(QDataStream &in, ctkCaseInsensitiveString& str);
-
-/** @}*/
-
-#endif // CTKCASEINSENSITIVESTRING_P_H

+ 3 - 29
Libs/PluginFramework/ctkDictionary.h

@@ -25,41 +25,15 @@
 
 #include <ctkPluginFrameworkExport.h>
 
-#include "ctkCaseInsensitiveString.h"
 #include "ctkPluginFramework_global.h"
 
 /**
  * \ingroup PluginFramework
  *
- * A QHash based dictionary class with case-insensitive keys. This class
- * uses ctkCaseInsensitiveString as key type and QVariant as values. Due
- * to the conversion capabilities of ctkCaseInsensitiveString, QString objects
- * can be used transparantly to insert or retrieve key-value pairs.
+ * A typedef for a QString to QVariant hash map. Typically used for service properties.
  *
- * <p>
- * Note that the case of the keys will be preserved.
+ * \note This is equivalent to using ctkProperties.
  */
-class CTK_PLUGINFW_EXPORT ctkDictionary : public QHash<ctkCaseInsensitiveString, QVariant>
-{
-
-public:
-
-  ctkDictionary();
-  ctkDictionary(const ctkDictionary& other);
-
-  /**
-   * Constructs a ctkDictionary object by using the entries of a
-   * ctkProperties object. The keys in <code>properties</code> must
-   * not contain case-variants of the same string.
-   *
-   * @param properties The ctkProperties object from which to copy the key-value pairs.
-   * @throws ctkInvalidArgumentException if <code>properties</code> contains case-variants of a key
-   */
-  ctkDictionary(const ctkProperties& properties);
-
-protected:
-
-  typedef QHash<ctkCaseInsensitiveString, QVariant> Super;
-};
+typedef QHash<QString,QVariant> ctkDictionary;
 
 #endif // CTKDICTIONARY_H

+ 5 - 3
Libs/PluginFramework/ctkLDAPExpr.cpp

@@ -281,11 +281,13 @@ bool ctkLDAPExpr::query( const QString &filter, const ctkDictionary &pd )
 }
 
 //----------------------------------------------------------------------------
-bool ctkLDAPExpr::evaluate( const ctkDictionary &p, bool matchCase ) const
+bool ctkLDAPExpr::evaluate( const ctkServiceProperties &p, bool matchCase ) const
 {
   if ((d->m_operator & SIMPLE) != 0) {
-    return compare(p[ matchCase ? d->m_attrName : d->m_attrName.toLower() ],
-      d->m_operator, d->m_attrValue);
+    // try case sensitive match first
+    int index = p.findCaseSensitive(d->m_attrName);
+    if (index < 0 && !matchCase) index = p.find(d->m_attrName);
+    return index < 0 ? false : compare(p.value(index), d->m_operator, d->m_attrValue);
   } else { // (d->m_operator & COMPLEX) != 0
     switch (d->m_operator) {
     case AND:

+ 7 - 7
Libs/PluginFramework/ctkLDAPExpr_p.h

@@ -23,7 +23,7 @@ limitations under the License.
 #define CTKLDAPEXPR_P_H
 
 #include "ctkPluginConstants.h"
-#include "ctkDictionary.h"
+#include "ctkServiceProperties_p.h"
 
 #include <QString>
 #include <QHash>
@@ -120,13 +120,13 @@ public:
    */
   bool isNull() const;
 
-  //! 
+  //!
   static bool query(const QString &filter, const ctkDictionary &pd);
 
   //! Evaluate this LDAP filter.
-  bool evaluate(const ctkDictionary &p, bool matchCase) const;
+  bool evaluate(const ctkServiceProperties &p, bool matchCase) const;
 
-  //! 
+  //!
   const QString toString() const;
 
 
@@ -149,13 +149,13 @@ private:
   //!
   bool compare(const QVariant &obj, int op, const QString &s) const;
 
-  //! 
+  //!
   static bool compareString(const QString &s1, int op, const QString &s2);
 
-  //! 
+  //!
   static QString fixupString(const QString &s);
 
-  //! 
+  //!
   static bool patSubstr(const QString &s, const QString &pat);
 
   //!

+ 5 - 2
Libs/PluginFramework/ctkPluginFrameworkListeners.cpp

@@ -113,12 +113,15 @@ QSet<ctkServiceSlotEntry> ctkPluginFrameworkListeners::getMatchingServiceSlots(
   QMutexLocker lock(&mutex); Q_UNUSED(lock);
 
   QSet<ctkServiceSlotEntry> set;
+  set.reserve(serviceSet.size());
   // Check complicated or empty listener filters
   int n = 0;
-  foreach (ctkServiceSlotEntry sse, complicatedListeners)
+  ctkLDAPExpr expr;
+  foreach (const ctkServiceSlotEntry& sse, complicatedListeners)
   {
     ++n;
-    if (sse.getLDAPExpr().isNull() || sse.getLDAPExpr().evaluate(sr.d_func()->getProperties(), false))
+    expr = sse.getLDAPExpr();
+    if (expr.isNull() || expr.evaluate(sr.d_func()->getProperties(), false))
     {
       set.insert(sse);
     }

+ 1 - 0
Libs/PluginFramework/ctkPluginFramework_global.h

@@ -32,6 +32,7 @@
  * @{
  */
 typedef QHash<QString, QVariant> ctkProperties;
+typedef ctkProperties ctkDictionary;
 
 #if QT_VERSION < 0x040700
 #include <QSharedPointer>

+ 88 - 0
Libs/PluginFramework/ctkServiceProperties.cpp

@@ -0,0 +1,88 @@
+/*=============================================================================
+
+Library: CTK
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=============================================================================*/
+
+#include "ctkServiceProperties_p.h"
+
+#include <ctkException.h>
+
+//----------------------------------------------------------------------------
+ctkServiceProperties::ctkServiceProperties(const ctkProperties& props)
+{
+  for(ctkProperties::ConstIterator i = props.begin(), end = props.end();
+      i != end; ++i)
+  {
+    if (find(i.key()) != -1)
+    {
+      QString msg("ctkProperties object contains case variants of the key: ");
+      msg += i.key();
+      throw ctkInvalidArgumentException(msg);
+    }
+    ks.append(i.key());
+    vs.append(i.value());
+  }
+}
+
+//----------------------------------------------------------------------------
+QVariant ctkServiceProperties::value(const QString &key) const
+{
+  int index = find(key);
+  if (index < 0) return QVariant();
+  return vs[index];
+}
+
+//----------------------------------------------------------------------------
+QVariant ctkServiceProperties::value(int index) const
+{
+  return (index < 0 || index >= vs.size()) ? QVariant() : vs[index];
+}
+
+//----------------------------------------------------------------------------
+QStringList ctkServiceProperties::keys() const
+{
+  QStringList result;
+  for(int i = 0; i < ks.size(); ++i)
+  {
+    result.append(ks[i]);
+  }
+  return result;
+}
+
+//----------------------------------------------------------------------------
+int ctkServiceProperties::find(const QString &key) const
+{
+  for (int i = 0; i < ks.size(); ++i)
+  {
+    if (ks[i].compare(key, Qt::CaseInsensitive) == 0)
+      return i;
+  }
+  return -1;
+}
+
+//----------------------------------------------------------------------------
+int ctkServiceProperties::findCaseSensitive(const QString &key) const
+{
+  for (int i = 0; i < ks.size(); ++i)
+  {
+    if (ks[i] == key)
+      return i;
+  }
+  return -1;
+}

+ 54 - 0
Libs/PluginFramework/ctkServiceProperties_p.h

@@ -0,0 +1,54 @@
+/*=============================================================================
+
+Library: CTK
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=============================================================================*/
+
+#ifndef CTKSERVICEPROPERTIES_P_H
+#define CTKSERVICEPROPERTIES_P_H
+
+#include <QVarLengthArray>
+#include <QVariant>
+
+#include "ctkPluginFramework_global.h"
+
+class ctkServiceProperties
+{
+
+private:
+
+  QVarLengthArray<QString,10> ks;
+  QVarLengthArray<QVariant,10> vs;
+
+  QMap<QString, QVariant> map;
+
+public:
+
+  ctkServiceProperties(const ctkProperties& props);
+
+  QVariant value(const QString& key) const;
+  QVariant value(int index) const;
+
+  int find(const QString& key) const;
+  int findCaseSensitive(const QString& key) const;
+
+  QStringList keys() const;
+
+};
+
+#endif // CTKSERVICEPROPERTIES_P_H

+ 1 - 6
Libs/PluginFramework/ctkServiceReference.cpp

@@ -92,12 +92,7 @@ QStringList ctkServiceReference::getPropertyKeys() const
 
   QMutexLocker lock(&d->registration->propsLock);
 
-  QStringList result;
-  foreach (ctkCaseInsensitiveString key, d->registration->properties.keys())
-  {
-    result << key;
-  }
-  return result;
+  return d->registration->properties.keys();
 }
 
 //----------------------------------------------------------------------------

+ 1 - 1
Libs/PluginFramework/ctkServiceReference_p.cpp

@@ -168,7 +168,7 @@ bool ctkServiceReferencePrivate::ungetService(QSharedPointer<ctkPlugin> plugin,
 }
 
 //----------------------------------------------------------------------------
-ctkDictionary ctkServiceReferencePrivate::getProperties() const
+const ctkServiceProperties& ctkServiceReferencePrivate::getProperties() const
 {
   return registration->properties;
 }

+ 2 - 2
Libs/PluginFramework/ctkServiceReference_p.h

@@ -26,7 +26,7 @@
 #include <QAtomicInt>
 #include <QSharedPointer>
 
-#include "ctkDictionary.h"
+#include "ctkServiceProperties_p.h"
 
 class QObject;
 
@@ -70,7 +70,7 @@ public:
    * @return A ctkDictionary object containing properties or being empty
    *         if service has been removed.
    */
-  ctkDictionary getProperties() const;
+  const ctkServiceProperties& getProperties() const;
 
   /**
    * Returns the property value to which the specified property key is mapped

+ 1 - 1
Libs/PluginFramework/ctkServiceRegistration.h

@@ -144,7 +144,7 @@ public:
    * @see ctkPluginContext#ungetService
    * @see ctkServiceFactory#ungetService
    */
-  virtual void unregister();
+  void unregister();
 
   bool operator<(const ctkServiceRegistration& o) const;
 

+ 2 - 2
Libs/PluginFramework/ctkServiceRegistration_p.h

@@ -26,7 +26,7 @@
 #include <QHash>
 #include <QMutex>
 
-#include "ctkDictionary.h"
+#include "ctkServiceProperties_p.h"
 #include "ctkServiceReference.h"
 
 
@@ -75,7 +75,7 @@ public:
   /**
    * Service properties.
    */
-  ctkDictionary properties;
+  ctkServiceProperties properties;
 
   /**
    * Plugins dependent on this service. Integer is used as

+ 9 - 2
Libs/PluginFramework/ctkServiceSlotEntry.cpp

@@ -37,7 +37,8 @@ public:
   ctkServiceSlotEntryData(QSharedPointer<ctkPlugin> p, QObject* receiver,
                           const char* slot)
     : plugin(p), receiver(receiver),
-      slot(slot), removed(false)
+      slot(slot), removed(false),
+      hashValue(0)
   {
 
   }
@@ -69,6 +70,7 @@ public:
   const char* slot;
   bool removed;
 
+  uint hashValue;
 };
 
 //----------------------------------------------------------------------------
@@ -171,5 +173,10 @@ ctkLDAPExpr::LocalCache& ctkServiceSlotEntry::getLocalCache() const
 //----------------------------------------------------------------------------
 uint qHash(const ctkServiceSlotEntry& serviceSlot)
 {
-  return qHash(serviceSlot.getPlugin());
+  if (serviceSlot.d->hashValue == 0)
+  {
+    serviceSlot.d->hashValue = qHash(serviceSlot.d->plugin) * 4 +
+        qHash(serviceSlot.d->receiver) * 2 + qHash(serviceSlot.d->slot);
+  }
+  return serviceSlot.d->hashValue;
 }

+ 2 - 0
Libs/PluginFramework/ctkServiceSlotEntry_p.h

@@ -80,6 +80,8 @@ public:
 
 private:
 
+  friend uint qHash(const ctkServiceSlotEntry& serviceSlot);
+
   QExplicitlySharedDataPointer<ctkServiceSlotEntryData> d;
 
 };

+ 1 - 6
Libs/PluginFramework/service/event/ctkEvent.cpp

@@ -143,12 +143,7 @@ bool ctkEvent::containsProperty(const QString& name) const
 //----------------------------------------------------------------------------
 QStringList ctkEvent::getPropertyNames() const
 {
-  QStringList result;
-  foreach (ctkCaseInsensitiveString key, d->properties.keys())
-  {
-    result << key;
-  }
-  return result;
+  return d->properties.keys();
 }
 
 //----------------------------------------------------------------------------

+ 4 - 0
Plugins/org.commontk.eventadmin/CMakeLists.txt

@@ -114,6 +114,10 @@ set(PLUGIN_CACHED_RESOURCEFILES
 
 ctkFunctionGetTargetLibraries(PLUGIN_target_libraries)
 
+if(UNIX)
+  list(APPEND PLUGIN_target_libraries rt)
+endif()
+
 ctkMacroBuildPlugin(
   EXPORT_DIRECTIVE ${PLUGIN_export_directive}
   SRCS ${PLUGIN_SRCS}

+ 18 - 1
Plugins/org.commontk.eventadmin/Testing/Cpp/CMakeLists.txt

@@ -8,9 +8,11 @@ set(MY_MOC_CXX )
 
 #QT4_WRAP_CPP(MY_MOC_CXX ${MOC_SRCS})
 
+# Create a test for this EventAdmin implementation
+
 set(test_executable ${PROJECT_NAME}CppTests)
 
-add_executable(${test_executable} ${SRCS} ${MY_MOC_CXX})
+add_executable(${test_executable} ctkEventAdminImplTestMain.cpp)
 target_link_libraries(${test_executable}
   ${fw_lib}
   ${fwtestutil_lib}
@@ -20,3 +22,18 @@ add_dependencies(${test_executable} ${PROJECT_NAME} ${eventadmin_test})
 
 add_test(${PROJECT_NAME}Tests ${CPP_TEST_PATH}/${test_executable})
 set_property(TEST ${PROJECT_NAME}Tests PROPERTY LABELS ${PROJECT_NAME})
+
+# Create a performance test for this EventAdmin implementation
+
+set(test_executable ${PROJECT_NAME}PerfTests)
+
+add_executable(${test_executable} ctkEventAdminImplPerfTestMain.cpp)
+target_link_libraries(${test_executable}
+  ${fw_lib}
+  ${fwtestutil_lib}
+)
+
+add_dependencies(${test_executable} ${PROJECT_NAME} ${eventadmin_perftest})
+
+add_test(${PROJECT_NAME}PerfTests ${CPP_TEST_PATH}/${test_executable})
+set_property(TEST ${PROJECT_NAME}PerfTests PROPERTY LABELS ${PROJECT_NAME})

+ 70 - 0
Plugins/org.commontk.eventadmin/Testing/Cpp/ctkEventAdminImplPerfTestMain.cpp

@@ -0,0 +1,70 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+
+#include <QCoreApplication>
+
+#include <ctkConfig.h>
+#include <ctkPluginConstants.h>
+
+#include <Testing/Cpp/ctkPluginFrameworkTestRunner.h>
+
+
+int main(int argc, char** argv)
+{
+  QCoreApplication app(argc, argv);
+
+  ctkPluginFrameworkTestRunner testRunner;
+
+  app.setOrganizationName("CTK");
+  app.setOrganizationDomain("commontk.org");
+  app.setApplicationName("ctkEventAdminImplCppTests");
+
+  QString pluginDir;
+#ifdef CMAKE_INTDIR
+  pluginDir = CTK_PLUGIN_DIR CMAKE_INTDIR "/";
+#else
+  pluginDir = CTK_PLUGIN_DIR;
+#endif
+
+  QString testpluginDir;
+#ifdef CMAKE_INTDIR
+  testpluginDir = qApp->applicationDirPath() + "/../test_plugins/" CMAKE_INTDIR "/";
+#else
+  testpluginDir = qApp->applicationDirPath() + "/test_plugins/";
+#endif
+
+  testRunner.addPluginPath(pluginDir, false);
+  testRunner.addPlugin(testpluginDir, "org_commontk_eventadmintest_perf");
+  testRunner.addPlugin(pluginDir, "org_commontk_eventadmin");
+  testRunner.addPlugin(pluginDir, "org_commontk_log");
+  testRunner.startPluginOnRun("org.commontk.eventadmintest.perf");
+
+  ctkProperties fwProps;
+  fwProps.insert(ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN, ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
+  fwProps.insert("pluginfw.testDir", testpluginDir);
+  fwProps.insert("event.impl", "org.commontk.eventadmin");
+
+  fwProps.insert("org.commontk.eventadmin.ThreadPoolSize", 10);
+
+  testRunner.init(fwProps);
+  return testRunner.run(argc, argv);
+}

+ 4 - 9
Plugins/org.commontk.eventadmin/dispatch/ctkEALinkedQueue.cpp

@@ -25,10 +25,7 @@
 #include "ctkEAInterruptibleThread_p.h"
 #include "ctkEAInterruptedException_p.h"
 
-// for ctk::msecsTo() - remove after switching to Qt 4.7
-#include <ctkUtils.h>
-
-#include <QDateTime>
+#include <ctkHighPrecisionTimer.h>
 
 
 ctkEALinkedQueue::ctkEALinkedQueue()
@@ -125,9 +122,8 @@ ctkEARunnable* ctkEALinkedQueue::poll(long msecs)
     QMutexLocker l(&putLock_);
     try {
       qint64 waitTime = static_cast<qint64>(msecs);
-      //TODO Use Qt4.7 API
-      //long start = (msecs <= 0)? 0 : System.currentTimeMillis();
-      QDateTime start = QDateTime::currentDateTime();
+      ctkHighPrecisionTimer t;
+      t.start();
       ++waitingForTake_;
       forever
       {
@@ -140,8 +136,7 @@ ctkEARunnable* ctkEALinkedQueue::poll(long msecs)
         else
         {
           ctkEAInterruptibleThread::currentThread()->wait(&putLock_, &putLockWait_, waitTime);
-          //waitTime = msecs - (System.currentTimeMillis() - start);
-          waitTime = static_cast<qint64>(msecs) - ctk::msecsTo(start, QDateTime::currentDateTime());
+          waitTime = static_cast<qint64>(msecs) - t.elapsedMilli();
         }
       }
     }

+ 1 - 1
Plugins/org.commontk.eventadmin/dispatch/ctkEASignalPublisher_p.h

@@ -27,7 +27,7 @@
 
 #include <service/event/ctkEvent.h>
 
-struct ctkEventAdminService;
+class ctkEventAdminService;
 
 class ctkEASignalPublisher : public QObject
 {

+ 6 - 9
Plugins/org.commontk.eventadmin/util/ctkEACyclicBarrier.cpp

@@ -22,10 +22,6 @@
 
 #include "ctkEACyclicBarrier_p.h"
 
-// for ctk::msecsTo() - remove after switching to Qt 4.7
-#include <ctkUtils.h>
-
-#include <QDateTime>
 #include <QRunnable>
 #include <QDebug>
 
@@ -35,6 +31,8 @@
 #include "ctkEATimeoutException_p.h"
 #include "ctkEABrokenBarrierException_p.h"
 
+#include "ctkHighPrecisionTimer.h"
+
 ctkEACyclicBarrier::ctkEACyclicBarrier(int parties, ctkEARunnable* command)
   : parties_(parties), broken_(false), barrierCommand_(command),
     count_(parties), resets_(0)
@@ -90,7 +88,7 @@ int ctkEACyclicBarrier::doBarrier(bool timed, long msecs)
 
   ctkEAInterruptibleThread* currThread = ctkEAInterruptibleThread::currentThread();
   Q_ASSERT(currThread != 0); // ctkEACyclicBarrier can only be used with ctkEAInterruptibleThread
-  
+
   if (broken_)
   {
     throw ctkEABrokenBarrierException(index);
@@ -131,7 +129,8 @@ int ctkEACyclicBarrier::doBarrier(bool timed, long msecs)
   else
   { // wait until next reset
     int r = resets_;
-    QDateTime startTime = QDateTime::currentDateTime();
+    ctkHighPrecisionTimer t;
+    t.start();
     qint64 waitTime = static_cast<qint64>(msecs);
     forever
     {
@@ -165,9 +164,7 @@ int ctkEACyclicBarrier::doBarrier(bool timed, long msecs)
       }
       else if (timed)
       {
-        //TODO use Qt 4.7 API
-        //waitTime = msecs - QDateTime::toMSecs(startTime);
-        waitTime = static_cast<qint64>(msecs) - ctk::msecsTo(startTime, QDateTime::currentDateTime());
+        waitTime = static_cast<qint64>(msecs) - t.elapsedMilli();
         if  (waitTime <= 0)
         {
           broken_ = true;