source: src/Patterns/Observer.cpp@ cd5047

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 Candidate_v1.7.0 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 cd5047 was cd5047, checked in by Tillmann Crueger <crueger@…>, 15 years ago

Added Logging capabilities to Observer Framework

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