소스 검색

EventAdmin: subscribed slot connection type can now be specified.

Sascha Zelzer 13 년 전
부모
커밋
fad4bbc83d

+ 26 - 4
Libs/PluginFramework/Documentation/CTKPluginFramework_EventAdmin.dox

@@ -18,6 +18,9 @@ Events are composed of two attributes:
 - A \b topic defining the nature of the event. Topic names are usually arranged in a hierarchical namespace, where slashes are used to separate the levels (i.e. "org/commontk/PluginFrameworkEvent/STARTED") and
 - A set of \b properties describing the event.
 
+Read the <a href="http://www.osgi.org/Download/File?url=/download/r4v42/r4.cmpn.pdf">
+EventAdmin Service Specifications</a> (Chapter 113) for an in-depth explanation.
+
 \section EventAdmin_CreatePublisher Creating an Event Publisher
 
 An event publisher can either be a simple C++ class that creates events and sends
@@ -38,6 +41,13 @@ method, such as:
 
 \snippet EventAdmin-Intro/ctkSnippetReportManager.h Publish event async
 
+Synchronous event delivery is significantly more expensive than asynchronous
+delivery. Even for synchronous delivery event notifications could be
+handled in a separate thread (depending on the EventAdmin implementation). This
+implies that <code>sendEvent()</code> callers should generally not hold any
+locks when calling this method. Asynchronous delivery should be preferred
+over the synchronous delivery.
+
 \subsection EventAdmin_CreatePublisher_Signal Using a Qt signal
 
 Using a Qt signal to publish an event requires declaring a signal and registering (publishing)
@@ -66,8 +76,14 @@ tied to a specific event topic.
 \section EventAdmin_CreateHandler Creating and registering an Event Handler
 
 An event handler can either be a class implementing the <code>ctkEventHandler</code>
-interface which is registered as a service object or a Qt slot which is registered with the Event Admin (subscribed to certain
-topics).
+interface which is registered as a service object or a Qt slot which is registered
+with the Event Admin (subscribed to certain topics).
+
+Event handlers should not spend too long in the event handling method. This will
+prevent other handlers from being notified. Long running operations should be executed
+in their own thread.
+
+Note that in general, your event handling code will be called from a separate thread.
 
 \subsection EventAdmin_CreateHandler_Service Event Handler as a Service
 
@@ -106,12 +122,18 @@ You can use the same expressions for <code>EVENT_TOPIC</code> and
 <code>EVENT_FILTER</code> as in the examples above for registering the event
 handler as a service object implementing <code>ctkEventHandler</code>.
 
+Using Qt slots as Event Handlers will makes it easy to ensure that the event
+handling code is executed in the receiver's thread (the default connection type
+is Qt::AutoConnection).
+
 \subsection EventAdmin_CreateHandler_Compare Comparison
 
 Registering an event handler using either the <code>ctkEventHandler</code> interface or
 a Qt slot involves approximately the same amount of code. However, using slots
-will be less performant (which might be neglectable, depending on your use case). Further,
-subscribing slots means that you require a registered Event Admin service implementation.
+will be less performant (which might be neglectable, depending on your use case) but the
+code will be automatically synchronized with the receiver thread.
+
+Further, subscribing slots means that you require a registered Event Admin service implementation.
 The <code>ctkEventHandler</code> approach does not need to know anything about the Event
 Admin, since you register your handler as a service object in the framework.
 

+ 12 - 2
Libs/PluginFramework/service/event/ctkEventAdmin.h

@@ -108,7 +108,9 @@ struct ctkEventAdmin
   /**
    * Subsribe for (observe) events. The slot is called whenever an event is sent
    * which matches the topic string and LDAP search expression contained
-   * in the properties.
+   * in the properties. Slots are executed either in the event delivery thread of
+   * the Event Admin implemenation or in the subscriber's thread, depending on
+   * the <code>type</code> argument.
    *
    * Slots should be registered with a property ctkEventConstants::EVENT_TOPIC.
    * The value being a QString or QStringList object that describes which
@@ -135,11 +137,19 @@ struct ctkEventAdmin
    * @param subscriber The owner of the slot.
    * @param member The slot in normalized form.
    * @param properties A map containing topics and a filter expression.
+   * @param type One of Qt::AutoConnection, Qt::DirectConnection, Qt::QueuedConnection, or
+   *        Qt::BlockingQueuedConnection. Only a direct or blocking queued connection
+   *        ensures that calls to <code>sendEvent()</code> will block until all event
+   *        handlers completed their tasks.
    * @return Returns an id which can be used to update the properties.
    *
+   * @throws std::invalid_argument If <code>subscriber</code> or <code>member</code> is 0
+   *         or <code>type</code> is invalid.
+   *
    * @see unsubscribeSlot(qlonglong)
    */
-  virtual qlonglong subscribeSlot(const QObject* subscriber, const char* member, const ctkDictionary& properties) = 0;
+  virtual qlonglong subscribeSlot(const QObject* subscriber, const char* member,
+                                  const ctkDictionary& properties, Qt::ConnectionType type = Qt::AutoConnection) = 0;
 
   /**
    * Unsubscribe a previously subscribed slot. Use this method to allow the EventAdmin

+ 11 - 2
Plugins/org.commontk.eventadmin/ctkEventAdminService.cpp

@@ -150,10 +150,19 @@ void ctkEventAdminService::unpublishSignal(const QObject* publisher, const char*
   }
 }
 
-qlonglong ctkEventAdminService::subscribeSlot(const QObject* subscriber, const char* member, const ctkDictionary& properties)
+qlonglong ctkEventAdminService::subscribeSlot(const QObject* subscriber, const char* member,
+                                              const ctkDictionary& properties, Qt::ConnectionType type)
 {
+  if (subscriber == 0) throw std::invalid_argument("subscriber cannot be NULL");
+  if (member == 0) throw std::invalid_argument("slot cannot be NULL");
+  if (type != Qt::AutoConnection && type != Qt::DirectConnection &&
+      type != Qt::QueuedConnection && type != Qt::BlockingQueuedConnection)
+  {
+    throw std::invalid_argument("connection type invalid");
+  }
+
   ctkEASlotHandler* handler = new ctkEASlotHandler();
-  connect(handler, SIGNAL(eventOccured(ctkEvent)), subscriber, member, Qt::DirectConnection);
+  connect(handler, SIGNAL(eventOccured(ctkEvent)), subscriber, member, type);
   ctkServiceRegistration reg = context->registerService<ctkEventHandler>(handler, properties);
   handler->reg = reg;
   qlonglong id = reg.getReference().getProperty(ctkPluginConstants::SERVICE_ID).toLongLong();

+ 2 - 1
Plugins/org.commontk.eventadmin/ctkEventAdminService_p.h

@@ -95,7 +95,8 @@ public:
   void unpublishSignal(const QObject* publisher, const char* signal = 0,
                        const QString& topic = "");
 
-  qlonglong subscribeSlot(const QObject* subscriber, const char* member, const ctkDictionary& properties);
+  qlonglong subscribeSlot(const QObject* subscriber, const char* member,
+                          const ctkDictionary& properties, Qt::ConnectionType type = Qt::AutoConnection);
 
   void unsubscribeSlot(qlonglong subscriptionId);
 

+ 4 - 1
Plugins/org.commontk.eventbus/ctkEventBusImpl.cpp

@@ -61,8 +61,11 @@ void ctkEventBusImpl::unpublishSignal(const QObject *publisher, const char *sign
   //TODO implement
 }
 
-qlonglong ctkEventBusImpl::subscribeSlot(const QObject* subscriber, const char* member, const ctkDictionary& properties)
+qlonglong ctkEventBusImpl::subscribeSlot(const QObject* subscriber, const char* member,
+                                         const ctkDictionary& properties, Qt::ConnectionType type)
 {
+  Q_UNUSED(type)
+
     ctkDictionary toSend(properties);
     QString topic = properties.value(TOPIC).toString();
     toSend.insert(TOPIC, topic);

+ 1 - 1
Plugins/org.commontk.eventbus/ctkEventBusImpl_p.h

@@ -52,7 +52,7 @@ public:
 
   //void publishSignal(const QObject* publisher, const char* signal, Qt::ConnectionType type = Qt::QueuedConnection);
 
-  qlonglong subscribeSlot(const QObject* subscriber, const char* member, const ctkDictionary& properties);
+  qlonglong subscribeSlot(const QObject* subscriber, const char* member, const ctkDictionary& properties, Qt::ConnectionType type = Qt::AutoConnection);
   virtual void unsubscribeSlot(qlonglong subscriptionId);
 
   virtual bool updateProperties(qlonglong subsriptionId, const ctkDictionary& properties);