| [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 |  | 
|---|
| [9098f9] | 20 | #include "MemDebug.hpp" | 
|---|
| [a80f419] | 21 |  | 
|---|
| [9098f9] | 22 | #include "Assert.hpp" | 
|---|
| [e2e035e] | 23 | #include "Channels.hpp" | 
|---|
|  | 24 | #include "Notification.hpp" | 
|---|
|  | 25 | #include "Observable.hpp" | 
|---|
| [a80f419] | 26 |  | 
|---|
|  | 27 |  | 
|---|
|  | 28 | // All infrastructure for the observer-pattern is bundled at a central place | 
|---|
|  | 29 | // this is more efficient if many objects can be observed (inherit from observable) | 
|---|
|  | 30 | // but only few are actually coupled with observers. E.g. TMV has over 500.000 Atoms, | 
|---|
| [70672e3] | 31 | // which might become observable. Handling Observable infrastructure in each of | 
|---|
| [a80f419] | 32 | // these would use memory for each atom. By handling Observer-infrastructure | 
|---|
|  | 33 | // here we only need memory for objects that actually are observed. | 
|---|
|  | 34 | // See [Gamma et al, 1995] p. 297 | 
|---|
|  | 35 |  | 
|---|
| [e2e035e] | 36 | std::map<Observable*, int> Observable::depth;  //!< Map of Observables to the depth of the DAG of Observers | 
|---|
|  | 37 | std::map<Observable*,std::multimap<int,Observer*> > Observable::callTable; //!< Table for each Observable of all its Observers | 
|---|
| [a80f419] | 38 | std::map<Observable*,std::set<Notification*> > Observable::notifications; | 
|---|
| [e2e035e] | 39 | std::set<Observable*> Observable::busyObservables; //!< Set of Observables that are currently busy notifying their sign-on'ed Observers | 
|---|
| [bc2698] | 40 | Observable::ChannelMap Observable::NotificationChannels; //!< Map of Observables to their Channels. | 
|---|
| [a80f419] | 41 |  | 
|---|
|  | 42 | /** Attaching Sub-observables to Observables. | 
|---|
|  | 43 | * Increases entry in Observable::depth 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 | */ | 
|---|
|  | 51 | void Observable::start_observer_internal(Observable *publisher){ | 
|---|
|  | 52 | // increase the count for this observable by one | 
|---|
|  | 53 | // if no entry for this observable is found, an new one is created | 
|---|
|  | 54 | // by the STL and initialized to 0 (see STL documentation) | 
|---|
|  | 55 | #ifdef LOG_OBSERVER | 
|---|
| [e2e035e] | 56 | observerLog().addMessage(depth[publisher]) << ">> Locking " << observerLog().getName(publisher) << std::endl; | 
|---|
| [a80f419] | 57 | #endif | 
|---|
|  | 58 | depth[publisher]++; | 
|---|
|  | 59 | } | 
|---|
|  | 60 |  | 
|---|
|  | 61 | /** Detaching Sub-observables from Observables. | 
|---|
|  | 62 | * Decreases entry in Observable::depth for this \a *publisher by one. If zero, we | 
|---|
|  | 63 | * start notifying all our Observers. | 
|---|
|  | 64 | * | 
|---|
|  | 65 | * The two functions start_observer_internal() and finish_observer_internal() | 
|---|
|  | 66 | * have to be used together at all time. Never use these functions directly | 
|---|
|  | 67 | * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop | 
|---|
|  | 68 | * thus producing compiler-errors whenever only one is used. | 
|---|
|  | 69 | * \param *publisher reference of sub-observable | 
|---|
|  | 70 | */ | 
|---|
|  | 71 | void Observable::finish_observer_internal(Observable *publisher){ | 
|---|
|  | 72 | // decrease the count for this observable | 
|---|
|  | 73 | // if zero is reached all observed blocks are done and we can | 
|---|
|  | 74 | // start to notify our observers | 
|---|
|  | 75 | --depth[publisher]; | 
|---|
|  | 76 | #ifdef LOG_OBSERVER | 
|---|
| [e2e035e] | 77 | observerLog().addMessage(depth[publisher]) << "<< Unlocking " << observerLog().getName(publisher) << std::endl; | 
|---|
| [a80f419] | 78 | #endif | 
|---|
|  | 79 | if(depth[publisher]){} | 
|---|
|  | 80 | else{ | 
|---|
|  | 81 | publisher->notifyAll(); | 
|---|
|  | 82 | // this item is done, so we don't have to keep the count with us | 
|---|
|  | 83 | // save some memory by erasing it | 
|---|
|  | 84 | depth.erase(publisher); | 
|---|
|  | 85 | } | 
|---|
|  | 86 | } | 
|---|
|  | 87 |  | 
|---|
|  | 88 | void Observable::enque_notification_internal(Observable *publisher, Notification_ptr notification){ | 
|---|
|  | 89 | notifications[publisher].insert(notification); | 
|---|
|  | 90 | } | 
|---|
|  | 91 |  | 
|---|
|  | 92 | /** Constructor for Observable Protector. | 
|---|
|  | 93 | * Basically, calls start_observer_internal(). Hence use this class instead of | 
|---|
|  | 94 | * calling the function directly. | 
|---|
|  | 95 | * | 
|---|
|  | 96 | * \param *protege Observable to be protected. | 
|---|
|  | 97 | */ | 
|---|
|  | 98 | Observable::_Observable_protector::_Observable_protector(Observable *_protege) : | 
|---|
|  | 99 | protege(_protege) | 
|---|
|  | 100 | { | 
|---|
|  | 101 | start_observer_internal(protege); | 
|---|
|  | 102 | } | 
|---|
|  | 103 |  | 
|---|
|  | 104 | Observable::_Observable_protector::_Observable_protector(const _Observable_protector &dest) : | 
|---|
|  | 105 | protege(dest.protege) | 
|---|
|  | 106 | { | 
|---|
|  | 107 | start_observer_internal(protege); | 
|---|
|  | 108 | } | 
|---|
|  | 109 |  | 
|---|
|  | 110 | /** Destructor for Observable Protector. | 
|---|
|  | 111 | * Basically, calls finish_observer_internal(). Hence use this class instead of | 
|---|
|  | 112 | * calling the function directly. | 
|---|
|  | 113 | * | 
|---|
|  | 114 | * \param *protege Observable to be protected. | 
|---|
|  | 115 | */ | 
|---|
|  | 116 | Observable::_Observable_protector::~_Observable_protector() | 
|---|
|  | 117 | { | 
|---|
|  | 118 | finish_observer_internal(protege); | 
|---|
|  | 119 | } | 
|---|
|  | 120 |  | 
|---|
|  | 121 | /************* Notification mechanism for observables **************/ | 
|---|
|  | 122 |  | 
|---|
|  | 123 | /** Notify all Observers of changes. | 
|---|
|  | 124 | * Puts \a *this into Observable::busyObservables, calls Observer::update() for all in callee_t | 
|---|
|  | 125 | * and removes from busy list. | 
|---|
|  | 126 | */ | 
|---|
|  | 127 | void Observable::notifyAll() { | 
|---|
|  | 128 | // we are busy notifying others right now | 
|---|
|  | 129 | // add ourselves to the list of busy subjects to enable circle detection | 
|---|
|  | 130 | busyObservables.insert(this); | 
|---|
|  | 131 | // see if anyone has signed up for observation | 
|---|
|  | 132 | // and call all observers | 
|---|
|  | 133 | try { | 
|---|
|  | 134 | if(callTable.count(this)) { | 
|---|
|  | 135 | // elements are stored sorted by keys in the multimap | 
|---|
|  | 136 | // so iterating over it gives us a the callees sorted by | 
|---|
|  | 137 | // the priorities | 
|---|
|  | 138 | callees_t callees = callTable[this]; | 
|---|
|  | 139 | callees_t::iterator iter; | 
|---|
|  | 140 | for(iter=callees.begin();iter!=callees.end();++iter){ | 
|---|
|  | 141 | #ifdef LOG_OBSERVER | 
|---|
|  | 142 | observerLog().addMessage() << "-> Sending update from " << observerLog().getName(this) | 
|---|
|  | 143 | << " to " << observerLog().getName((*iter).second) | 
|---|
| [e2e035e] | 144 | << " (priority=" << (*iter).first << ")"<< std::endl; | 
|---|
| [a80f419] | 145 | #endif | 
|---|
|  | 146 | (*iter).second->update(this); | 
|---|
|  | 147 | } | 
|---|
|  | 148 | } | 
|---|
|  | 149 | } | 
|---|
|  | 150 | ASSERT_NOCATCH("Exception thrown from Observer Update"); | 
|---|
|  | 151 |  | 
|---|
|  | 152 | // send out all notifications that need to be done | 
|---|
|  | 153 |  | 
|---|
|  | 154 | notificationSet currentNotifications = notifications[this]; | 
|---|
|  | 155 | for(notificationSet::iterator it = currentNotifications.begin(); | 
|---|
|  | 156 | it != currentNotifications.end();++it){ | 
|---|
| [e2e035e] | 157 | (*it)->notifyAll(this); | 
|---|
| [a80f419] | 158 | } | 
|---|
|  | 159 |  | 
|---|
|  | 160 | notifications.erase(this); | 
|---|
|  | 161 |  | 
|---|
|  | 162 | // done with notification, we can leave the set of busy subjects | 
|---|
|  | 163 | busyObservables.erase(this); | 
|---|
|  | 164 | } | 
|---|
|  | 165 |  | 
|---|
|  | 166 |  | 
|---|
|  | 167 | /** Handles passing on updates from sub-Observables. | 
|---|
|  | 168 | * Mimicks basically the Observer::update() function. | 
|---|
|  | 169 | * | 
|---|
|  | 170 | * \param *publisher The \a *this we observe. | 
|---|
|  | 171 | */ | 
|---|
|  | 172 | void Observable::update(Observable *publisher) { | 
|---|
|  | 173 | // circle detection | 
|---|
|  | 174 | if(busyObservables.find(this)!=busyObservables.end()) { | 
|---|
|  | 175 | // somehow a circle was introduced... we were busy notifying our | 
|---|
|  | 176 | // observers, but still we are called by one of our sub-Observables | 
|---|
|  | 177 | // we cannot be sure observation will still work at this point | 
|---|
|  | 178 | ASSERT(0,"Circle detected in observation-graph.\n" | 
|---|
|  | 179 | "Observation-graph always needs to be a DAG to work correctly!\n" | 
|---|
|  | 180 | "Please check your observation code and fix this!\n"); | 
|---|
|  | 181 | return; | 
|---|
|  | 182 | } | 
|---|
|  | 183 | else { | 
|---|
|  | 184 | // see if we are in the process of changing ourselves | 
|---|
|  | 185 | // if we are changing ourselves at the same time our sub-observables change | 
|---|
|  | 186 | // we do not need to publish all the changes at each time we are called | 
|---|
|  | 187 | if(depth.find(this)==depth.end()) { | 
|---|
|  | 188 | #ifdef LOG_OBSERVER | 
|---|
|  | 189 | observerLog().addMessage() << "-* Update from " << observerLog().getName(publisher) | 
|---|
| [e2e035e] | 190 | << " propagated by " << observerLog().getName(this) << std::endl; | 
|---|
| [a80f419] | 191 | #endif | 
|---|
|  | 192 | notifyAll(); | 
|---|
|  | 193 | } | 
|---|
|  | 194 | else{ | 
|---|
|  | 195 | #ifdef LOG_OBSERVER | 
|---|
|  | 196 | observerLog().addMessage() << "-| Update from " <<  observerLog().getName(publisher) | 
|---|
| [e2e035e] | 197 | << " not propagated by " << observerLog().getName(this) << std::endl; | 
|---|
| [a80f419] | 198 | #endif | 
|---|
|  | 199 | } | 
|---|
|  | 200 | } | 
|---|
|  | 201 | } | 
|---|
|  | 202 |  | 
|---|
|  | 203 | /** Sign on an Observer to this Observable. | 
|---|
|  | 204 | * Puts \a *target into Observable::callTable list. | 
|---|
|  | 205 | * \param *target Observer | 
|---|
|  | 206 | * \param priority number in [-20,20] | 
|---|
|  | 207 | */ | 
|---|
| [9e776f] | 208 | void Observable::signOn(Observer *target,int priority) const | 
|---|
|  | 209 | { | 
|---|
| [e2e035e] | 210 | ASSERT(priority>=-20 && priority<=+20, | 
|---|
|  | 211 | "Priority out of range [-20:+20] when signing on Observer"); | 
|---|
| [a80f419] | 212 | #ifdef LOG_OBSERVER | 
|---|
| [e2e035e] | 213 | observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target) << " to " << observerLog().getName(const_cast<Observable *>(this)) << std::endl; | 
|---|
| [a80f419] | 214 | #endif | 
|---|
|  | 215 | bool res = false; | 
|---|
| [9e776f] | 216 | callees_t &callees = callTable[const_cast<Observable *>(this)]; | 
|---|
| [a80f419] | 217 |  | 
|---|
|  | 218 | callees_t::iterator iter; | 
|---|
|  | 219 | for(iter=callees.begin();iter!=callees.end();++iter){ | 
|---|
|  | 220 | res |= ((*iter).second == target); | 
|---|
|  | 221 | } | 
|---|
|  | 222 | if(!res) | 
|---|
| [e2e035e] | 223 | callees.insert(std::pair<int,Observer*>(priority,target)); | 
|---|
| [a80f419] | 224 | } | 
|---|
|  | 225 |  | 
|---|
|  | 226 | /** Sign off an Observer from this Observable. | 
|---|
|  | 227 | * Removes \a *target from Observable::callTable list. | 
|---|
|  | 228 | * \param *target Observer | 
|---|
|  | 229 | */ | 
|---|
| [9e776f] | 230 | void Observable::signOff(Observer *target) const | 
|---|
|  | 231 | { | 
|---|
| [e2e035e] | 232 | ASSERT(callTable.count(const_cast<Observable *>(this)), | 
|---|
|  | 233 | "SignOff called for an Observable without Observers."); | 
|---|
| [a80f419] | 234 | #ifdef LOG_OBSERVER | 
|---|
| [e2e035e] | 235 | observerLog().addMessage() << "** Signing off " << observerLog().getName(target) << " from " << observerLog().getName(const_cast<Observable *>(this)) << std::endl; | 
|---|
| [a80f419] | 236 | #endif | 
|---|
| [9e776f] | 237 | callees_t &callees = callTable[const_cast<Observable *>(this)]; | 
|---|
| [a80f419] | 238 |  | 
|---|
|  | 239 | callees_t::iterator iter; | 
|---|
|  | 240 | callees_t::iterator deliter; | 
|---|
|  | 241 | for(iter=callees.begin();iter!=callees.end();) { | 
|---|
|  | 242 | if((*iter).second == target) { | 
|---|
|  | 243 | callees.erase(iter++); | 
|---|
|  | 244 | } | 
|---|
|  | 245 | else { | 
|---|
|  | 246 | ++iter; | 
|---|
|  | 247 | } | 
|---|
|  | 248 | } | 
|---|
|  | 249 | if(callees.empty()){ | 
|---|
| [9e776f] | 250 | callTable.erase(const_cast<Observable *>(this)); | 
|---|
| [a80f419] | 251 | } | 
|---|
|  | 252 | } | 
|---|
|  | 253 |  | 
|---|
| [9e776f] | 254 | void Observable::signOn(Observer *target, Notification_ptr notification) const | 
|---|
|  | 255 | { | 
|---|
| [a80f419] | 256 | notification->addObserver(target); | 
|---|
|  | 257 | } | 
|---|
|  | 258 |  | 
|---|
| [9e776f] | 259 | void Observable::signOff(Observer *target, Notification_ptr notification) const | 
|---|
|  | 260 | { | 
|---|
| [a80f419] | 261 | notification->removeObserver(target); | 
|---|
|  | 262 | } | 
|---|
|  | 263 |  | 
|---|
| [9e776f] | 264 | bool Observable::isBlocked() const | 
|---|
|  | 265 | { | 
|---|
|  | 266 | return depth.count(const_cast<Observable *>(this)) > 0; | 
|---|
| [a80f419] | 267 | } | 
|---|
|  | 268 |  | 
|---|
| [74e0f7] | 269 | Notification_ptr Observable::getChannel(size_t no) const | 
|---|
|  | 270 | { | 
|---|
| [e2e035e] | 271 | const ChannelMap::const_iterator iter = NotificationChannels.find(const_cast<Observable * const>(this)); | 
|---|
| [bc2698] | 272 | ASSERT(iter != NotificationChannels.end(), | 
|---|
|  | 273 | "Observable::getChannel() - we do not have a channel in NotificationChannels."); | 
|---|
|  | 274 | const Channels *OurChannel = iter->second; | 
|---|
|  | 275 | ASSERT(OurChannel != NULL, | 
|---|
| [74e0f7] | 276 | "Observable::getChannel() - observable has no channels."); | 
|---|
| [bc2698] | 277 | return OurChannel->getChannel(no); | 
|---|
| [74e0f7] | 278 | } | 
|---|
|  | 279 |  | 
|---|
| [a80f419] | 280 | /** Handles sub-observables that just got killed | 
|---|
|  | 281 | *  when an sub-observerable dies we usually don't need to do anything | 
|---|
|  | 282 | *  \param *publisher Sub-Observable. | 
|---|
|  | 283 | */ | 
|---|
|  | 284 | void Observable::subjectKilled(Observable *publisher){ | 
|---|
|  | 285 | } | 
|---|
|  | 286 |  | 
|---|
|  | 287 | /** Constructor for class Observable. | 
|---|
|  | 288 | */ | 
|---|
| [e2e035e] | 289 | Observable::Observable(std::string name) : | 
|---|
| [a80f419] | 290 | Observer(Observer::BaseConstructor()) | 
|---|
|  | 291 | { | 
|---|
|  | 292 | #ifdef LOG_OBSERVER | 
|---|
|  | 293 | observerLog().addName(this,name); | 
|---|
| [e2e035e] | 294 | observerLog().addMessage() << "++ Creating Observable " << observerLog().getName(this) << std::endl; | 
|---|
| [a80f419] | 295 | #endif | 
|---|
|  | 296 | } | 
|---|
|  | 297 |  | 
|---|
|  | 298 | /** Destructor for class Observable. | 
|---|
|  | 299 | * When an observable is deleted, we let all our observers know. \sa Observable::subjectKilled(). | 
|---|
|  | 300 | */ | 
|---|
|  | 301 | Observable::~Observable() | 
|---|
|  | 302 | { | 
|---|
|  | 303 | #ifdef LOG_OBSERVER | 
|---|
| [e2e035e] | 304 | observerLog().addMessage() << "-- Destroying Observable " << observerLog().getName(this) << std::endl; | 
|---|
| [a80f419] | 305 | #endif | 
|---|
|  | 306 | if(callTable.count(this)) { | 
|---|
|  | 307 | // delete all entries for this observable | 
|---|
|  | 308 | callees_t callees = callTable[this]; | 
|---|
|  | 309 | callees_t::iterator iter; | 
|---|
|  | 310 | for(iter=callees.begin();iter!=callees.end();++iter){ | 
|---|
|  | 311 | (*iter).second->subjectKilled(this); | 
|---|
|  | 312 | } | 
|---|
|  | 313 | callTable.erase(this); | 
|---|
|  | 314 | } | 
|---|
|  | 315 | } | 
|---|