source: src/Patterns/Observer/Observable.cpp@ e2e035e

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

Refactored all Observer stuff into own subfolder and split up into distinct modules.

  • Observer/all.hpp is all that's needed.
  • Property mode set to 100644
File size: 10.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 "MemDebug.hpp"
21
22#include "Assert.hpp"
23#include "Channels.hpp"
24#include "Notification.hpp"
25#include "Observable.hpp"
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,
31// which might become observable. Handling Observable infrastructure in each of
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
36std::map<Observable*, int> Observable::depth; //!< Map of Observables to the depth of the DAG of Observers
37std::map<Observable*,std::multimap<int,Observer*> > Observable::callTable; //!< Table for each Observable of all its Observers
38std::map<Observable*,std::set<Notification*> > Observable::notifications;
39std::set<Observable*> Observable::busyObservables; //!< Set of Observables that are currently busy notifying their sign-on'ed Observers
40Observable::ChannelMap Observable::NotificationChannels; //!< Map of Observables to their Channels.
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 */
51void 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
56 observerLog().addMessage(depth[publisher]) << ">> Locking " << observerLog().getName(publisher) << std::endl;
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 */
71void 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
77 observerLog().addMessage(depth[publisher]) << "<< Unlocking " << observerLog().getName(publisher) << std::endl;
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
88void 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 */
98Observable::_Observable_protector::_Observable_protector(Observable *_protege) :
99 protege(_protege)
100{
101 start_observer_internal(protege);
102}
103
104Observable::_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 */
116Observable::_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 */
127void 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)
144 << " (priority=" << (*iter).first << ")"<< std::endl;
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){
157 (*it)->notifyAll(this);
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 */
172void 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)
190 << " propagated by " << observerLog().getName(this) << std::endl;
191#endif
192 notifyAll();
193 }
194 else{
195#ifdef LOG_OBSERVER
196 observerLog().addMessage() << "-| Update from " << observerLog().getName(publisher)
197 << " not propagated by " << observerLog().getName(this) << std::endl;
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 */
208void Observable::signOn(Observer *target,int priority) const
209{
210 ASSERT(priority>=-20 && priority<=+20,
211 "Priority out of range [-20:+20] when signing on Observer");
212#ifdef LOG_OBSERVER
213 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target) << " to " << observerLog().getName(const_cast<Observable *>(this)) << std::endl;
214#endif
215 bool res = false;
216 callees_t &callees = callTable[const_cast<Observable *>(this)];
217
218 callees_t::iterator iter;
219 for(iter=callees.begin();iter!=callees.end();++iter){
220 res |= ((*iter).second == target);
221 }
222 if(!res)
223 callees.insert(std::pair<int,Observer*>(priority,target));
224}
225
226/** Sign off an Observer from this Observable.
227 * Removes \a *target from Observable::callTable list.
228 * \param *target Observer
229 */
230void Observable::signOff(Observer *target) const
231{
232 ASSERT(callTable.count(const_cast<Observable *>(this)),
233 "SignOff called for an Observable without Observers.");
234#ifdef LOG_OBSERVER
235 observerLog().addMessage() << "** Signing off " << observerLog().getName(target) << " from " << observerLog().getName(const_cast<Observable *>(this)) << std::endl;
236#endif
237 callees_t &callees = callTable[const_cast<Observable *>(this)];
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()){
250 callTable.erase(const_cast<Observable *>(this));
251 }
252}
253
254void Observable::signOn(Observer *target, Notification_ptr notification) const
255{
256 notification->addObserver(target);
257}
258
259void Observable::signOff(Observer *target, Notification_ptr notification) const
260{
261 notification->removeObserver(target);
262}
263
264bool Observable::isBlocked() const
265{
266 return depth.count(const_cast<Observable *>(this)) > 0;
267}
268
269Notification_ptr Observable::getChannel(size_t no) const
270{
271 const ChannelMap::const_iterator iter = NotificationChannels.find(const_cast<Observable * const>(this));
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,
276 "Observable::getChannel() - observable has no channels.");
277 return OurChannel->getChannel(no);
278}
279
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 */
284void Observable::subjectKilled(Observable *publisher){
285}
286
287/** Constructor for class Observable.
288 */
289Observable::Observable(std::string name) :
290 Observer(Observer::BaseConstructor())
291{
292#ifdef LOG_OBSERVER
293 observerLog().addName(this,name);
294 observerLog().addMessage() << "++ Creating Observable " << observerLog().getName(this) << std::endl;
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 */
301Observable::~Observable()
302{
303#ifdef LOG_OBSERVER
304 observerLog().addMessage() << "-- Destroying Observable " << observerLog().getName(this) << std::endl;
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}
Note: See TracBrowser for help on using the repository browser.