/src/quantlib/ql/patterns/observable.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl |
5 | | Copyright (C) 2003, 2004, 2005, 2006 StatPro Italia srl |
6 | | Copyright (C) 2011, 2012 Ferdinando Ametrano |
7 | | Copyright (C) 2013 Chris Higgs |
8 | | Copyright (C) 2015 Klaus Spanderen |
9 | | |
10 | | |
11 | | This file is part of QuantLib, a free-software/open-source library |
12 | | for financial quantitative analysts and developers - http://quantlib.org/ |
13 | | |
14 | | QuantLib is free software: you can redistribute it and/or modify it |
15 | | under the terms of the QuantLib license. You should have received a |
16 | | copy of the license along with this program; if not, please email |
17 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
18 | | <http://quantlib.org/license.shtml>. |
19 | | |
20 | | This program is distributed in the hope that it will be useful, but WITHOUT |
21 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
22 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
23 | | */ |
24 | | |
25 | | /*! \file observable.hpp |
26 | | \brief observer/observable pattern |
27 | | */ |
28 | | |
29 | | #ifndef quantlib_observable_hpp |
30 | | #define quantlib_observable_hpp |
31 | | |
32 | | #include <ql/errors.hpp> |
33 | | #include <ql/patterns/singleton.hpp> |
34 | | #include <ql/shared_ptr.hpp> |
35 | | #include <ql/types.hpp> |
36 | | #include <set> |
37 | | |
38 | | #if !defined(QL_USE_STD_SHARED_PTR) && BOOST_VERSION < 107400 |
39 | | |
40 | | namespace std { |
41 | | |
42 | | template<typename T> |
43 | | struct hash<boost::shared_ptr<T>> { |
44 | | std::size_t operator()(const boost::shared_ptr<T>& ptr) const noexcept { |
45 | | return std::hash<typename boost::shared_ptr<T>::element_type*>()(ptr.get()); |
46 | | } |
47 | | }; |
48 | | |
49 | | } |
50 | | |
51 | | #endif |
52 | | |
53 | | #ifndef QL_ENABLE_THREAD_SAFE_OBSERVER_PATTERN |
54 | | |
55 | | namespace QuantLib { |
56 | | |
57 | | class Observer; |
58 | | class ObservableSettings; |
59 | | |
60 | | //! Object that notifies its changes to a set of observers |
61 | | /*! \ingroup patterns */ |
62 | | class Observable { |
63 | | friend class Observer; |
64 | | friend class ObservableSettings; |
65 | | public: |
66 | | // constructors, assignment, destructor |
67 | 58.5M | Observable() = default; |
68 | | Observable(const Observable&); |
69 | | Observable& operator=(const Observable&); |
70 | | // delete the move operations because the semantics are not yet clear |
71 | | Observable(Observable&&) = delete; |
72 | | Observable& operator=(Observable&&) = delete; |
73 | 58.5M | virtual ~Observable() = default; |
74 | | /*! This method should be called at the end of non-const methods |
75 | | or when the programmer desires to notify any changes. |
76 | | */ |
77 | | void notifyObservers(); |
78 | | private: |
79 | | typedef std::set<Observer*> set_type; |
80 | | typedef set_type::iterator iterator; |
81 | | std::pair<iterator, bool> registerObserver(Observer*); |
82 | | Size unregisterObserver(Observer*); |
83 | | set_type observers_; |
84 | | }; |
85 | | |
86 | | //! global repository for run-time library settings |
87 | | class ObservableSettings : public Singleton<ObservableSettings> { |
88 | | friend class Singleton<ObservableSettings>; |
89 | | friend class Observable; |
90 | | public: |
91 | 0 | void disableUpdates(bool deferred=false) { |
92 | 0 | updatesEnabled_ = false; |
93 | 0 | updatesDeferred_ = deferred; |
94 | 0 | } |
95 | | void enableUpdates(); |
96 | | |
97 | 4.54M | bool updatesEnabled() const { return updatesEnabled_; } |
98 | 1.13M | bool updatesDeferred() const { return updatesDeferred_; } |
99 | | |
100 | | private: |
101 | 2 | ObservableSettings() = default; |
102 | | |
103 | | typedef std::set<Observer*> set_type; |
104 | | typedef set_type::iterator iterator; |
105 | | |
106 | | void registerDeferredObservers(const Observable::set_type& observers); |
107 | | void unregisterDeferredObserver(Observer*); |
108 | | |
109 | | set_type deferredObservers_; |
110 | | |
111 | | bool updatesEnabled_ = true, updatesDeferred_ = false; |
112 | | }; |
113 | | |
114 | | //! Object that gets notified when a given observable changes |
115 | | /*! \ingroup patterns */ |
116 | | class Observer { // NOLINT(cppcoreguidelines-special-member-functions) |
117 | | private: |
118 | | typedef std::set<ext::shared_ptr<Observable>> set_type; |
119 | | public: |
120 | | typedef set_type::iterator iterator; |
121 | | |
122 | | // constructors, assignment, destructor |
123 | 58.0M | Observer() = default; |
124 | | Observer(const Observer&); |
125 | | Observer& operator=(const Observer&); |
126 | | virtual ~Observer(); |
127 | | |
128 | | // observer interface |
129 | | std::pair<iterator, bool> |
130 | | registerWith(const ext::shared_ptr<Observable>&); |
131 | | |
132 | | /*! register with all observables of a given observer. Note |
133 | | that this does not include registering with the observer |
134 | | itself. |
135 | | */ |
136 | | void registerWithObservables(const ext::shared_ptr<Observer>&); |
137 | | |
138 | | Size unregisterWith(const ext::shared_ptr<Observable>&); |
139 | | void unregisterWithAll(); |
140 | | |
141 | | /*! This method must be implemented in derived classes. An |
142 | | instance of %Observer does not call this method directly: |
143 | | instead, it will be called by the observables the instance |
144 | | registered with when they need to notify any changes. |
145 | | */ |
146 | | virtual void update() = 0; |
147 | | |
148 | | /*! This method allows to explicitly update the instance itself |
149 | | and nested observers. If notifications are disabled a call to |
150 | | this method ensures an update of such nested observers. It |
151 | | should be implemented in derived classes whenever applicable */ |
152 | | virtual void deepUpdate(); |
153 | | |
154 | | private: |
155 | | set_type observables_; |
156 | | }; |
157 | | |
158 | | |
159 | | // inline definitions |
160 | | |
161 | 0 | inline void ObservableSettings::registerDeferredObservers(const Observable::set_type& observers) { |
162 | 0 | if (updatesDeferred()) { |
163 | 0 | deferredObservers_.insert(observers.begin(), observers.end()); |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | 0 | inline void ObservableSettings::unregisterDeferredObserver(Observer* o) { |
168 | 0 | deferredObservers_.erase(o); |
169 | 0 | } |
170 | | |
171 | 0 | inline Observable::Observable(const Observable&) { |
172 | | // the observer set is not copied; no observer asked to |
173 | | // register with this object |
174 | 0 | } |
175 | | |
176 | | /*! \warning notification is sent before the copy constructor has |
177 | | a chance of actually change the data |
178 | | members. Therefore, observers whose update() method |
179 | | tries to use their observables will not see the |
180 | | updated values. It is suggested that the update() |
181 | | method just raise a flag in order to trigger |
182 | | a later recalculation. |
183 | | */ |
184 | 0 | inline Observable& Observable::operator=(const Observable& o) { |
185 | 0 | // as above, the observer set is not copied. Moreover, |
186 | 0 | // observers of this object must be notified of the change |
187 | 0 | if (&o != this) |
188 | 0 | notifyObservers(); |
189 | 0 | return *this; |
190 | 0 | } |
191 | | |
192 | | inline std::pair<Observable::iterator, bool> |
193 | 1.21M | Observable::registerObserver(Observer* o) { |
194 | 1.21M | return observers_.insert(o); |
195 | 1.21M | } |
196 | | |
197 | 1.13M | inline Size Observable::unregisterObserver(Observer* o) { |
198 | 1.13M | if (ObservableSettings::instance().updatesDeferred()) |
199 | 0 | ObservableSettings::instance().unregisterDeferredObserver(o); |
200 | | |
201 | 1.13M | return observers_.erase(o); |
202 | 1.13M | } |
203 | | |
204 | | |
205 | | inline Observer::Observer(const Observer& o) |
206 | 0 | : observables_(o.observables_) { |
207 | 0 | for (const auto& observable : observables_) |
208 | 0 | observable->registerObserver(this); |
209 | 0 | } |
210 | | |
211 | 0 | inline Observer& Observer::operator=(const Observer& o) { |
212 | 0 | for (const auto& observable : observables_) |
213 | 0 | observable->unregisterObserver(this); |
214 | 0 | observables_ = o.observables_; |
215 | 0 | for (const auto& observable : observables_) |
216 | 0 | observable->registerObserver(this); |
217 | 0 | return *this; |
218 | 0 | } |
219 | | |
220 | 58.0M | inline Observer::~Observer() { |
221 | 58.0M | for (const auto& observable : observables_) |
222 | 1.13M | observable->unregisterObserver(this); |
223 | 58.0M | } |
224 | | |
225 | | inline std::pair<Observer::iterator, bool> |
226 | 1.21M | Observer::registerWith(const ext::shared_ptr<Observable>& h) { |
227 | 1.21M | if (h != nullptr) { |
228 | 1.21M | h->registerObserver(this); |
229 | 1.21M | return observables_.insert(h); |
230 | 1.21M | } |
231 | 0 | return std::make_pair(observables_.end(), false); |
232 | 1.21M | } |
233 | | |
234 | | inline void |
235 | 0 | Observer::registerWithObservables(const ext::shared_ptr<Observer> &o) { |
236 | 0 | if (o != nullptr) { |
237 | 0 | for (const auto& observable : o->observables_) |
238 | 0 | registerWith(observable); |
239 | 0 | } |
240 | 0 | } |
241 | | |
242 | | inline |
243 | 0 | Size Observer::unregisterWith(const ext::shared_ptr<Observable>& h) { |
244 | 0 | if (h != nullptr) |
245 | 0 | h->unregisterObserver(this); |
246 | 0 | return observables_.erase(h); |
247 | 0 | } |
248 | | |
249 | 0 | inline void Observer::unregisterWithAll() { |
250 | 0 | for (const auto& observable : observables_) |
251 | 0 | observable->unregisterObserver(this); |
252 | 0 | observables_.clear(); |
253 | 0 | } |
254 | | |
255 | 0 | inline void Observer::deepUpdate() { |
256 | 0 | update(); |
257 | 0 | } |
258 | | |
259 | | } |
260 | | |
261 | | #else |
262 | | |
263 | | #ifndef QL_USE_STD_SHARED_PTR |
264 | | #include <boost/smart_ptr/owner_less.hpp> |
265 | | #endif |
266 | | #include <atomic> |
267 | | #include <mutex> |
268 | | #include <set> |
269 | | #include <thread> |
270 | | |
271 | | namespace QuantLib { |
272 | | |
273 | | class Observable; |
274 | | class ObservableSettings; |
275 | | |
276 | | //! Object that gets notified when a given observable changes |
277 | | /*! \ingroup patterns */ |
278 | | class Observer : public ext::enable_shared_from_this<Observer> { |
279 | | friend class Observable; |
280 | | friend class ObservableSettings; |
281 | | private: |
282 | | typedef std::set<ext::shared_ptr<Observable>> set_type; |
283 | | public: |
284 | | typedef set_type::iterator iterator; |
285 | | |
286 | | // constructors, assignment, destructor |
287 | | Observer() {} |
288 | | Observer(const Observer&); |
289 | | Observer& operator=(const Observer&); |
290 | | virtual ~Observer(); |
291 | | // observer interface |
292 | | std::pair<iterator, bool> |
293 | | registerWith(const ext::shared_ptr<Observable>&); |
294 | | /*! register with all observables of a given observer. Note |
295 | | that this does not include registering with the observer |
296 | | itself. |
297 | | */ |
298 | | void registerWithObservables(const ext::shared_ptr<Observer>&); |
299 | | |
300 | | Size unregisterWith(const ext::shared_ptr<Observable>&); |
301 | | void unregisterWithAll(); |
302 | | |
303 | | /*! This method must be implemented in derived classes. An |
304 | | instance of %Observer does not call this method directly: |
305 | | instead, it will be called by the observables the instance |
306 | | registered with when they need to notify any changes. |
307 | | */ |
308 | | virtual void update() = 0; |
309 | | |
310 | | /*! This method allows to explicitly update the instance itself |
311 | | and nested observers. If notifications are disabled a call to |
312 | | this method ensures an update of such nested observers. It |
313 | | should be implemented in derived classes whenever applicable */ |
314 | | virtual void deepUpdate(); |
315 | | |
316 | | private: |
317 | | |
318 | | class Proxy { |
319 | | public: |
320 | | explicit Proxy(Observer* const observer) |
321 | | : active_ (true), |
322 | | observer_(observer) { |
323 | | } |
324 | | |
325 | | void update() const { |
326 | | std::lock_guard<std::recursive_mutex> lock(mutex_); |
327 | | if (active_) { |
328 | | // c++17 is required if used with std::shared_ptr<T> |
329 | | const ext::weak_ptr<Observer> o |
330 | | = observer_->weak_from_this(); |
331 | | |
332 | | //check for empty weak reference |
333 | | //https://stackoverflow.com/questions/45507041/how-to-check-if-weak-ptr-is-empty-non-assigned |
334 | | const ext::weak_ptr<Observer> empty; |
335 | | if (o.owner_before(empty) || empty.owner_before(o)) { |
336 | | const ext::shared_ptr<Observer> obs(o.lock()); |
337 | | if (obs) |
338 | | obs->update(); |
339 | | } |
340 | | else { |
341 | | observer_->update(); |
342 | | } |
343 | | } |
344 | | } |
345 | | |
346 | | void deactivate() { |
347 | | std::lock_guard<std::recursive_mutex> lock(mutex_); |
348 | | active_ = false; |
349 | | } |
350 | | |
351 | | private: |
352 | | bool active_; |
353 | | mutable std::recursive_mutex mutex_; |
354 | | Observer* const observer_; |
355 | | }; |
356 | | |
357 | | ext::shared_ptr<Proxy> proxy_; |
358 | | mutable std::recursive_mutex mutex_; |
359 | | |
360 | | set_type observables_; |
361 | | }; |
362 | | |
363 | | namespace detail { |
364 | | class Signal; |
365 | | } |
366 | | |
367 | | //! Object that notifies its changes to a set of observers |
368 | | /*! \ingroup patterns */ |
369 | | class Observable { |
370 | | friend class Observer; |
371 | | friend class ObservableSettings; |
372 | | private: |
373 | | typedef std::set<ext::shared_ptr<Observer::Proxy>> set_type; |
374 | | public: |
375 | | typedef set_type::iterator iterator; |
376 | | |
377 | | // constructors, assignment, destructor |
378 | | Observable(); |
379 | | Observable(const Observable&); |
380 | | Observable& operator=(const Observable&); |
381 | | virtual ~Observable() {} |
382 | | /*! This method should be called at the end of non-const methods |
383 | | or when the programmer desires to notify any changes. |
384 | | */ |
385 | | void notifyObservers(); |
386 | | private: |
387 | | void registerObserver(const ext::shared_ptr<Observer::Proxy>&); |
388 | | void unregisterObserver( |
389 | | const ext::shared_ptr<Observer::Proxy>& proxy, bool disconnect); |
390 | | |
391 | | ext::shared_ptr<detail::Signal> sig_; |
392 | | set_type observers_; |
393 | | mutable std::recursive_mutex mutex_; |
394 | | }; |
395 | | |
396 | | //! global repository for run-time library settings |
397 | | class ObservableSettings : public Singleton<ObservableSettings> { |
398 | | friend class Singleton<ObservableSettings>; |
399 | | friend class Observable; |
400 | | |
401 | | public: |
402 | | void disableUpdates(bool deferred=false) { |
403 | | std::lock_guard<std::mutex> lock(mutex_); |
404 | | updatesType_ = (deferred) ? UpdatesDeferred : UpdatesDisabled; |
405 | | } |
406 | | void enableUpdates(); |
407 | | |
408 | | bool updatesEnabled() {return (updatesType_ & UpdatesEnabled) != 0; } |
409 | | bool updatesDeferred() {return (updatesType_ & UpdatesDeferred) != 0; } |
410 | | private: |
411 | | ObservableSettings() : updatesType_(UpdatesEnabled) {} |
412 | | |
413 | | #if defined(QL_USE_STD_SHARED_PTR) |
414 | | typedef std::set<ext::weak_ptr<Observer::Proxy>, |
415 | | std::owner_less<ext::weak_ptr<Observer::Proxy> > > |
416 | | set_type; |
417 | | #else |
418 | | typedef std::set<ext::weak_ptr<Observer::Proxy>, |
419 | | boost::owner_less<ext::weak_ptr<Observer::Proxy> > > |
420 | | set_type; |
421 | | #endif |
422 | | |
423 | | void registerDeferredObservers(const Observable::set_type& observers); |
424 | | void unregisterDeferredObserver(const ext::shared_ptr<Observer::Proxy>& proxy); |
425 | | |
426 | | set_type deferredObservers_; |
427 | | mutable std::mutex mutex_; |
428 | | |
429 | | enum UpdateType { UpdatesDisabled = 0, UpdatesEnabled = 1, UpdatesDeferred = 2} ; |
430 | | std::atomic<int> updatesType_; |
431 | | }; |
432 | | |
433 | | |
434 | | // inline definitions |
435 | | |
436 | | inline void ObservableSettings::registerDeferredObservers(const Observable::set_type& observers) { |
437 | | deferredObservers_.insert(observers.begin(), observers.end()); |
438 | | } |
439 | | |
440 | | inline void ObservableSettings::unregisterDeferredObserver( |
441 | | const ext::shared_ptr<Observer::Proxy>& o) { |
442 | | deferredObservers_.erase(o); |
443 | | } |
444 | | |
445 | | inline void ObservableSettings::enableUpdates() { |
446 | | std::lock_guard<std::mutex> lock(mutex_); |
447 | | |
448 | | // if there are outstanding deferred updates, do the notification |
449 | | updatesType_ = UpdatesEnabled; |
450 | | |
451 | | if (deferredObservers_.size()) { |
452 | | bool successful = true; |
453 | | std::string errMsg; |
454 | | |
455 | | for (auto i=deferredObservers_.begin(); |
456 | | i!=deferredObservers_.end(); ++i) { |
457 | | try { |
458 | | const ext::shared_ptr<Observer::Proxy> proxy = i->lock(); |
459 | | if (proxy) |
460 | | proxy->update(); |
461 | | } catch (std::exception& e) { |
462 | | successful = false; |
463 | | errMsg = e.what(); |
464 | | } catch (...) { |
465 | | successful = false; |
466 | | } |
467 | | } |
468 | | |
469 | | deferredObservers_.clear(); |
470 | | |
471 | | QL_ENSURE(successful, |
472 | | "could not notify one or more observers: " << errMsg); |
473 | | } |
474 | | } |
475 | | |
476 | | |
477 | | /*! \warning notification is sent before the copy constructor has |
478 | | a chance of actually change the data |
479 | | members. Therefore, observers whose update() method |
480 | | tries to use their observables will not see the |
481 | | updated values. It is suggested that the update() |
482 | | method just raise a flag in order to trigger |
483 | | a later recalculation. |
484 | | */ |
485 | | inline Observable& Observable::operator=(const Observable& o) { |
486 | | // as above, the observer set is not copied. Moreover, |
487 | | // observers of this object must be notified of the change |
488 | | if (&o != this) |
489 | | notifyObservers(); |
490 | | return *this; |
491 | | } |
492 | | |
493 | | inline Observer::Observer(const Observer& o) { |
494 | | proxy_.reset(new Proxy(this)); |
495 | | |
496 | | { |
497 | | std::lock_guard<std::recursive_mutex> lock(o.mutex_); |
498 | | observables_ = o.observables_; |
499 | | } |
500 | | |
501 | | for (const auto& observable : observables_) |
502 | | observable->registerObserver(proxy_); |
503 | | } |
504 | | |
505 | | inline Observer& Observer::operator=(const Observer& o) { |
506 | | std::lock_guard<std::recursive_mutex> lock(mutex_); |
507 | | if (!proxy_) { |
508 | | proxy_.reset(new Proxy(this)); |
509 | | } |
510 | | |
511 | | for (const auto& observable : observables_) |
512 | | observable->unregisterObserver(proxy_, true); |
513 | | |
514 | | { |
515 | | std::lock_guard<std::recursive_mutex> lock(o.mutex_); |
516 | | observables_ = o.observables_; |
517 | | } |
518 | | for (const auto& observable : observables_) |
519 | | observable->registerObserver(proxy_); |
520 | | |
521 | | return *this; |
522 | | } |
523 | | |
524 | | inline Observer::~Observer() { |
525 | | std::lock_guard<std::recursive_mutex> lock(mutex_); |
526 | | if (proxy_) |
527 | | proxy_->deactivate(); |
528 | | |
529 | | for (const auto& observable : observables_) |
530 | | observable->unregisterObserver(proxy_, false); |
531 | | } |
532 | | |
533 | | inline std::pair<Observer::iterator, bool> |
534 | | Observer::registerWith(const ext::shared_ptr<Observable>& h) { |
535 | | std::lock_guard<std::recursive_mutex> lock(mutex_); |
536 | | if (!proxy_) { |
537 | | proxy_.reset(new Proxy(this)); |
538 | | } |
539 | | |
540 | | if (h) { |
541 | | h->registerObserver(proxy_); |
542 | | return observables_.insert(h); |
543 | | } |
544 | | return std::make_pair(observables_.end(), false); |
545 | | } |
546 | | |
547 | | inline void |
548 | | Observer::registerWithObservables(const ext::shared_ptr<Observer>& o) { |
549 | | if (o) { |
550 | | std::lock_guard<std::recursive_mutex> lock(o->mutex_); |
551 | | |
552 | | for (const auto& observable : o->observables_) |
553 | | registerWith(observable); |
554 | | } |
555 | | } |
556 | | |
557 | | inline |
558 | | Size Observer::unregisterWith(const ext::shared_ptr<Observable>& h) { |
559 | | std::lock_guard<std::recursive_mutex> lock(mutex_); |
560 | | |
561 | | if (h && proxy_) { |
562 | | h->unregisterObserver(proxy_, true); |
563 | | } |
564 | | |
565 | | return observables_.erase(h); |
566 | | } |
567 | | |
568 | | inline void Observer::unregisterWithAll() { |
569 | | std::lock_guard<std::recursive_mutex> lock(mutex_); |
570 | | |
571 | | for (const auto& observable : observables_) |
572 | | observable->unregisterObserver(proxy_, true); |
573 | | |
574 | | observables_.clear(); |
575 | | } |
576 | | |
577 | | inline void Observer::deepUpdate() { |
578 | | update(); |
579 | | } |
580 | | } |
581 | | #endif |
582 | | #endif |