AbstractEvent.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. //
  2. // AbstractEvent.h
  3. //
  4. // $Id: //poco/1.4/Foundation/include/Poco/AbstractEvent.h#3 $
  5. //
  6. // Library: Foundation
  7. // Package: Events
  8. // Module: AbstractEvent
  9. //
  10. // Definition of the AbstractEvent class.
  11. //
  12. // Copyright (c) 2006-2011, Applied Informatics Software Engineering GmbH.
  13. // and Contributors.
  14. //
  15. // SPDX-License-Identifier: BSL-1.0
  16. //
  17. #ifndef Foundation_AbstractFoundation_INCLUDED
  18. #define Foundation_AbstractFoundation_INCLUDED
  19. #include "Poco/Foundation.h"
  20. #include "Poco/SingletonHolder.h"
  21. #include "Poco/SharedPtr.h"
  22. #include "Poco/ActiveResult.h"
  23. #include "Poco/ActiveMethod.h"
  24. #include "Poco/Mutex.h"
  25. namespace Poco {
  26. template <class TArgs, class TStrategy, class TDelegate, class TMutex = FastMutex>
  27. class AbstractEvent
  28. /// An AbstractEvent is the base class of all events.
  29. /// It works similar to the way C# handles notifications (aka events in C#).
  30. ///
  31. /// Events can be used to send information to a set of delegates
  32. /// which are registered with the event. The type of the data is specified with
  33. /// the template parameter TArgs. The TStrategy parameter must be a subclass
  34. /// of NotificationStrategy. The parameter TDelegate can either be a subclass of AbstractDelegate
  35. /// or of AbstractPriorityDelegate.
  36. ///
  37. /// Note that AbstractEvent should never be used directly. One ought to use
  38. /// one of its subclasses which set the TStrategy and TDelegate template parameters
  39. /// to fixed values. For most use-cases the BasicEvent template will be sufficient:
  40. ///
  41. /// #include "Poco/BasicEvent.h"
  42. /// #include "Poco/Delegate.h"
  43. ///
  44. /// Note that as of release 1.4.2, the behavior of BasicEvent equals that of FIFOEvent,
  45. /// so the FIFOEvent class is no longer necessary and provided for backwards compatibility
  46. /// only.
  47. ///
  48. /// BasicEvent works with a standard delegate. They allow one object to register
  49. /// one or more delegates with an event. In contrast, a PriorityDelegate comes with an attached priority value
  50. /// and allows one object to register for one priority value one or more delegates. Note that PriorityDelegates
  51. /// only work with PriorityEvents:
  52. ///
  53. /// #include "Poco/PriorityEvent.h"
  54. /// #include "Poco/PriorityDelegate.h"
  55. ///
  56. /// Use events by adding them as public members to the object which is throwing notifications:
  57. ///
  58. /// class MyData
  59. /// {
  60. /// public:
  61. /// Poco::BasicEvent<int> dataChanged;
  62. ///
  63. /// MyData();
  64. /// ...
  65. /// void setData(int i);
  66. /// ...
  67. /// private:
  68. /// int _data;
  69. /// };
  70. ///
  71. /// Firing the event is done either by calling the event's notify() or notifyAsync() method:
  72. ///
  73. /// void MyData::setData(int i)
  74. /// {
  75. /// this->_data = i;
  76. /// dataChanged.notify(this, this->_data);
  77. /// }
  78. ///
  79. /// Alternatively, instead of notify(), operator () can be used.
  80. ///
  81. /// void MyData::setData(int i)
  82. /// {
  83. /// this->_data = i;
  84. /// dataChanged(this, this->_data);
  85. /// }
  86. ///
  87. /// Note that operator (), notify() and notifyAsync() do not catch exceptions, i.e. in case a
  88. /// delegate throws an exception, notifying is immediately aborted and the exception is propagated
  89. /// back to the caller.
  90. ///
  91. /// Delegates can register methods at the event. In the case of a BasicEvent
  92. /// the Delegate template is used, in case of an PriorityEvent a PriorityDelegate is used.
  93. /// Mixing of delegates, e.g. using a PriorityDelegate with a BasicEvent is not allowed and
  94. /// can lead to compile-time and/or run-time errors. The standalone delegate() functions
  95. /// can be used to construct Delegate objects.
  96. ///
  97. /// Events require the observers to have one of the following method signatures:
  98. ///
  99. /// void onEvent(const void* pSender, TArgs& args);
  100. /// void onEvent(TArgs& args);
  101. /// static void onEvent(const void* pSender, TArgs& args);
  102. /// static void onEvent(void* pSender, TArgs& args);
  103. /// static void onEvent(TArgs& args);
  104. ///
  105. /// For performance reasons arguments are always sent by reference. This also allows observers
  106. /// to modify the event argument. To prevent that, use <[const TArg]> as template
  107. /// parameter. A non-conformant method signature leads to compile errors.
  108. ///
  109. /// Assuming that the observer meets the method signature requirement, it can register
  110. /// this method with the += operator:
  111. ///
  112. /// class MyController
  113. /// {
  114. /// protected:
  115. /// MyData _data;
  116. ///
  117. /// void onDataChanged(void* pSender, int& data);
  118. /// ...
  119. /// };
  120. ///
  121. /// MyController::MyController()
  122. /// {
  123. /// _data.dataChanged += delegate(this, &MyController::onDataChanged);
  124. /// }
  125. ///
  126. /// In some cases it might be desirable to work with automatically expiring registrations. Simply add
  127. /// to delegate as 3rd parameter a expireValue (in milliseconds):
  128. ///
  129. /// _data.dataChanged += delegate(this, &MyController::onDataChanged, 1000);
  130. ///
  131. /// This will add a delegate to the event which will automatically be removed in 1000 millisecs.
  132. ///
  133. /// Unregistering happens via the -= operator. Forgetting to unregister a method will lead to
  134. /// segmentation faults later, when one tries to send a notify to a no longer existing object.
  135. ///
  136. /// MyController::~MyController()
  137. /// {
  138. /// _data.dataChanged -= delegate(this, &MyController::onDataChanged);
  139. /// }
  140. ///
  141. /// Working with PriorityDelegate's as similar to working with BasicEvent.
  142. /// Instead of delegate(), the priorityDelegate() function must be used
  143. /// to create the PriorityDelegate.
  144. {
  145. public:
  146. typedef TDelegate* DelegateHandle;
  147. typedef TArgs Args;
  148. AbstractEvent():
  149. _executeAsync(this, &AbstractEvent::executeAsyncImpl),
  150. _enabled(true)
  151. {
  152. }
  153. AbstractEvent(const TStrategy& strat):
  154. _executeAsync(this, &AbstractEvent::executeAsyncImpl),
  155. _strategy(strat),
  156. _enabled(true)
  157. {
  158. }
  159. virtual ~AbstractEvent()
  160. {
  161. }
  162. void operator += (const TDelegate& aDelegate)
  163. /// Adds a delegate to the event.
  164. ///
  165. /// Exact behavior is determined by the TStrategy.
  166. {
  167. typename TMutex::ScopedLock lock(_mutex);
  168. _strategy.add(aDelegate);
  169. }
  170. void operator -= (const TDelegate& aDelegate)
  171. /// Removes a delegate from the event.
  172. ///
  173. /// If the delegate is not found, this function does nothing.
  174. {
  175. typename TMutex::ScopedLock lock(_mutex);
  176. _strategy.remove(aDelegate);
  177. }
  178. DelegateHandle add(const TDelegate& aDelegate)
  179. /// Adds a delegate to the event.
  180. ///
  181. /// Exact behavior is determined by the TStrategy.
  182. ///
  183. /// Returns a DelegateHandle which can be used in call to
  184. /// remove() to remove the delegate.
  185. {
  186. typename TMutex::ScopedLock lock(_mutex);
  187. return _strategy.add(aDelegate);
  188. }
  189. void remove(DelegateHandle delegateHandle)
  190. /// Removes a delegate from the event using a DelegateHandle
  191. /// returned by add().
  192. ///
  193. /// If the delegate is not found, this function does nothing.
  194. {
  195. typename TMutex::ScopedLock lock(_mutex);
  196. _strategy.remove(delegateHandle);
  197. }
  198. void operator () (const void* pSender, TArgs& args)
  199. /// Shortcut for notify(pSender, args);
  200. {
  201. notify(pSender, args);
  202. }
  203. void operator () (TArgs& args)
  204. /// Shortcut for notify(args).
  205. {
  206. notify(0, args);
  207. }
  208. void notify(const void* pSender, TArgs& args)
  209. /// Sends a notification to all registered delegates. The order is
  210. /// determined by the TStrategy. This method is blocking. While executing,
  211. /// the list of delegates may be modified. These changes don't
  212. /// influence the current active notifications but are activated with
  213. /// the next notify. If a delegate is removed during a notify(), the
  214. /// delegate will no longer be invoked (unless it has already been
  215. /// invoked prior to removal). If one of the delegates throws an exception,
  216. /// the notify method is immediately aborted and the exception is propagated
  217. /// to the caller.
  218. {
  219. Poco::ScopedLockWithUnlock<TMutex> lock(_mutex);
  220. if (!_enabled) return;
  221. // thread-safeness:
  222. // copy should be faster and safer than blocking until
  223. // execution ends
  224. TStrategy strategy(_strategy);
  225. lock.unlock();
  226. strategy.notify(pSender, args);
  227. }
  228. bool hasDelegates() const {
  229. return !(_strategy.empty());
  230. }
  231. ActiveResult<TArgs> notifyAsync(const void* pSender, const TArgs& args)
  232. /// Sends a notification to all registered delegates. The order is
  233. /// determined by the TStrategy. This method is not blocking and will
  234. /// immediately return. The delegates are invoked in a seperate thread.
  235. /// Call activeResult.wait() to wait until the notification has ended.
  236. /// While executing, other objects can change the delegate list. These changes don't
  237. /// influence the current active notifications but are activated with
  238. /// the next notify. If a delegate is removed during a notify(), the
  239. /// delegate will no longer be invoked (unless it has already been
  240. /// invoked prior to removal). If one of the delegates throws an exception,
  241. /// the execution is aborted and the exception is propagated to the caller.
  242. {
  243. NotifyAsyncParams params(pSender, args);
  244. {
  245. typename TMutex::ScopedLock lock(_mutex);
  246. // thread-safeness:
  247. // copy should be faster and safer than blocking until
  248. // execution ends
  249. // make a copy of the strategy here to guarantee that
  250. // between notifyAsync and the execution of the method no changes can occur
  251. params.ptrStrat = SharedPtr<TStrategy>(new TStrategy(_strategy));
  252. params.enabled = _enabled;
  253. }
  254. ActiveResult<TArgs> result = _executeAsync(params);
  255. return result;
  256. }
  257. void enable()
  258. /// Enables the event.
  259. {
  260. typename TMutex::ScopedLock lock(_mutex);
  261. _enabled = true;
  262. }
  263. void disable()
  264. /// Disables the event. notify and notifyAsnyc will be ignored,
  265. /// but adding/removing delegates is still allowed.
  266. {
  267. typename TMutex::ScopedLock lock(_mutex);
  268. _enabled = false;
  269. }
  270. bool isEnabled() const
  271. {
  272. typename TMutex::ScopedLock lock(_mutex);
  273. return _enabled;
  274. }
  275. void clear()
  276. /// Removes all delegates.
  277. {
  278. typename TMutex::ScopedLock lock(_mutex);
  279. _strategy.clear();
  280. }
  281. bool empty() const
  282. /// Checks if any delegates are registered at the delegate.
  283. {
  284. typename TMutex::ScopedLock lock(_mutex);
  285. return _strategy.empty();
  286. }
  287. protected:
  288. struct NotifyAsyncParams
  289. {
  290. SharedPtr<TStrategy> ptrStrat;
  291. const void* pSender;
  292. TArgs args;
  293. bool enabled;
  294. NotifyAsyncParams(const void* pSend, const TArgs& a):ptrStrat(), pSender(pSend), args(a), enabled(true)
  295. /// Default constructor reduces the need for TArgs to have an empty constructor, only copy constructor is needed.
  296. {
  297. }
  298. };
  299. ActiveMethod<TArgs, NotifyAsyncParams, AbstractEvent> _executeAsync;
  300. TArgs executeAsyncImpl(const NotifyAsyncParams& par)
  301. {
  302. if (!par.enabled)
  303. {
  304. return par.args;
  305. }
  306. NotifyAsyncParams params = par;
  307. TArgs retArgs(params.args);
  308. params.ptrStrat->notify(params.pSender, retArgs);
  309. return retArgs;
  310. }
  311. TStrategy _strategy; /// The strategy used to notify observers.
  312. bool _enabled; /// Stores if an event is enabled. Notfies on disabled events have no effect
  313. /// but it is possible to change the observers.
  314. mutable TMutex _mutex;
  315. private:
  316. AbstractEvent(const AbstractEvent& other);
  317. AbstractEvent& operator = (const AbstractEvent& other);
  318. };
  319. template <class TStrategy, class TDelegate, class TMutex>
  320. class AbstractEvent<void, TStrategy, TDelegate, TMutex>
  321. {
  322. public:
  323. typedef TDelegate* DelegateHandle;
  324. AbstractEvent():
  325. _executeAsync(this, &AbstractEvent::executeAsyncImpl),
  326. _enabled(true)
  327. {
  328. }
  329. AbstractEvent(const TStrategy& strat):
  330. _executeAsync(this, &AbstractEvent::executeAsyncImpl),
  331. _strategy(strat),
  332. _enabled(true)
  333. {
  334. }
  335. virtual ~AbstractEvent()
  336. {
  337. }
  338. void operator += (const TDelegate& aDelegate)
  339. /// Adds a delegate to the event.
  340. ///
  341. /// Exact behavior is determined by the TStrategy.
  342. {
  343. typename TMutex::ScopedLock lock(_mutex);
  344. _strategy.add(aDelegate);
  345. }
  346. void operator -= (const TDelegate& aDelegate)
  347. /// Removes a delegate from the event.
  348. ///
  349. /// If the delegate is not found, this function does nothing.
  350. {
  351. typename TMutex::ScopedLock lock(_mutex);
  352. _strategy.remove(aDelegate);
  353. }
  354. DelegateHandle add(const TDelegate& aDelegate)
  355. /// Adds a delegate to the event.
  356. ///
  357. /// Exact behavior is determined by the TStrategy.
  358. ///
  359. /// Returns a DelegateHandle which can be used in call to
  360. /// remove() to remove the delegate.
  361. {
  362. typename TMutex::ScopedLock lock(_mutex);
  363. return _strategy.add(aDelegate);
  364. }
  365. void remove(DelegateHandle delegateHandle)
  366. /// Removes a delegate from the event using a DelegateHandle
  367. /// returned by add().
  368. ///
  369. /// If the delegate is not found, this function does nothing.
  370. {
  371. typename TMutex::ScopedLock lock(_mutex);
  372. _strategy.remove(delegateHandle);
  373. }
  374. void operator () (const void* pSender)
  375. /// Shortcut for notify(pSender, args);
  376. {
  377. notify(pSender);
  378. }
  379. void operator () ()
  380. /// Shortcut for notify(args).
  381. {
  382. notify(0);
  383. }
  384. void notify(const void* pSender)
  385. /// Sends a notification to all registered delegates. The order is
  386. /// determined by the TStrategy. This method is blocking. While executing,
  387. /// the list of delegates may be modified. These changes don't
  388. /// influence the current active notifications but are activated with
  389. /// the next notify. If a delegate is removed during a notify(), the
  390. /// delegate will no longer be invoked (unless it has already been
  391. /// invoked prior to removal). If one of the delegates throws an exception,
  392. /// the notify method is immediately aborted and the exception is propagated
  393. /// to the caller.
  394. {
  395. Poco::ScopedLockWithUnlock<TMutex> lock(_mutex);
  396. if (!_enabled) return;
  397. // thread-safeness:
  398. // copy should be faster and safer than blocking until
  399. // execution ends
  400. TStrategy strategy(_strategy);
  401. lock.unlock();
  402. strategy.notify(pSender);
  403. }
  404. ActiveResult<void> notifyAsync(const void* pSender)
  405. /// Sends a notification to all registered delegates. The order is
  406. /// determined by the TStrategy. This method is not blocking and will
  407. /// immediately return. The delegates are invoked in a seperate thread.
  408. /// Call activeResult.wait() to wait until the notification has ended.
  409. /// While executing, other objects can change the delegate list. These changes don't
  410. /// influence the current active notifications but are activated with
  411. /// the next notify. If a delegate is removed during a notify(), the
  412. /// delegate will no longer be invoked (unless it has already been
  413. /// invoked prior to removal). If one of the delegates throws an exception,
  414. /// the execution is aborted and the exception is propagated to the caller.
  415. {
  416. NotifyAsyncParams params(pSender);
  417. {
  418. typename TMutex::ScopedLock lock(_mutex);
  419. // thread-safeness:
  420. // copy should be faster and safer than blocking until
  421. // execution ends
  422. // make a copy of the strategy here to guarantee that
  423. // between notifyAsync and the execution of the method no changes can occur
  424. params.ptrStrat = SharedPtr<TStrategy>(new TStrategy(_strategy));
  425. params.enabled = _enabled;
  426. }
  427. ActiveResult<void> result = _executeAsync(params);
  428. return result;
  429. }
  430. void enable()
  431. /// Enables the event.
  432. {
  433. typename TMutex::ScopedLock lock(_mutex);
  434. _enabled = true;
  435. }
  436. void disable()
  437. /// Disables the event. notify and notifyAsnyc will be ignored,
  438. /// but adding/removing delegates is still allowed.
  439. {
  440. typename TMutex::ScopedLock lock(_mutex);
  441. _enabled = false;
  442. }
  443. bool isEnabled() const
  444. {
  445. typename TMutex::ScopedLock lock(_mutex);
  446. return _enabled;
  447. }
  448. void clear()
  449. /// Removes all delegates.
  450. {
  451. typename TMutex::ScopedLock lock(_mutex);
  452. _strategy.clear();
  453. }
  454. bool empty() const
  455. /// Checks if any delegates are registered at the delegate.
  456. {
  457. typename TMutex::ScopedLock lock(_mutex);
  458. return _strategy.empty();
  459. }
  460. protected:
  461. struct NotifyAsyncParams
  462. {
  463. SharedPtr<TStrategy> ptrStrat;
  464. const void* pSender;
  465. bool enabled;
  466. NotifyAsyncParams(const void* pSend):ptrStrat(), pSender(pSend), enabled(true)
  467. /// Default constructor reduces the need for TArgs to have an empty constructor, only copy constructor is needed.
  468. {
  469. }
  470. };
  471. ActiveMethod<void, NotifyAsyncParams, AbstractEvent> _executeAsync;
  472. void executeAsyncImpl(const NotifyAsyncParams& par)
  473. {
  474. if (!par.enabled)
  475. {
  476. return;
  477. }
  478. NotifyAsyncParams params = par;
  479. params.ptrStrat->notify(params.pSender);
  480. return;
  481. }
  482. TStrategy _strategy; /// The strategy used to notify observers.
  483. bool _enabled; /// Stores if an event is enabled. Notfies on disabled events have no effect
  484. /// but it is possible to change the observers.
  485. mutable TMutex _mutex;
  486. private:
  487. AbstractEvent(const AbstractEvent& other);
  488. AbstractEvent& operator = (const AbstractEvent& other);
  489. };
  490. } // namespace Poco
  491. #endif // Foundation_AbstractFoundation_INCLUDED