source: ThirdParty/CodePatterns/src/Observer/Observable.cpp@ 13e5be

stable v1.7.0
Last change on this file since 13e5be was 77c4a0, checked in by Frederik Heber <frederik.heber@…>, 5 years ago

Observable_protector requires noexcept(false).

  • otherwise we cannot use the CircleDetection unit test anymore as C++11 default behavior is to terminate if an exception is thrown during a destructor (here, Observable_protector, i.e. OBSERVE; going out of scope). The unit test expects an AssertionFailure which we then no longer obtain but the whole test terminates. Therefore we set to noexcept(false) and allow exceptions to escape.
  • Property mode set to 100644
File size: 19.1 KB
RevLine 
[084729c]1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010 University of Bonn. All rights reserved.
5 * Please see the LICENSE file or "Copyright notice" in builder.cpp for details.
6 */
7
8/*
9 * Observable.cpp
10 *
11 * Created on: Dec 1, 2011
12 * Author: heber
13 */
14
15// include config.h
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
[9eb71b3]20//#include "CodePatterns/MemDebug.hpp"
[084729c]21
22#include "CodePatterns/Observer/Observable.hpp"
23
24#include "CodePatterns/Assert.hpp"
25#include "CodePatterns/Observer/Channels.hpp"
26#include "CodePatterns/Observer/defs.hpp"
27#include "CodePatterns/Observer/Notification.hpp"
28
29#include <algorithm>
30
31#include <boost/thread/locks.hpp>
32#include <boost/thread/recursive_mutex.hpp>
33
34//!> This function does nothing with the given Observable
35void NoOp_informer(const Observable *)
36{}
37
38Observable::graveyard_informer_t Observable::noop_informer(&NoOp_informer);
39
40Observable::ChannelMap Observable::NotificationChannels;
41
42/** Attaching Sub-observables to Observables.
43 * Increases entry in Observable::(GlobalObservableInfo::getInstance().getdepth()) for this \a *publisher by one.
44 *
45 * The two functions \sa start_observer_internal() and \sa finish_observer_internal()
46 * have to be used together at all time. Never use these functions directly
47 * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop
48 * thus producing compiler-errors whenever only one is used.
49 * \param *publisher reference of sub-observable
50 */
51void Observable::start_observer_internal(Observable *publisher)
52{
53 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
54 // increase the count for this observable by one
55 // if no entry for this observable is found, an new one is created
56 // by the STL and initialized to 0 (see STL documentation)
57#ifdef LOG_OBSERVER
58 observerLog().addMessage((GlobalObservableInfo::getInstance().getdepth())[publisher]) << ">> Locking " << observerLog().getName(publisher);
59#endif
60 (GlobalObservableInfo::getInstance().getdepth())[publisher]++;
61}
62
63/** Detaching Sub-observables from Observables.
64 * Decreases entry in Observable::(GlobalObservableInfo::getInstance().getdepth()) for this \a *publisher by one. If zero, we
65 * start notifying all our Observers.
66 *
67 * The two functions start_observer_internal() and finish_observer_internal()
68 * have to be used together at all time. Never use these functions directly
69 * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop
70 * thus producing compiler-errors whenever only one is used.
71 * \param *publisher reference of sub-observable
72 */
73void Observable::finish_observer_internal(Observable *publisher)
74{
75 // decrease the count for this observable
76 // if zero is reached all observed blocks are done and we can
77 // start to notify our observers
78 int depth_publisher = 0;
79 {
80 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
81 --(GlobalObservableInfo::getInstance().getdepth())[publisher];
82#ifdef LOG_OBSERVER
83 observerLog().addMessage((GlobalObservableInfo::getInstance().getdepth())[publisher]) << "<< Unlocking " << observerLog().getName(publisher);
84#endif
85 depth_publisher = (GlobalObservableInfo::getInstance().getdepth())[publisher];
86 }
87 if(depth_publisher){}
88 else{
89 publisher->notifyAll();
90 // this item is done, so we don't have to keep the count with us
91 // save some memory by erasing it
92 {
93 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
94 (GlobalObservableInfo::getInstance().getdepth()).erase(publisher);
95 }
96 }
97}
98
99void Observable::enque_notification_internal(Observable *publisher, Notification_ptr notification)
100{
101 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
102 (GlobalObservableInfo::getInstance().getnotifications())[publisher].insert(notification);
103}
104
105/** Constructor for Observable Protector.
106 * Basically, calls start_observer_internal(). Hence use this class instead of
107 * calling the function directly.
108 *
109 * \param *protege Observable to be protected.
110 */
111Observable::_Observable_protector::_Observable_protector(Observable *_protege) :
112 protege(_protege)
113{
114 start_observer_internal(protege);
115}
116
117Observable::_Observable_protector::_Observable_protector(const _Observable_protector &dest) :
118 protege(dest.protege)
119{
120 start_observer_internal(protege);
121}
122
123/** Destructor for Observable Protector.
124 * Basically, calls finish_observer_internal(). Hence use this class instead of
125 * calling the function directly.
126 *
127 * \param *protege Observable to be protected.
128 */
[77c4a0]129Observable::_Observable_protector::~_Observable_protector() noexcept(false)
[084729c]130{
131 finish_observer_internal(protege);
132}
133
134/************* Notification mechanism for observables **************/
135
136/** Notify all Observers of changes.
137 * Puts \a *this into Observable::(GlobalObservableInfo::getInstance().getbusyObservables()), calls Observer::update() for all in callee_t
138 * and removes from busy list.
139 */
140void Observable::notifyAll() {
141#ifdef LOG_OBSERVER
142 observerLog().addMessage() << "--> " << observerLog().getName(this)
143 << " is about to inform all its Observers.";
144#endif
145 // we are busy notifying others right now
146 // add ourselves to the list of busy subjects to enable circle detection
147 {
148 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
149 (GlobalObservableInfo::getInstance().getbusyObservables()).insert(this);
150 }
151 // see if anyone has signed up for observation
152 // and call all observers
153 try {
[9ac3d3]154 bool callTable_contains;
155 {
156 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
157 GlobalObservableInfo::calltable_t& callTable = GlobalObservableInfo::getInstance().getcallTable();
158 callTable_contains = callTable.find(this) != callTable.end();
159 }
[084729c]160 if (callTable_contains) {
[9ac3d3]161 GlobalObservableInfo::callees_t callees;
162 {
163 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
164 GlobalObservableInfo::calltable_t& callTable = GlobalObservableInfo::getInstance().getcallTable();
165 callees = callTable[this];
166 }
[084729c]167 // elements are stored sorted by keys in the multimap
168 // so iterating over it gives us a the callees sorted by
169 // the priorities
170 // copy such that signOff() within receiving update() does not affect iterating
171 // this is because within the same thread and with the updateKilled() signOff() may be
172 // called and when executed it modifies targets
173 GlobalObservableInfo::callees_t::iterator iter;
174 for(iter=callees.begin();iter!=callees.end();++iter){
175#ifdef LOG_OBSERVER
176 observerLog().addMessage() << "-> Sending update from " << observerLog().getName(this)
177 << " to " << observerLog().getName((*iter).second)
178 << " (priority=" << (*iter).first << ")";
179#endif
180 (*iter).second->update(this);
181 }
182 }
183 }
184 ASSERT_NOCATCH("Exception thrown from Observer Update");
185
186 // send out all notifications that need to be done
187 {
[9ac3d3]188 GlobalObservableInfo::notificationSet currentNotifications;
189 {
190 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
191 currentNotifications = (GlobalObservableInfo::getInstance().getnotifications())[this];
192 }
[084729c]193 for(GlobalObservableInfo::notificationSet::iterator it = currentNotifications.begin();
194 it != currentNotifications.end();++it){
195 (*it)->notifyAll(this);
196 }
197 }
198
199 {
200 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
201 (GlobalObservableInfo::getInstance().getnotifications()).erase(this);
202
203 // done with notification, we can leave the set of busy subjects
204 (GlobalObservableInfo::getInstance().getbusyObservables()).erase(this);
205 }
206
207#ifdef LOG_OBSERVER
208 observerLog().addMessage() << "--> " << observerLog().getName(this)
209 << " is done informing all its Observers.";
210#endif
211}
212
213
214/** Handles passing on updates from sub-Observables.
215 * Mimicks basically the Observer::update() function.
216 *
217 * \param *publisher The \a *this we observe.
218 */
219void Observable::update(Observable *publisher) {
220 // circle detection
221 bool presentCircle = false;
222 {
223 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
224 presentCircle = (GlobalObservableInfo::getInstance().getbusyObservables()).find(this)!=(GlobalObservableInfo::getInstance().getbusyObservables()).end();
225 }
226 if(presentCircle) {
227 // somehow a circle was introduced... we were busy notifying our
228 // observers, but still we are called by one of our sub-Observables
229 // we cannot be sure observation will still work at this point
230 ASSERT(0,"Circle detected in observation-graph.\n"
231 "Observation-graph always needs to be a DAG to work correctly!\n"
232 "Please check your observation code and fix this!\n");
233 return;
234 }
235 else {
[9ac3d3]236 bool depth_contains;
237 {
238 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
239 // see if we are in the process of changing ourselves
240 // if we are changing ourselves at the same time our sub-observables change
241 // we do not need to publish all the changes at each time we are called
242 std::map<Observable*, int>& depth = GlobalObservableInfo::getInstance().getdepth();
243 depth_contains = depth.find(this)==depth.end();
244 }
[084729c]245 if(depth_contains) {
246#ifdef LOG_OBSERVER
247 observerLog().addMessage() << "-* Update from " << observerLog().getName(publisher)
248 << " propagated by " << observerLog().getName(this);
249#endif
250 notifyAll();
251 }
252 else{
253#ifdef LOG_OBSERVER
254 observerLog().addMessage() << "-| Update from " << observerLog().getName(publisher)
255 << " not propagated by " << observerLog().getName(this);
256#endif
257 }
258 }
259}
260
261/** Sign on an Observer to this Observable.
262 * Puts \a *target into Observable::(GlobalObservableInfo::getInstance().getcallTable()) list.
263 * \param *target Observer
264 * \param priority number in [-20,20]
265 */
266void Observable::signOn(Observer *target, GlobalObservableInfo::PriorityLevel priority) const
267{
268#ifdef LOG_OBSERVER
269 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target) << " to " << observerLog().getName(const_cast<Observable *>(this));
270#endif
271 bool res = false;
272 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
273 GlobalObservableInfo::callees_t &callees = (GlobalObservableInfo::getInstance().getcallTable())[const_cast<Observable *>(this)];
274
275 GlobalObservableInfo::callees_t::iterator iter;
276 for(iter=callees.begin();iter!=callees.end();++iter){
277 res |= ((*iter).second == target);
278 }
279 if(!res)
280 callees.insert(std::pair<int,Observer*>(priority.level,target));
281}
282
283/** Sign off an Observer from this Observable.
284 * Removes \a *target from Observable::(GlobalObservableInfo::getInstance().getcallTable()) list.
285 * \param *target Observer
286 */
287void Observable::signOff(Observer *target) const
288{
289 {
290 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
291 GlobalObservableInfo::calltable_t &callTable = GlobalObservableInfo::getInstance().getcallTable();
292 ASSERT(callTable.count(const_cast<Observable *>(this)),
293 "SignOff called for an Observable without Observers.");
294#ifdef LOG_OBSERVER
295 observerLog().addMessage() << "** Signing off " << observerLog().getName(target) << " from " << observerLog().getName(const_cast<Observable *>(this));
296#endif
297 GlobalObservableInfo::callees_t &callees = callTable[const_cast<Observable *>(this)];
298
299 GlobalObservableInfo::callees_t::iterator iter;
300 GlobalObservableInfo::callees_t::iterator deliter;
301 for(iter=callees.begin();iter!=callees.end();) {
302 if((*iter).second == target) {
303 callees.erase(iter++);
304 }
305 else {
306 ++iter;
307 }
308 }
309 if(callees.empty()){
310 callTable.erase(const_cast<Observable *>(this));
311 }
312 }
313 (*graveyard_informer)(this);
314}
315
316void Observable::signOn(
317 Observer *target,
318 size_t channelno,
319 GlobalObservableInfo::PriorityLevel priority) const
320{
321 Notification_ptr notification = getChannel(channelno);
322#ifdef LOG_OBSERVER
323 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target)
324 << " to " << observerLog().getName(const_cast<Observable *>(this))
325 << "'s channel no." << channelno << ".";
326#endif
327 notification->addObserver(target, priority.level);
328}
329
330void Observable::signOff(Observer *target, size_t channelno) const
331{
332 Notification_ptr notification = getChannel(channelno);
333#ifdef LOG_OBSERVER
334 observerLog().addMessage() << "** Signing off " << observerLog().getName(target)
335 << " from " << observerLog().getName(const_cast<Observable *>(this))
336 << "'s channel no." << channelno << ".";
337#endif
338 notification->removeObserver(target);
339 (*graveyard_informer)(this);
340}
341
342bool Observable::isBlocked() const
343{
344 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
345 return (GlobalObservableInfo::getInstance().getdepth()).count(const_cast<Observable *>(this)) > 0;
346}
347
348Notification_ptr Observable::getChannel(size_t no) const
349{
350 return getNotificationChannel(this, no);
351}
352
353size_t Observable::getNumberOfObservers() const
354{
355 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
356 size_t ObserverCount = 0;
357 {
358 GlobalObservableInfo::calltable_t &callTable = GlobalObservableInfo::getInstance().getcallTable();
359 GlobalObservableInfo::calltable_t::const_iterator callees_t_iter =
360 callTable.find(const_cast<Observable *>(this));
361 // if not present, then we have zero observers
362 if (callees_t_iter != callTable.end())
363 ObserverCount += callees_t_iter->second.size();
364 }
365 {
366 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
367 const Channels *OurChannels = getNotificationChannels(this);
368 if (OurChannels != NULL)
369 for (Channels::NotificationTypetoRefMap::const_iterator channeliter = OurChannels->ChannelMap.begin();
370 channeliter != OurChannels->ChannelMap.end();
371 ++channeliter)
372 ObserverCount += (channeliter->second)->getNumberOfObservers();
373 }
374 return ObserverCount;
375}
376
377/** Handles sub-observables that just got killed
378 * when an sub-observerable dies we usually don't need to do anything
379 * \param *publisher Sub-Observable.
380 */
381void Observable::subjectKilled(Observable *publisher)
382{
383}
384
385/** Constructor for class Observable.
386 */
387Observable::Observable(
388 std::string name,
389 const channels_t &_channels) :
390 Observer(Observer::BaseConstructor()),
391 graveyard_informer(&noop_informer)
392{
393#ifdef LOG_OBSERVER
394 observerLog().addName(this,name);
395 observerLog().addMessage() << "++ Creating Observable "
396 << observerLog().getName(static_cast<Observable *>(this));
397#endif
398
399 if (!_channels.empty()) {
400 Channels *OurChannel = new Channels;
401 // add instance for each notification type
402 for (channels_t::const_iterator iter = _channels.begin();
403 iter != _channels.end(); ++iter)
404 OurChannel->addChannel(*iter);
405 insertNotificationChannel( std::make_pair(static_cast<Observable *>(this), OurChannel) );
406 }
407}
408
409/** Destructor for class Observable.
410 * When an observable is deleted, we let all our observers know. \sa Observable::subjectKilled().
411 */
412Observable::~Observable()
413{
414#ifdef LOG_OBSERVER
415 observerLog().addMessage() << "-- Destroying Observable "
416 << observerLog().getName(static_cast<Observable *>(this));
417#endif
418 bool CallTable_contains = false;
419 {
420 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
421 CallTable_contains = (GlobalObservableInfo::getInstance().getcallTable()).count(this);
422 }
423 if(CallTable_contains) {
[9ac3d3]424 GlobalObservableInfo::callees_t callees;
425 {
426 // copy the list from the map
427 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
428 // copy such that signOff() within receiving subjectKilled() does not affect iterating
429 // this is because within the same thread and with the subjectKilled() signOff() may be
430 // called and when executed it modifies targets
431 callees = (GlobalObservableInfo::getInstance().getcallTable())[this];
432 }
[084729c]433 // delete all entries for this observable
434 GlobalObservableInfo::callees_t::iterator iter;
435 for(iter=callees.begin();iter!=callees.end();++iter)
436 (*iter).second->subjectKilled(this);
437 // erase the list in the map
[9ac3d3]438 {
439 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
440 (GlobalObservableInfo::getInstance().getcallTable()).erase(this);
441 }
[084729c]442 }
443
444 // also kill instance in static Channels map if present
445 eraseNotificationChannel(this);
446}
447
448Observable::channels_t Observable::getChannelList(const size_t max)
449{
450 channels_t channels(max);
451 std::generate(channels.begin(), channels.end(), UniqueNumber());
452 return channels;
453}
454
455void Observable::insertNotificationChannel(std::pair<Observable*, Channels *> _pair)
456{
457 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
458 NotificationChannels.insert(_pair);
459}
460
461void Observable::eraseNotificationChannel(Observable * const _target)
462{
463 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
464 ChannelMap::iterator iter = NotificationChannels.find(static_cast<Observable *>(_target));
465 if (iter != NotificationChannels.end()) {
466 iter->second->subjectKilled(static_cast<Observable *>(_target));
467 delete iter->second;
468 NotificationChannels.erase(iter);
469 }
470}
471
472bool Observable::isNotificationChannelPresent(const Observable * const _target)
473{
474 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
475 ChannelMap::const_iterator iter =
476 NotificationChannels.find(const_cast<Observable * const>(_target));
477 return iter != NotificationChannels.end();
478}
479
480
481const Channels* Observable::getNotificationChannels(const Observable * const _target)
482{
483 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
484 ChannelMap::const_iterator iter =
485 NotificationChannels.find(const_cast<Observable * const>(_target));
486 if (iter != NotificationChannels.end())
487 return iter->second;
488 else
489 return NULL;
490}
491
492Notification_ptr Observable::getNotificationChannel(const Observable * const _target, const size_t _no)
493{
494 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
495 ChannelMap::const_iterator iter =
496 NotificationChannels.find(const_cast<Observable * const>(_target));
497 ASSERT(iter != NotificationChannels.end(),
498 "Observable::getNotificationChannel() - could not find channel for target "
499 +toString(_target)+".");
500 return iter->second->getChannel(_no);
501}
Note: See TracBrowser for help on using the repository browser.