source: src/Observer/Observable.cpp@ e24dde

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

FIX: Made Cacheables internally stored Observable const.

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