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