source: src/Observer/Observable.cpp@ 1b5188

Last change on this file since 1b5188 was 1b5188, checked in by Frederik Heber <heber@…>, 10 years ago

Observables are now protected by mutexes, i.e. allow for concurrent use.

  • Property mode set to 100644
File size: 15.6 KB
RevLine 
[a80f419]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/*
[e2e035e]9 * Observable.cpp
[a80f419]10 *
[e2e035e]11 * Created on: Dec 1, 2011
12 * Author: heber
[a80f419]13 */
14
15// include config.h
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
[9b8fa4]20#include "CodePatterns/MemDebug.hpp"
[a80f419]21
[9b8fa4]22#include "CodePatterns/Observer/Observable.hpp"
[8fe1e2]23
[9b8fa4]24#include "CodePatterns/Assert.hpp"
25#include "CodePatterns/Observer/Channels.hpp"
26#include "CodePatterns/Observer/defs.hpp"
27#include "CodePatterns/Observer/Notification.hpp"
[a80f419]28
[b324a3]29//!> This function does nothing with the given Observable
30void NoOp_informer(const Observable *)
31{}
32
33Observable::graveyard_informer_t Observable::noop_informer(&NoOp_informer);
[a80f419]34
35// All infrastructure for the observer-pattern is bundled at a central place
36// this is more efficient if many objects can be observed (inherit from observable)
37// but only few are actually coupled with observers. E.g. TMV has over 500.000 Atoms,
[70672e3]38// which might become observable. Handling Observable infrastructure in each of
[a80f419]39// these would use memory for each atom. By handling Observer-infrastructure
40// here we only need memory for objects that actually are observed.
41// See [Gamma et al, 1995] p. 297
42
[e2e035e]43std::map<Observable*, int> Observable::depth; //!< Map of Observables to the depth of the DAG of Observers
44std::map<Observable*,std::multimap<int,Observer*> > Observable::callTable; //!< Table for each Observable of all its Observers
[8fe1e2]45std::map<Observable*,std::set<Notification*> > Observable::notifications; //!< Table for all current notifications to perform
[e2e035e]46std::set<Observable*> Observable::busyObservables; //!< Set of Observables that are currently busy notifying their sign-on'ed Observers
[bc2698]47Observable::ChannelMap Observable::NotificationChannels; //!< Map of Observables to their Channels.
[1b5188]48boost::recursive_mutex Observable::ObservablesMapLock; //!< mutex for locking the above maps
[a80f419]49
[8fe1e2]50// ValidRange must be initialized before PriorityLevel.
51range<int> Observable::PriorityLevel::ValidRange(-20, 21);
52Observable::PriorityLevel Observable::PriorityDefault(int(0));
53
54/** Constructor of PriorityLevel.
55 *
56 * \note We check whether the level is within Observable::PriorityLevel::ValidRange.
57 *
58 * @param i priority level encapsulated in this class.
59 */
60Observable::PriorityLevel::PriorityLevel(const int i) :
61 level(i)
62{
63 ASSERT(ValidRange.isInRange(level),
64 "Observable::PriorityLevel::PriorityLevel() - Priority level "
65 +toString(level)+" out of range "+toString(ValidRange)+".");
66}
67
68Observable::PriorityLevel::~PriorityLevel()
69{}
70
[a80f419]71/** Attaching Sub-observables to Observables.
72 * Increases entry in Observable::depth for this \a *publisher by one.
73 *
74 * The two functions \sa start_observer_internal() and \sa finish_observer_internal()
75 * have to be used together at all time. Never use these functions directly
76 * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop
77 * thus producing compiler-errors whenever only one is used.
78 * \param *publisher reference of sub-observable
79 */
[1b5188]80void Observable::start_observer_internal(Observable *publisher)
81{
82 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[a80f419]83 // increase the count for this observable by one
84 // if no entry for this observable is found, an new one is created
85 // by the STL and initialized to 0 (see STL documentation)
86#ifdef LOG_OBSERVER
[2c11c1]87 observerLog().addMessage(depth[publisher]) << ">> Locking " << observerLog().getName(publisher);
[a80f419]88#endif
89 depth[publisher]++;
90}
91
92/** Detaching Sub-observables from Observables.
93 * Decreases entry in Observable::depth for this \a *publisher by one. If zero, we
94 * start notifying all our Observers.
95 *
96 * The two functions start_observer_internal() and finish_observer_internal()
97 * have to be used together at all time. Never use these functions directly
98 * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop
99 * thus producing compiler-errors whenever only one is used.
100 * \param *publisher reference of sub-observable
101 */
[1b5188]102void Observable::finish_observer_internal(Observable *publisher)
103{
[a80f419]104 // decrease the count for this observable
105 // if zero is reached all observed blocks are done and we can
106 // start to notify our observers
[1b5188]107 ObservablesMapLock.lock();
[a80f419]108 --depth[publisher];
[1b5188]109 ObservablesMapLock.unlock();
[a80f419]110#ifdef LOG_OBSERVER
[2c11c1]111 observerLog().addMessage(depth[publisher]) << "<< Unlocking " << observerLog().getName(publisher);
[a80f419]112#endif
[1b5188]113 ObservablesMapLock.lock();
114 int depth_publisher = depth[publisher];
115 ObservablesMapLock.unlock();
116 if(depth_publisher){}
[a80f419]117 else{
118 publisher->notifyAll();
119 // this item is done, so we don't have to keep the count with us
120 // save some memory by erasing it
[1b5188]121 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[a80f419]122 depth.erase(publisher);
123 }
124}
125
[1b5188]126void Observable::enque_notification_internal(Observable *publisher, Notification_ptr notification)
127{
128 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[a80f419]129 notifications[publisher].insert(notification);
130}
131
132/** Constructor for Observable Protector.
133 * Basically, calls start_observer_internal(). Hence use this class instead of
134 * calling the function directly.
135 *
136 * \param *protege Observable to be protected.
137 */
138Observable::_Observable_protector::_Observable_protector(Observable *_protege) :
139 protege(_protege)
140{
141 start_observer_internal(protege);
142}
143
144Observable::_Observable_protector::_Observable_protector(const _Observable_protector &dest) :
145 protege(dest.protege)
146{
147 start_observer_internal(protege);
148}
149
150/** Destructor for Observable Protector.
151 * Basically, calls finish_observer_internal(). Hence use this class instead of
152 * calling the function directly.
153 *
154 * \param *protege Observable to be protected.
155 */
156Observable::_Observable_protector::~_Observable_protector()
157{
158 finish_observer_internal(protege);
159}
160
161/************* Notification mechanism for observables **************/
162
163/** Notify all Observers of changes.
164 * Puts \a *this into Observable::busyObservables, calls Observer::update() for all in callee_t
165 * and removes from busy list.
166 */
167void Observable::notifyAll() {
[3d87df]168#ifdef LOG_OBSERVER
169 observerLog().addMessage() << "--> " << observerLog().getName(this)
170 << " is about to inform all its Observers.";
171#endif
[a80f419]172 // we are busy notifying others right now
173 // add ourselves to the list of busy subjects to enable circle detection
[1b5188]174 ObservablesMapLock.lock();
[a80f419]175 busyObservables.insert(this);
[1b5188]176 ObservablesMapLock.unlock();
[a80f419]177 // see if anyone has signed up for observation
178 // and call all observers
179 try {
[1b5188]180 ObservablesMapLock.lock();
181 const bool anybodyThere = callTable.count(this);
182 ObservablesMapLock.unlock();
183 if(anybodyThere) {
[a80f419]184 // elements are stored sorted by keys in the multimap
185 // so iterating over it gives us a the callees sorted by
186 // the priorities
[1b5188]187 ObservablesMapLock.lock();
[a80f419]188 callees_t callees = callTable[this];
[1b5188]189 ObservablesMapLock.unlock();
[a80f419]190 callees_t::iterator iter;
191 for(iter=callees.begin();iter!=callees.end();++iter){
192#ifdef LOG_OBSERVER
193 observerLog().addMessage() << "-> Sending update from " << observerLog().getName(this)
194 << " to " << observerLog().getName((*iter).second)
[2c11c1]195 << " (priority=" << (*iter).first << ")";
[a80f419]196#endif
197 (*iter).second->update(this);
198 }
199 }
200 }
201 ASSERT_NOCATCH("Exception thrown from Observer Update");
202
203 // send out all notifications that need to be done
204
205 notificationSet currentNotifications = notifications[this];
206 for(notificationSet::iterator it = currentNotifications.begin();
207 it != currentNotifications.end();++it){
[e2e035e]208 (*it)->notifyAll(this);
[a80f419]209 }
210
[1b5188]211 ObservablesMapLock.lock();
[a80f419]212 notifications.erase(this);
213
214 // done with notification, we can leave the set of busy subjects
215 busyObservables.erase(this);
[1b5188]216 ObservablesMapLock.unlock();
[3d87df]217
218#ifdef LOG_OBSERVER
219 observerLog().addMessage() << "--> " << observerLog().getName(this)
220 << " is done informing all its Observers.";
221#endif
[a80f419]222}
223
224
225/** Handles passing on updates from sub-Observables.
226 * Mimicks basically the Observer::update() function.
227 *
228 * \param *publisher The \a *this we observe.
229 */
230void Observable::update(Observable *publisher) {
231 // circle detection
[1b5188]232 ObservablesMapLock.lock();
233 const bool presentCircle = busyObservables.find(this)!=busyObservables.end();
234 ObservablesMapLock.unlock();
235 if(presentCircle) {
[a80f419]236 // somehow a circle was introduced... we were busy notifying our
237 // observers, but still we are called by one of our sub-Observables
238 // we cannot be sure observation will still work at this point
239 ASSERT(0,"Circle detected in observation-graph.\n"
240 "Observation-graph always needs to be a DAG to work correctly!\n"
241 "Please check your observation code and fix this!\n");
242 return;
243 }
244 else {
245 // see if we are in the process of changing ourselves
246 // if we are changing ourselves at the same time our sub-observables change
247 // we do not need to publish all the changes at each time we are called
[1b5188]248 ObservablesMapLock.lock();
249 const bool depth_this = depth.find(this)==depth.end();
250 ObservablesMapLock.unlock();
251 if(depth_this) {
[a80f419]252#ifdef LOG_OBSERVER
253 observerLog().addMessage() << "-* Update from " << observerLog().getName(publisher)
[2c11c1]254 << " propagated by " << observerLog().getName(this);
[a80f419]255#endif
256 notifyAll();
257 }
258 else{
259#ifdef LOG_OBSERVER
260 observerLog().addMessage() << "-| Update from " << observerLog().getName(publisher)
[2c11c1]261 << " not propagated by " << observerLog().getName(this);
[a80f419]262#endif
263 }
264 }
265}
266
267/** Sign on an Observer to this Observable.
268 * Puts \a *target into Observable::callTable list.
269 * \param *target Observer
270 * \param priority number in [-20,20]
271 */
[8fe1e2]272void Observable::signOn(Observer *target, PriorityLevel priority) const
[9e776f]273{
[a80f419]274#ifdef LOG_OBSERVER
[2c11c1]275 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target) << " to " << observerLog().getName(const_cast<Observable *>(this));
[a80f419]276#endif
277 bool res = false;
[1b5188]278 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[9e776f]279 callees_t &callees = callTable[const_cast<Observable *>(this)];
[a80f419]280
281 callees_t::iterator iter;
282 for(iter=callees.begin();iter!=callees.end();++iter){
283 res |= ((*iter).second == target);
284 }
285 if(!res)
[8fe1e2]286 callees.insert(std::pair<int,Observer*>(priority.level,target));
[a80f419]287}
288
289/** Sign off an Observer from this Observable.
290 * Removes \a *target from Observable::callTable list.
291 * \param *target Observer
292 */
[9e776f]293void Observable::signOff(Observer *target) const
294{
[1b5188]295 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[e2e035e]296 ASSERT(callTable.count(const_cast<Observable *>(this)),
297 "SignOff called for an Observable without Observers.");
[a80f419]298#ifdef LOG_OBSERVER
[2c11c1]299 observerLog().addMessage() << "** Signing off " << observerLog().getName(target) << " from " << observerLog().getName(const_cast<Observable *>(this));
[a80f419]300#endif
[9e776f]301 callees_t &callees = callTable[const_cast<Observable *>(this)];
[a80f419]302
303 callees_t::iterator iter;
304 callees_t::iterator deliter;
305 for(iter=callees.begin();iter!=callees.end();) {
306 if((*iter).second == target) {
307 callees.erase(iter++);
308 }
309 else {
310 ++iter;
311 }
312 }
313 if(callees.empty()){
[9e776f]314 callTable.erase(const_cast<Observable *>(this));
[a80f419]315 }
[f3d16a]316 (*graveyard_informer)(this);
[a80f419]317}
318
[40f2e6]319void Observable::signOn(Observer *target, size_t channelno) const
[9e776f]320{
[1b5188]321 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[40f2e6]322 Notification_ptr notification = getChannel(channelno);
[1c291d]323#ifdef LOG_OBSERVER
324 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target)
325 << " to " << observerLog().getName(const_cast<Observable *>(this))
326 << "'s channel no." << channelno << ".";
327#endif
[a80f419]328 notification->addObserver(target);
329}
330
[40f2e6]331void Observable::signOff(Observer *target, size_t channelno) const
[9e776f]332{
[1b5188]333 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[40f2e6]334 Notification_ptr notification = getChannel(channelno);
[1c291d]335#ifdef LOG_OBSERVER
336 observerLog().addMessage() << "** Signing off " << observerLog().getName(target)
337 << " from " << observerLog().getName(const_cast<Observable *>(this))
338 << "'s channel no." << channelno << ".";
339#endif
[a80f419]340 notification->removeObserver(target);
[f3d16a]341 (*graveyard_informer)(this);
[a80f419]342}
343
[9e776f]344bool Observable::isBlocked() const
345{
[1b5188]346 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[9e776f]347 return depth.count(const_cast<Observable *>(this)) > 0;
[a80f419]348}
349
[74e0f7]350Notification_ptr Observable::getChannel(size_t no) const
351{
[1b5188]352 ObservablesMapLock.lock();
[e2e035e]353 const ChannelMap::const_iterator iter = NotificationChannels.find(const_cast<Observable * const>(this));
[1b5188]354 const bool status = iter != NotificationChannels.end();
355 Channels *OurChannel = NULL;
356 if (status)
357 OurChannel = iter->second;
358 ObservablesMapLock.unlock();
359 ASSERT(status,
[bc2698]360 "Observable::getChannel() - we do not have a channel in NotificationChannels.");
361 ASSERT(OurChannel != NULL,
[74e0f7]362 "Observable::getChannel() - observable has no channels.");
[bc2698]363 return OurChannel->getChannel(no);
[74e0f7]364}
365
[b324a3]366size_t Observable::getNumberOfObservers() const
367{
[1b5188]368 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[b324a3]369 size_t ObserverCount = 0;
370 {
371 std::map<Observable*,callees_t>::const_iterator callTableiter =
372 callTable.find(const_cast<Observable *>(this));
373 // if not present, then we have zero observers
374 if (callTableiter != callTable.end())
375 ObserverCount += callTableiter->second.size();
376 }
377 {
378 const ChannelMap::const_iterator iter =
379 NotificationChannels.find(const_cast<Observable * const>(this));
380 // if not present, then we have zero observers
381 if (iter != NotificationChannels.end())
382 for (Channels::NotificationTypetoRefMap::const_iterator channeliter = iter->second->ChannelMap.begin();
383 channeliter != iter->second->ChannelMap.end();
384 ++channeliter)
385 ObserverCount += (channeliter->second)->getNumberOfObservers();
386 }
387 return ObserverCount;
388}
389
[a80f419]390/** Handles sub-observables that just got killed
391 * when an sub-observerable dies we usually don't need to do anything
392 * \param *publisher Sub-Observable.
393 */
[1b5188]394void Observable::subjectKilled(Observable *publisher)
395{
[a80f419]396}
397
398/** Constructor for class Observable.
399 */
[e2e035e]400Observable::Observable(std::string name) :
[b324a3]401 Observer(Observer::BaseConstructor()),
[f3d16a]402 graveyard_informer(&noop_informer)
[a80f419]403{
404#ifdef LOG_OBSERVER
405 observerLog().addName(this,name);
[1c291d]406 observerLog().addMessage() << "++ Creating Observable "
407 << observerLog().getName(static_cast<Observable *>(this));
[a80f419]408#endif
409}
410
411/** Destructor for class Observable.
412 * When an observable is deleted, we let all our observers know. \sa Observable::subjectKilled().
413 */
414Observable::~Observable()
415{
416#ifdef LOG_OBSERVER
[1c291d]417 observerLog().addMessage() << "-- Destroying Observable "
418 << observerLog().getName(static_cast<Observable *>(this));
[a80f419]419#endif
[1b5188]420 boost::recursive_mutex::scoped_lock guard(ObservablesMapLock);
[a80f419]421 if(callTable.count(this)) {
422 // delete all entries for this observable
[1b5188]423 ObservablesMapLock.lock();
[a80f419]424 callees_t callees = callTable[this];
[1b5188]425 ObservablesMapLock.unlock();
[a80f419]426 callees_t::iterator iter;
[1c291d]427 for(iter=callees.begin();iter!=callees.end();++iter)
[a80f419]428 (*iter).second->subjectKilled(this);
[1b5188]429 ObservablesMapLock.lock();
[a80f419]430 callTable.erase(this);
[1b5188]431 ObservablesMapLock.unlock();
[a80f419]432 }
[e429dc]433
434 // also kill instance in static Channels map if present
[1c291d]435 ChannelMap::iterator iter = NotificationChannels.find(static_cast<Observable *>(this));
[e429dc]436 if (iter != NotificationChannels.end()) {
[1c291d]437 iter->second->subjectKilled(static_cast<Observable *>(this));
[1b5188]438 ObservablesMapLock.lock();
[e429dc]439 delete iter->second;
440 NotificationChannels.erase(iter);
[1b5188]441 ObservablesMapLock.unlock();
[e429dc]442 }
[a80f419]443}
Note: See TracBrowser for help on using the repository browser.