// // AbstractEvent.h // // $Id: //poco/svn/Foundation/include/Poco/AbstractEvent.h#2 $ // // Library: Foundation // Package: Events // Module: AbstractEvent // // Definition of the AbstractEvent class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_AbstractFoundation_INCLUDED #define Foundation_AbstractFoundation_INCLUDED #include "Poco/Foundation.h" #include "Poco/SingletonHolder.h" #include "Poco/SharedPtr.h" #include "Poco/ActiveResult.h" #include "Poco/ActiveMethod.h" #include "Poco/Mutex.h" namespace Poco { template class AbstractEvent /// An AbstractEvent is the super-class of all events. /// It works similar to the way C# handles notifications (aka events in C#). /// Events can be used to send information to a set of observers /// which are registered at the event. The type of the data is specified with /// the template parameter TArgs. The TStrategy parameter must be a subclass /// of NotificationStrategy. The parameter TDelegate can either be a subclass of AbstractDelegate /// or of PriorityAbstractDelegate. /// /// Note that AbstractEvent should never be used directly. One ought to use /// one of its subclasses which set the TStrategy and TDelegate template parameters /// to fixed values. For most use-cases the BasicEvent template will be sufficient: /// #include "Poco/BasicEvent.h" /// #include "Poco/Delegate.h" /// /// If one requires delegates to be called in the order they registered, use FIFOEvent: /// #include "Poco/FIFOEvent.h" /// #include "Poco/Delegate.h" /// /// Both FIFOEvent and BasicEvent work with a standard delegate. They allow one object to register /// exactly one delegate at an event. In contrast, a PriorityDelegate comes with an attached priority value /// and allows one object to register for one priority value one delegate. Note that PriorityDelegates /// only work with PriorityEvents: /// #include "Poco/PriorityEvent.h" /// #include "Poco/PriorityDelegate.h" /// /// Use events by adding them as public members to the object which is throwing notifications: /// /// class MyData /// { /// public: /// Poco::BasicEvent AgeChanged; /// /// MyData(); /// ... /// }; /// /// Throwing the event can be done either by the events notify() or notifyAsync() method: /// /// /// Alternatively, instead of notify(), operator () can be used. /// /// void MyData::setAge(int i) /// { /// this->_age = i; /// AgeChanged(this, this->_age); /// } /// /// Note that notify and notifyAsync do not catch exceptions, i.e. in case a delegate /// throws an exception, the notify is immediately aborted and the exception is thrown /// back to the caller. /// /// Delegates can register methods at the event. In the case of a BasicEvent or FIFOEvent /// the Delegate template is used, in case of an PriorityEvent a PriorityDelegate is used. /// Mixing of observers, e.g. using a PriorityDelegate with a BasicEvent is not possible and /// checked for during compile time. /// Events require the observers to follow one of the following method signature: /// /// void onEvent(const void* pSender, TArgs& args); /// void onEvent(TArgs& args); /// static void onEvent(const void* pSender, TArgs& args); /// static void onEvent(void* pSender, TArgs& args); /// static void onEvent(TArgs& args); /// /// For performance reasons arguments are always sent by reference. This also allows observers /// to modify the sent argument. To prevent that, use as template /// parameter. A non-conformant method signature leads to compile errors. /// /// Assuming that the observer meets the method signature requirement, it can register /// this method with the += operator: /// /// class MyController /// { /// protected: /// MyData _data; /// /// void onDataChanged(void* pSender, int& data); /// ... /// }; /// /// MyController::MyController() /// { /// _data.AgeChanged += delegate(this, &MyController::onDataChanged); /// } /// /// In some cases it might be desirable to work with automatically expiring registrations. Simply add /// to delegate as 3rd parameter a expireValue (in milliseconds): /// /// _data.DataChanged += delegate(this, &MyController::onDataChanged, 1000); /// /// This will add a delegate to the event which will automatically be removed in 1000 millisecs. /// /// Unregistering happens via the -= operator. Forgetting to unregister a method will lead to /// segmentation faults later, when one tries to send a notify to a no longer existing object. /// /// MyController::~MyController() /// { /// _data.DataChanged -= delegate(this, &MyController::onDataChanged); /// } /// /// Working with PriorityDelegates as similar to working with BasicEvent/FIFOEvent.Instead of ''delegate'' /// simply use ''priorityDelegate''. /// /// For further examples refer to the event testsuites. { public: AbstractEvent(): _executeAsync(this, &AbstractEvent::executeAsyncImpl), _enabled(true) { } AbstractEvent(const TStrategy& strat): _executeAsync(this, &AbstractEvent::executeAsyncImpl), _strategy(strat), _enabled(true) { } virtual ~AbstractEvent() { } void operator += (const TDelegate& aDelegate) /// Adds a delegate to the event. If the observer is equal to an /// already existing one (determined by the < operator), /// it will simply replace the existing observer. /// This behavior is determined by the TStrategy. Current implementations /// (DefaultStrategy, FIFOStrategy) follow that guideline but future ones /// can deviate. { FastMutex::ScopedLock lock(_mutex); _strategy.add(aDelegate); } void operator -= (const TDelegate& aDelegate) /// Removes a delegate from the event. If the delegate is equal to an /// already existing one is determined by the < operator. /// If the observer is not found, the unregister will be ignored { FastMutex::ScopedLock lock(_mutex); _strategy.remove(aDelegate); } void operator () (const void* pSender, TArgs& args) { notify(pSender, args); } void notify(const void* pSender, TArgs& args) /// Sends a notification to all registered delegates. The order is /// determined by the TStrategy. This method is blocking. While executing, /// other objects can change the list of delegates. These changes don't /// influence the current active notifications but are activated with /// the next notify. If one of the delegates throws an exception, the notify /// method is immediately aborted and the exception is reported to the caller. { SharedPtr ptrStrat; bool enabled = false; { FastMutex::ScopedLock lock(_mutex); enabled = _enabled; if (_enabled) { // thread-safeness: // copy should be faster and safer than blocking until // execution ends ptrStrat = new TStrategy(_strategy); } } if (enabled) { ptrStrat->notify(pSender, args); } } ActiveResult notifyAsync(const void* pSender, const TArgs& args) /// Sends a notification to all registered delegates. The order is /// determined by the TStrategy. This method is not blocking and will /// immediately return. The delegates are invoked in a seperate thread. /// Call activeResult.wait() to wait until the notification has ended. /// While executing, other objects can change the delegate list. These changes don't /// influence the current active notifications but are activated with /// the next notify. If one of the delegates throws an exception, the execution /// is aborted and the exception is reported to the caller. { NotifyAsyncParams params(pSender, args); { FastMutex::ScopedLock lock(_mutex); // thread-safeness: // copy should be faster and safer than blocking until // execution ends // make a copy of the strategy here to guarantee that // between notifyAsync and the execution of the method no changes can occur params.ptrStrat = SharedPtr(new TStrategy(_strategy)); params.enabled = _enabled; } ActiveResult result = _executeAsync(params); return result; } void enable() /// Enables the event { FastMutex::ScopedLock lock(_mutex); _enabled = true; } void disable() /// Disables the event. notify and notifyAsnyc will be ignored, /// but adding/removing delegates is still allowed. { FastMutex::ScopedLock lock(_mutex); _enabled = false; } bool isEnabled() const { FastMutex::ScopedLock lock(_mutex); return _enabled; } void clear() /// Removes all delegates. { FastMutex::ScopedLock lock(_mutex); _strategy.clear(); } protected: struct NotifyAsyncParams { SharedPtr ptrStrat; const void* pSender; TArgs args; bool enabled; NotifyAsyncParams(const void* pSend, const TArgs& a):ptrStrat(), pSender(pSend), args(a), enabled(true) /// default constructor reduces the need for TArgs to have an empty constructor, only copy constructor is needed. { } }; ActiveMethod _executeAsync; TArgs executeAsyncImpl(const NotifyAsyncParams& par) { if (!par.enabled) { return par.args; } NotifyAsyncParams params = par; TArgs retArgs(params.args); params.ptrStrat->notify(params.pSender, retArgs); return retArgs; } TStrategy _strategy; /// The strategy used to notify observers. bool _enabled; /// Stores if an event is enabled. Notfies on disabled events have no effect /// but it is possible to change the observers. mutable FastMutex _mutex; private: AbstractEvent(const AbstractEvent& other); AbstractEvent& operator = (const AbstractEvent& other); }; } // namespace Poco #endif