source: src/Patterns/Observer.cpp@ bf3817

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since bf3817 was bf3817, checked in by Frederik Heber <heber@…>, 15 years ago

Added ifdef HAVE_CONFIG and config.h include to each and every cpp file.

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