source: src/Patterns/Observer.cpp@ d5f216

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 d5f216 was 63c1f6, checked in by Tillmann Crueger <crueger@…>, 15 years ago

Added generic observer pattern framework.
(cherry picked from commit 7bc7ce52eb7b4d606d90f49cbfa9da7a300c5d82)

Conflicts:

molecuilder/src/Makefile.am
molecuilder/src/unittests/Makefile.am

  • Property mode set to 100644
File size: 4.6 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
13using namespace std;
14
15/****************** Static stuff for the observer mechanism ************/
16
17// All infrastructure for the observer-pattern is bundled at a central place
18// this is more efficient if many objects can be observed (inherit from observable)
19// but only few are actually coupled with observers. E.g. TMV has over 500.000 Atoms,
20// which might become observable. Handling Observerable infrastructure in each of
21// these would use memory for each atom. By handling Observer-infrastructure
22// here we only need memory for objects that actually are observed.
23// See [Gamma et al, 1995] p. 297
24
25map<Observable*, int> Observable::depth;
26multimap<Observable*,Observer*> Observable::callTable;
27set<Observable*> Observable::busyObservables;
28
29// The two functions start_observer_internal and finish_observer_internal
30// have to be used together at all time. Never use these functions directly
31// START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop
32// thus producing compiler-errors whenever only one is used
33
34void Observable::start_observer_internal(Observable *publisher){
35 // increase the count for this observable by one
36 // if no entry for this observable is found, an new one is created
37 // by the STL and initialized to 0 (see STL documentation)
38 depth[publisher]++;
39}
40
41void Observable::finish_observer_internal(Observable *publisher){
42 // decrease the count for this observable
43 // if zero is reached all observed blocks are done and we can
44 // start to notify our observers
45 if(--(depth[publisher])){}
46 else{
47 publisher->notifyAll();
48 // this item is done, so we don't have to keep the count with us
49 // save some memory by erasing it
50 depth.erase(publisher);
51 }
52}
53
54/************* Notification mechanism for observables **************/
55
56typedef multimap<Observable*,Observer*>::iterator callIterator;
57typedef pair<callIterator, callIterator> iteratorRange;
58
59void Observable::notifyAll() {
60 // we are busy notifying others right now
61 // add ourselves to the list of busy subjects to enable circle detection
62 busyObservables.insert(this);
63 // see if anyone has signed up for observation
64 // and call all observers
65 if(callTable.count(this)) {
66 iteratorRange callerRange = callTable.equal_range(this);
67 callIterator iter;
68 for(iter=callerRange.first;iter!=callerRange.second;iter++){
69 (*iter).second->update(this);
70 }
71 }
72 // done with notification, we can leave the set of busy subjects
73 busyObservables.erase(this);
74}
75
76// this handles passing on updates from sub-Observables
77void Observable::update(Observable *publisher) {
78 // circle detection
79 if(busyObservables.find(this)!=busyObservables.end()) {
80 // somehow a circle was introduced... we were busy notifying our
81 // observers, but still we are called by one of our sub-Observables
82 // we cannot be sure observation will still work at this point
83 cerr << "Circle detected in observation-graph." << endl;
84 cerr << "Observation-graph always needs to be a DAG to work correctly!" << endl;
85 cerr << "Please check your observation code and fix this!" << endl;
86 return;
87 }
88 else {
89 // see if we are in the process of changing ourselves
90 // if we are changing ourselves at the same time our sub-observables change
91 // we do not need to publish all the changes at each time we are called
92 if(depth.find(this)==depth.end()) {
93 notifyAll();
94 }
95 }
96}
97
98// methods to sign-on and off
99void Observable::signOn(Observer *target) {
100 bool res = false;
101 iteratorRange callerRange = callTable.equal_range(this);
102 callIterator iter;
103 for(iter=callerRange.first;iter!=callerRange.second;iter++){
104 res |= ((*iter).second == target);
105 }
106 if(!res)
107 callTable.insert(pair<Observable*,Observer*>(this,target));
108}
109
110void Observable::signOff(Observer *target) {
111 iteratorRange callerRange = callTable.equal_range(this);
112 callIterator iter;
113 for(iter=callerRange.first;iter!=callerRange.second;iter++) {
114 if((*iter).second == target)
115 callTable.erase(iter);
116 }
117}
118
119// when an sub-observerable dies we usually don't need to do anything
120void Observable::subjectKilled(Observable *publisher){
121}
122
123Observable::Observable()
124{}
125
126// when an observable is deleted, we let all our observers know
127Observable::~Observable()
128{
129 // delete all entries for this observable
130 iteratorRange callerRange = callTable.equal_range(this);
131 callIterator iter;
132 for(iter=callerRange.first;iter!=callerRange.second;iter++){
133 (*iter).second->subjectKilled(this);
134 }
135 callTable.erase(callerRange.first,callerRange.second);
136}
137
138Observer::Observer()
139{}
140
141Observer::~Observer()
142{}
Note: See TracBrowser for help on using the repository browser.