• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 20598199576

30 Dec 2025 01:59PM UTC coverage: 74.172% (-0.004%) from 74.176%
20598199576

push

github

web-flow
Some deferred observers get destroyed during updates II (#2412)

7 of 7 new or added lines in 2 files covered. (100.0%)

3 existing lines in 2 files now uncovered.

57519 of 77548 relevant lines covered (74.17%)

8736706.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.86
/ql/patterns/observable.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
Copyright (C) 2013 Chris Higgs
5
Copyright (C) 2015 Klaus Spanderen
6

7
This file is part of QuantLib, a free-software/open-source library
8
for financial quantitative analysts and developers - http://quantlib.org/
9

10
QuantLib is free software: you can redistribute it and/or modify it
11
under the terms of the QuantLib license.  You should have received a
12
copy of the license along with this program; if not, please email
13
<quantlib-dev@lists.sf.net>. The license is also available online at
14
<https://www.quantlib.org/license.shtml>.
15

16
This program is distributed in the hope that it will be useful, but WITHOUT
17
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
FOR A PARTICULAR PURPOSE.  See the license for more details.
19
*/
20

21

22
#include <ql/patterns/observable.hpp>
23

24
#ifndef QL_ENABLE_THREAD_SAFE_OBSERVER_PATTERN
25

26
namespace QuantLib {
27

28
    void ObservableSettings::enableUpdates() {
641,554✔
29
        updatesEnabled_  = true;
641,554✔
30
        updatesDeferred_ = false;
641,554✔
31

32
        // if there are outstanding deferred updates, do the notification
33
        if (!deferredObservers_.empty()) {
641,554✔
34
            bool successful = true;
35
            std::string errMsg;
36
            runningDeferredUpdates_ = true;
3✔
37

38
            for (const auto& [deferredObserver, isValid] : deferredObservers_) {
10✔
39
                if (!isValid)
7✔
UNCOV
40
                    continue;
×
41

42
                try {
43
                    deferredObserver->update();
7✔
44
                } catch (std::exception& e) {
×
45
                    successful = false;
46
                    errMsg = e.what();
×
47
                } catch (...) {
×
48
                    successful = false;
49
                }
×
50
            }
51

52
            deferredObservers_.clear();
53
            runningDeferredUpdates_ = false;
3✔
54

55
            QL_ENSURE(successful,
3✔
56
                  "could not notify one or more observers: " << errMsg);
57
        }
58

59
    }
641,554✔
60

61

62
    void Observable::notifyObservers() {
36,617,863✔
63
        if (!ObservableSettings::instance().updatesEnabled()) {
36,617,863✔
64
            // if updates are only deferred, flag this for later notification
65
            // these are held centrally by the settings singleton
66
            ObservableSettings::instance().registerDeferredObservers(observers_);
641,574✔
67
        } else if (!observers_.empty()) {
35,976,289✔
68
            bool successful = true;
69
            std::string errMsg;
70
            for (auto* observer : observers_) {
53,027,429✔
71
                try {
72
                    observer->update();
28,088,026✔
73
                } catch (std::exception& e) {
×
74
                    // quite a dilemma. If we don't catch the exception,
75
                    // other observers will not receive the notification
76
                    // and might be left in an incorrect state. If we do
77
                    // catch it and continue the loop (as we do here) we
78
                    // lose the exception. The least evil might be to try
79
                    // and notify all observers, while raising an
80
                    // exception if something bad happened.
81
                    successful = false;
82
                    errMsg = e.what();
×
83
                } catch (...) {
×
84
                    successful = false;
85
                }
×
86
            }
87
            QL_ENSURE(successful,
24,939,403✔
88
                  "could not notify one or more observers: " << errMsg);
89
        }
90
    }
36,617,863✔
91

92
}
93

94
#else
95

96
#include <boost/signals2/signal_type.hpp>
97

98
namespace QuantLib {
99

100
    namespace detail {
101

102
        class Signal {
103
          public:
104
            typedef boost::signals2::signal_type<
105
                void(),
106
                boost::signals2::keywords::mutex_type<std::recursive_mutex> >
107
                ::type signal_type;
108

109
            void connect(const signal_type::slot_type& slot) {
110
                sig_.connect(slot);
111
            }
112

113
            template <class T>
114
            void disconnect(const T& slot) {
115
                sig_.disconnect(slot);
116
            }
117

118
            void operator()() const {
119
                sig_();
120
            }
121
          private:
122
            signal_type sig_;
123
        };
124

125
        template <class T>
126
        class ProxyUpdater {
127
            T* proxy_;
128
          public:
129
            explicit ProxyUpdater(const ext::shared_ptr<T>& observerProxy)
130
            : proxy_(observerProxy.get()) {}
131

132
            void operator()() const {
133
                proxy_->update();
134
            }
135

136
            bool operator==(const ProxyUpdater<T>& other) const {
137
                return proxy_ == other.proxy_;
138
            }
139

140
            bool operator!=(const ProxyUpdater<T>& other) const {
141
                return proxy_ != other.proxy_;
142
            }
143
        };
144

145
    }
146

147
    void Observable::registerObserver(const ext::shared_ptr<Observer::Proxy>& observerProxy) {
148
        {
149
            std::lock_guard<std::recursive_mutex> lock(mutex_);
150
            observers_.insert(observerProxy);
151
        }
152

153
        detail::Signal::signal_type::slot_type slot {detail::ProxyUpdater<Observer::Proxy>(observerProxy)};
154
        #if defined(QL_USE_STD_SHARED_PTR)
155
        sig_->connect(slot.track_foreign(observerProxy));
156
        #else
157
        sig_->connect(slot.track(observerProxy));
158
        #endif
159
    }
160

161
    void Observable::unregisterObserver(const ext::shared_ptr<Observer::Proxy>& observerProxy,
162
                                        bool disconnect) {
163
        {
164
            std::lock_guard<std::recursive_mutex> lock(mutex_);
165
            observers_.erase(observerProxy);
166
        }
167

168
        if (ObservableSettings::instance().updatesDeferred()) {
169
            std::lock_guard<std::mutex> sLock(ObservableSettings::instance().mutex_);
170
            if (ObservableSettings::instance().updatesDeferred())
171
                ObservableSettings::instance().unregisterDeferredObserver(observerProxy);
172
        }
173

174
        if (disconnect) {
175
            sig_->disconnect(detail::ProxyUpdater<Observer::Proxy>(observerProxy));
176
        }
177
    }
178

179
    void Observable::notifyObservers() {
180
        if (ObservableSettings::instance().updatesEnabled()) {
181
            sig_->operator()();
182
        }
183
        else {
184
            bool updatesEnabled = false;
185
            {
186
                std::lock_guard<std::mutex> sLock(ObservableSettings::instance().mutex_);
187
                updatesEnabled = ObservableSettings::instance().updatesEnabled();
188

189
                if (ObservableSettings::instance().updatesDeferred()) {
190
                    std::lock_guard<std::recursive_mutex> lock(mutex_);
191
                    ObservableSettings::instance().registerDeferredObservers(observers_);
192
                }
193
            }
194

195
            if (updatesEnabled)
196
                sig_->operator()();
197
        }
198
    }
199

200
    Observable::Observable()
201
    : sig_(new detail::Signal()) { }
202

203
    Observable::Observable(const Observable&)
204
    : sig_(new detail::Signal()) {
205
        // the observer set is not copied; no observer asked to
206
        // register with this object
207
    }
208

209
}
210

211
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc