source: src/Observer/Observable.cpp@ 3d87df

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

Observable::notifyAll() gives ObserverLog()ged messages on start and stop of informing Observers.

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