source: src/Observer/Observable.cpp@ 9b8fa4

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

Huge update of file structure to place installation header files into right folder.

  • The problem ist that we desire use as include "CodePatterns/...". For this to work, especially with the new Observer subfolder structure, it has been necessary to place all header files away from their source files into a distinct folder called CodePatterns. This emulates the later, after make install present structure.
  • essentially all source and header files had to be changed to adapt the include.
  • all Makefile.am's had to be changed.
  • nobase_ ... was removed such that header files are installed flat and not creating their subfolder along the process.
  • We placed Observer into its own convenience library and own folder Observer away from Patterns.

Some other changes:

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