AbstractEvent.h 16 KB

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