source: ThirdParty/CodePatterns/src/Observer/Observable.cpp@ c742bb1

Candidate_v1.7.1 stable
Last change on this file since c742bb1 was c8cb0d, checked in by Frederik Heber <frederik.heber@…>, 2 days ago

Streamlines channel creation in Observables.

  • CodePatterns is now version 1.3.4.
  • we no longer need to add the channels manually in the cstor of a class that derives from Observable. Instead, we just need to pass the maximum number of channels (as they are typically enumerated anyway) and they are generated and added.
  • added mutex protection when inserting.
  • adjusted class Relay to forward similar convenience cstors.
  • adjusted all call sites in molecuilder.
  • Property mode set to 100644
File size: 19.9 KB
Line 
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
20//#include "CodePatterns/MemDebug.hpp"
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 */
129Observable::_Observable_protector::~_Observable_protector() noexcept(false)
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 {
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 }
160 if (callTable_contains) {
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 }
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 {
188 GlobalObservableInfo::notificationSet currentNotifications;
189 {
190 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
191 currentNotifications = (GlobalObservableInfo::getInstance().getnotifications())[this];
192 }
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 {
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 }
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
385Observable::Observable(
386 std::string name,
387 const channels_t &_channels) :
388 Observer(Observer::BaseConstructor()),
389 graveyard_informer(&noop_informer)
390{
391#ifdef LOG_OBSERVER
392 observerLog().addName(this,name);
393 observerLog().addMessage() << "++ Creating Observable "
394 << observerLog().getName(static_cast<Observable *>(this));
395#endif
396
397 if (!_channels.empty()) {
398 Channels *OurChannel = new Channels;
399 // add instance for each notification type
400 for (channels_t::const_iterator iter = _channels.begin();
401 iter != _channels.end(); ++iter)
402 OurChannel->addChannel(*iter);
403 insertNotificationChannel( std::make_pair(static_cast<Observable *>(this), OurChannel) );
404 }
405}
406
407Observable::Observable(
408 std::string name,
409 const unsigned int _maximum_notification_types) :
410 Observer(Observer::BaseConstructor()),
411 graveyard_informer(&noop_informer)
412{
413#ifdef LOG_OBSERVER
414 observerLog().addName(this,name);
415 observerLog().addMessage() << "++ Creating Observable "
416 << observerLog().getName(static_cast<Observable *>(this));
417#endif
418 createAndInsertNotificationChannel(_maximum_notification_types);
419}
420
421/** Destructor for class Observable.
422 * When an observable is deleted, we let all our observers know. \sa Observable::subjectKilled().
423 */
424Observable::~Observable()
425{
426#ifdef LOG_OBSERVER
427 observerLog().addMessage() << "-- Destroying Observable "
428 << observerLog().getName(static_cast<Observable *>(this));
429#endif
430 bool CallTable_contains = false;
431 {
432 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
433 CallTable_contains = (GlobalObservableInfo::getInstance().getcallTable()).count(this);
434 }
435 if(CallTable_contains) {
436 GlobalObservableInfo::callees_t callees;
437 {
438 // copy the list from the map
439 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
440 // copy such that signOff() within receiving subjectKilled() does not affect iterating
441 // this is because within the same thread and with the subjectKilled() signOff() may be
442 // called and when executed it modifies targets
443 callees = (GlobalObservableInfo::getInstance().getcallTable())[this];
444 }
445 // delete all entries for this observable
446 GlobalObservableInfo::callees_t::iterator iter;
447 for(iter=callees.begin();iter!=callees.end();++iter)
448 (*iter).second->subjectKilled(this);
449 // erase the list in the map
450 {
451 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
452 (GlobalObservableInfo::getInstance().getcallTable()).erase(this);
453 }
454 }
455
456 // also kill instance in static Channels map if present
457 eraseNotificationChannel(this);
458}
459
460Observable::channels_t Observable::getChannelList(const size_t max)
461{
462 channels_t channels(max);
463 std::generate(channels.begin(), channels.end(), UniqueNumber());
464 return channels;
465}
466
467void Observable::insertNotificationChannel(std::pair<Observable*, Channels *> _pair)
468{
469 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
470 NotificationChannels.insert(_pair);
471}
472
473void Observable::createAndInsertNotificationChannel(const unsigned int _maximum_notification_types)
474{
475 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
476 Channels *OurChannel = new Channels;
477 // add instance for each notification type
478 for (size_t type = 0; type < _maximum_notification_types; ++type)
479 OurChannel->addChannel(type);
480 insertNotificationChannel( std::make_pair(static_cast<Observable *>(this), OurChannel) );
481}
482
483void Observable::eraseNotificationChannel(Observable * const _target)
484{
485 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
486 ChannelMap::iterator iter = NotificationChannels.find(static_cast<Observable *>(_target));
487 if (iter != NotificationChannels.end()) {
488 iter->second->subjectKilled(static_cast<Observable *>(_target));
489 delete iter->second;
490 NotificationChannels.erase(iter);
491 }
492}
493
494bool Observable::isNotificationChannelPresent(const Observable * const _target)
495{
496 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
497 ChannelMap::const_iterator iter =
498 NotificationChannels.find(const_cast<Observable * const>(_target));
499 return iter != NotificationChannels.end();
500}
501
502
503const Channels* Observable::getNotificationChannels(const Observable * const _target)
504{
505 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
506 ChannelMap::const_iterator iter =
507 NotificationChannels.find(const_cast<Observable * const>(_target));
508 if (iter != NotificationChannels.end())
509 return iter->second;
510 else
511 return NULL;
512}
513
514Notification_ptr Observable::getNotificationChannel(const Observable * const _target, const size_t _no)
515{
516 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
517 ChannelMap::const_iterator iter =
518 NotificationChannels.find(const_cast<Observable * const>(_target));
519 ASSERT(iter != NotificationChannels.end(),
520 "Observable::getNotificationChannel() - could not find channel for target "
521 +toString(_target)+".");
522 return iter->second->getChannel(_no);
523}
Note: See TracBrowser for help on using the repository browser.