source: src/Patterns/Observer.cpp@ bc2698

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

NotificationChannels is now static entity.

  • this is in line with the other static entities on Observable that are intended to save memory (before we needed 8 bytes for the pointer in each and every Observable instances, whether it had channels or not.)
  • Modified NOTIFY macro to accomodate for the changes.
  • enhanced documentation of class Notifications.
  • Property mode set to 100644
File size: 15.2 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 * Observer.cpp
10 *
11 * Created on: Jan 19, 2010
12 * Author: crueger
13 */
14
15// include config.h
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
20#include "MemDebug.hpp"
21
22#include "Observer.hpp"
23
24#include <iostream>
25
26#include "Assert.hpp"
27#include "MemDebug.hpp"
28
29using namespace std;
30
31/****************** Static stuff for the observer mechanism ************/
32
33// All infrastructure for the observer-pattern is bundled at a central place
34// this is more efficient if many objects can be observed (inherit from observable)
35// but only few are actually coupled with observers. E.g. TMV has over 500.000 Atoms,
36// which might become observable. Handling Observable infrastructure in each of
37// these would use memory for each atom. By handling Observer-infrastructure
38// here we only need memory for objects that actually are observed.
39// See [Gamma et al, 1995] p. 297
40
41map<Observable*, int> Observable::depth; //!< Map of Observables to the depth of the DAG of Observers
42map<Observable*,multimap<int,Observer*> > Observable::callTable; //!< Table for each Observable of all its Observers
43std::map<Observable*,std::set<Notification*> > Observable::notifications;
44set<Observable*> Observable::busyObservables; //!< Set of Observables that are currently busy notifying their sign-on'ed Observers
45Observable::ChannelMap Observable::NotificationChannels; //!< Map of Observables to their Channels.
46
47/** Attaching Sub-observables to Observables.
48 * Increases entry in Observable::depth for this \a *publisher by one.
49 *
50 * The two functions \sa start_observer_internal() and \sa finish_observer_internal()
51 * have to be used together at all time. Never use these functions directly
52 * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop
53 * thus producing compiler-errors whenever only one is used.
54 * \param *publisher reference of sub-observable
55 */
56void Observable::start_observer_internal(Observable *publisher){
57 // increase the count for this observable by one
58 // if no entry for this observable is found, an new one is created
59 // by the STL and initialized to 0 (see STL documentation)
60#ifdef LOG_OBSERVER
61 observerLog().addMessage(depth[publisher]) << ">> Locking " << observerLog().getName(publisher) << endl;
62#endif
63 depth[publisher]++;
64}
65
66/** Detaching Sub-observables from Observables.
67 * Decreases entry in Observable::depth for this \a *publisher by one. If zero, we
68 * start notifying all our Observers.
69 *
70 * The two functions start_observer_internal() and finish_observer_internal()
71 * have to be used together at all time. Never use these functions directly
72 * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop
73 * thus producing compiler-errors whenever only one is used.
74 * \param *publisher reference of sub-observable
75 */
76void Observable::finish_observer_internal(Observable *publisher){
77 // decrease the count for this observable
78 // if zero is reached all observed blocks are done and we can
79 // start to notify our observers
80 --depth[publisher];
81#ifdef LOG_OBSERVER
82 observerLog().addMessage(depth[publisher]) << "<< Unlocking " << observerLog().getName(publisher) << endl;
83#endif
84 if(depth[publisher]){}
85 else{
86 publisher->notifyAll();
87 // this item is done, so we don't have to keep the count with us
88 // save some memory by erasing it
89 depth.erase(publisher);
90 }
91}
92
93void Observable::enque_notification_internal(Observable *publisher, Notification_ptr notification){
94 ASSERT(notification->owner==publisher,"Some object tried to send a notification it does not own");
95 notifications[publisher].insert(notification);
96}
97
98/** Constructor for Observable Protector.
99 * Basically, calls start_observer_internal(). Hence use this class instead of
100 * calling the function directly.
101 *
102 * \param *protege Observable to be protected.
103 */
104Observable::_Observable_protector::_Observable_protector(Observable *_protege) :
105 protege(_protege)
106{
107 start_observer_internal(protege);
108}
109
110Observable::_Observable_protector::_Observable_protector(const _Observable_protector &dest) :
111 protege(dest.protege)
112{
113 start_observer_internal(protege);
114}
115
116/** Destructor for Observable Protector.
117 * Basically, calls finish_observer_internal(). Hence use this class instead of
118 * calling the function directly.
119 *
120 * \param *protege Observable to be protected.
121 */
122Observable::_Observable_protector::~_Observable_protector()
123{
124 finish_observer_internal(protege);
125}
126
127/************* Notification mechanism for observables **************/
128
129/** Notify all Observers of changes.
130 * Puts \a *this into Observable::busyObservables, calls Observer::update() for all in callee_t
131 * and removes from busy list.
132 */
133void Observable::notifyAll() {
134 // we are busy notifying others right now
135 // add ourselves to the list of busy subjects to enable circle detection
136 busyObservables.insert(this);
137 // see if anyone has signed up for observation
138 // and call all observers
139 try {
140 if(callTable.count(this)) {
141 // elements are stored sorted by keys in the multimap
142 // so iterating over it gives us a the callees sorted by
143 // the priorities
144 callees_t callees = callTable[this];
145 callees_t::iterator iter;
146 for(iter=callees.begin();iter!=callees.end();++iter){
147#ifdef LOG_OBSERVER
148 observerLog().addMessage() << "-> Sending update from " << observerLog().getName(this)
149 << " to " << observerLog().getName((*iter).second)
150 << " (priority=" << (*iter).first << ")"<< endl;
151#endif
152 (*iter).second->update(this);
153 }
154 }
155 }
156 ASSERT_NOCATCH("Exception thrown from Observer Update");
157
158 // send out all notifications that need to be done
159
160 notificationSet currentNotifications = notifications[this];
161 for(notificationSet::iterator it = currentNotifications.begin();
162 it != currentNotifications.end();++it){
163 (*it)->notifyAll();
164 }
165
166 notifications.erase(this);
167
168 // done with notification, we can leave the set of busy subjects
169 busyObservables.erase(this);
170}
171
172
173/** Handles passing on updates from sub-Observables.
174 * Mimicks basically the Observer::update() function.
175 *
176 * \param *publisher The \a *this we observe.
177 */
178void Observable::update(Observable *publisher) {
179 // circle detection
180 if(busyObservables.find(this)!=busyObservables.end()) {
181 // somehow a circle was introduced... we were busy notifying our
182 // observers, but still we are called by one of our sub-Observables
183 // we cannot be sure observation will still work at this point
184 ASSERT(0,"Circle detected in observation-graph.\n"
185 "Observation-graph always needs to be a DAG to work correctly!\n"
186 "Please check your observation code and fix this!\n");
187 return;
188 }
189 else {
190 // see if we are in the process of changing ourselves
191 // if we are changing ourselves at the same time our sub-observables change
192 // we do not need to publish all the changes at each time we are called
193 if(depth.find(this)==depth.end()) {
194#ifdef LOG_OBSERVER
195 observerLog().addMessage() << "-* Update from " << observerLog().getName(publisher)
196 << " propagated by " << observerLog().getName(this) << endl;
197#endif
198 notifyAll();
199 }
200 else{
201#ifdef LOG_OBSERVER
202 observerLog().addMessage() << "-| Update from " << observerLog().getName(publisher)
203 << " not propagated by " << observerLog().getName(this) << endl;
204#endif
205 }
206 }
207}
208
209/** Sign on an Observer to this Observable.
210 * Puts \a *target into Observable::callTable list.
211 * \param *target Observer
212 * \param priority number in [-20,20]
213 */
214void Observable::signOn(Observer *target,int priority) const
215{
216 ASSERT(priority>=-20 && priority<=+20, "Priority out of range [-20:+20] when signing on Observer");
217#ifdef LOG_OBSERVER
218 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target) << " to " << observerLog().getName(const_cast<Observable *>(this)) << endl;
219#endif
220 bool res = false;
221 callees_t &callees = callTable[const_cast<Observable *>(this)];
222
223 callees_t::iterator iter;
224 for(iter=callees.begin();iter!=callees.end();++iter){
225 res |= ((*iter).second == target);
226 }
227 if(!res)
228 callees.insert(pair<int,Observer*>(priority,target));
229}
230
231/** Sign off an Observer from this Observable.
232 * Removes \a *target from Observable::callTable list.
233 * \param *target Observer
234 */
235void Observable::signOff(Observer *target) const
236{
237 ASSERT(callTable.count(const_cast<Observable *>(this)),"SignOff called for an Observable without Observers.");
238#ifdef LOG_OBSERVER
239 observerLog().addMessage() << "** Signing off " << observerLog().getName(target) << " from " << observerLog().getName(const_cast<Observable *>(this)) << endl;
240#endif
241 callees_t &callees = callTable[const_cast<Observable *>(this)];
242
243 callees_t::iterator iter;
244 callees_t::iterator deliter;
245 for(iter=callees.begin();iter!=callees.end();) {
246 if((*iter).second == target) {
247 callees.erase(iter++);
248 }
249 else {
250 ++iter;
251 }
252 }
253 if(callees.empty()){
254 callTable.erase(const_cast<Observable *>(this));
255 }
256}
257
258void Observable::signOn(Observer *target, Notification_ptr notification) const
259{
260 ASSERT(notification->owner==this,
261 "Trying to sign on for a notification that is not provided by this object");
262
263 notification->addObserver(target);
264}
265
266void Observable::signOff(Observer *target, Notification_ptr notification) const
267{
268 ASSERT(notification->owner==this,
269 "Trying to sign off from a notification that is not provided by this object");
270
271 notification->removeObserver(target);
272}
273
274bool Observable::isBlocked() const
275{
276 return depth.count(const_cast<Observable *>(this)) > 0;
277}
278
279Notification_ptr Observable::getChannel(size_t no) const
280{
281 ChannelMap::iterator iter = NotificationChannels.find(const_cast<Observable *>(this));
282 ASSERT(iter != NotificationChannels.end(),
283 "Observable::getChannel() - we do not have a channel in NotificationChannels.");
284 const Channels *OurChannel = iter->second;
285 ASSERT(OurChannel != NULL,
286 "Observable::getChannel() - observable has no channels.");
287 return OurChannel->getChannel(no);
288}
289
290/** Handles sub-observables that just got killed
291 * when an sub-observerable dies we usually don't need to do anything
292 * \param *publisher Sub-Observable.
293 */
294void Observable::subjectKilled(Observable *publisher){
295}
296
297/** Constructor for class Observable.
298 */
299Observable::Observable(string name) :
300 Observer(Observer::BaseConstructor())
301{
302#ifdef LOG_OBSERVER
303 observerLog().addName(this,name);
304 observerLog().addMessage() << "++ Creating Observable " << observerLog().getName(this) << endl;
305#endif
306}
307
308/** Destructor for class Observable.
309 * When an observable is deleted, we let all our observers know. \sa Observable::subjectKilled().
310 */
311Observable::~Observable()
312{
313#ifdef LOG_OBSERVER
314 observerLog().addMessage() << "-- Destroying Observable " << observerLog().getName(this) << endl;
315#endif
316 if(callTable.count(this)) {
317 // delete all entries for this observable
318 callees_t callees = callTable[this];
319 callees_t::iterator iter;
320 for(iter=callees.begin();iter!=callees.end();++iter){
321 (*iter).second->subjectKilled(this);
322 }
323 callTable.erase(this);
324 }
325}
326
327/** Constructor for class Observer.
328 */
329Observer::Observer(string name)
330{
331#ifdef LOG_OBSERVER
332 observerLog().addName(this,name);
333 observerLog().addMessage() << "++ Creating Observer " << observerLog().getName(this) << endl;
334#endif
335}
336
337/**
338 * Base Constructor for class Observer
339 *
340 * only called from Observable Constructor
341 */
342Observer::Observer(Observer::BaseConstructor){
343#ifdef LOG_OBSERVER
344 observerLog().addObservable(this);
345#endif
346}
347
348/** Destructor for class Observer.
349 */
350Observer::~Observer()
351{
352#ifdef LOG_OBSERVER
353 if(!observerLog().isObservable(this)){
354 observerLog().addMessage() << "-- Destroying Observer " << observerLog().getName(this) << endl;
355 }
356#endif
357}
358
359/**
360 * Method for specialized notifications.
361 * Most Observers wont need or use this, so it is implemented
362 * empty in the base case;
363 */
364void Observer::recieveNotification(Observable *publisher, Notification_ptr notification){
365 ASSERT(0,"Notification received by object that did not sign on for it.");
366}
367
368Notification::Notification(Observable *_owner) :
369 owner(_owner), channelno(-1)
370{}
371
372Notification::Notification(Observable *_owner, size_t _channelno) :
373 owner(_owner), channelno(_channelno)
374{}
375
376Notification::~Notification(){}
377
378void Notification::addObserver(Observer *target){
379 targets.insert(target);
380}
381
382void Notification::removeObserver(Observer *target){
383 targets.erase(target);
384}
385
386void Notification::notifyAll(){
387 for(std::set<Observer*>::iterator it=targets.begin();
388 it!=targets.end();++it){
389 (*it)->recieveNotification(owner,this);
390 }
391}
392
393Channels::Channels(Observable *_owner) :
394 owner(_owner)
395{}
396
397Channels::~Channels()
398{
399 // free all present Notifications
400 for(NotificationTypetoRefMap::iterator iter = ChannelMap.begin();
401 !ChannelMap.empty(); iter = ChannelMap.begin()) {
402 delete iter->second;
403 ChannelMap.erase(iter);
404 }
405}
406
407void Channels::addChannel(size_t no)
408{
409 NotificationTypetoRefMap::const_iterator iter = ChannelMap.find(no);
410 ASSERT(iter == ChannelMap.end(),
411 "Channels::addChannel() - channel "+toString(int(no))+" is already present in ChannelMap.");
412 ChannelMap.insert( std::make_pair(no, new Notification(owner, no)) );
413}
414
415void Channels::removeChannel(size_t no)
416{
417 NotificationTypetoRefMap::iterator iter = ChannelMap.find(no);
418 ASSERT(iter != ChannelMap.end(),
419 "Channels::removeChannel() - channel "+toString(int(no))+" not present in ChannelMap.");
420 delete iter->second;
421 ChannelMap.erase(iter);
422}
423
424Notification_ptr Channels::getChannel(size_t no) const
425{
426 NotificationTypetoRefMap::const_iterator iter = ChannelMap.find(no);
427 ASSERT(iter != ChannelMap.end(),
428 "Channels::getChannel() - channel "+toString(int(no))+" not present in ChannelMap.");
429 return iter->second;
430}
431
432size_t Channels::getType(Notification_ptr channel) const
433{
434 return channel->getChannelNo();
435}
436
437
438#ifdef LOG_OBSERVER
439
440/************************* Methods to do logging of the Observer Mechanism *********/
441
442// The log needs to exist fairly early, so we make it construct on first use,
443// and never destroy it
444ObserverLog &observerLog(){
445 // yes, this memory is never freed... we need it around for the whole programm,
446 // so no freeing is possible
447 static ObserverLog *theLog = Memory::ignore(new ObserverLog());
448 return *theLog;
449}
450
451
452ObserverLog::ObserverLog() :
453 count (0)
454{}
455
456ObserverLog::~ObserverLog(){}
457
458string ObserverLog::getLog(){return log.str();}
459
460std::string ObserverLog::getName(void* obj){
461 return names[obj];
462}
463
464bool ObserverLog::isObservable(void* obj){
465 return observables.count(obj);
466}
467
468void ObserverLog::addName(void* obj , string name){
469 stringstream sstr;
470 sstr << name << "_" << count++;
471 names[obj] = sstr.str();
472}
473
474void ObserverLog::addObservable(void* obj){
475 observables.insert(obj);
476}
477
478void ObserverLog::deleteName(void* obj){
479 names.erase(obj);
480}
481
482void ObserverLog::deleteObservable(void* obj){
483 observables.erase(obj);
484}
485
486stringstream &ObserverLog::addMessage(int depth){
487 for(int i=depth;i--;)
488 log << " ";
489 return log;
490}
491
492#endif
Note: See TracBrowser for help on using the repository browser.