source: src/Patterns/Observer.cpp@ 0d1ad0

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 0d1ad0 was 112b09, checked in by Tillmann Crueger <crueger@…>, 15 years ago

Added #include "Helpers/MemDebug.hpp" to all .cpp files

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