source: src/CodePatterns/ObservedValue.hpp@ 084729c

Action_Thermostats Add_AtomRandomPerturbation Add_RotateAroundBondAction Add_SelectAtomByNameAction Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_StructOpt_integration_tests Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_ChronosMutex Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion GeometryObjects Gui_displays_atomic_force_velocity IndependentFragmentGrids_IntegrationTest JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks RotateToPrincipalAxisSystem_UndoRedo StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg ThirdParty_MPQC_rebuilt_buildsystem TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps Ubuntu_1604_changes stable
Last change on this file since 084729c was 084729c, checked in by Frederik Heber <heber@…>, 8 years ago

Squashed 'ThirdParty/CodePatterns/' content from commit c1e1041

git-subtree-dir: ThirdParty/CodePatterns
git-subtree-split: c1e10418c454f98be2f43d93167642b0008428fc

  • Property mode set to 100644
File size: 7.5 KB
Line 
1/*
2 * ObservedValue.hpp
3 *
4 * Created on: Jun 19, 2015
5 * Author: heber
6 */
7
8#ifndef OBSERVEDVALUE_HPP_
9#define OBSERVEDVALUE_HPP_
10
11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include <boost/thread/locks.hpp>
17#include <boost/thread/recursive_mutex.hpp>
18#include <list>
19
20#include "CodePatterns/Assert.hpp"
21
22#include "CodePatterns/Observer/Observable.hpp"
23#include "CodePatterns/Observer/Notification.hpp"
24
25/** This class wraps a value in another class and instance which we want to read
26 * from in a thread-safe manner.
27 *
28 * The information in this class is directly derived from information in another
29 * class that infrequently changes and whose changes are propagated through the
30 * Observer/Observable mechanism. I.e. it is very similar to the \sa Chacheable
31 * class, however here the information is updated directly. In other words, this
32 * class allows for disconnecting two classes from another when they depend
33 * oneway or bothways from information in the other class.
34 *
35 * A classical example is a GUI that needs to display internal data. Changes in
36 * the GUI occur too slowly, i.e. the class owning the value being displayed
37 * may be changed and destroyed before the GUI is actually updated. To safely
38 * access the value, the ObservedValue is put in between two class and the GUI.
39 * The value must be accessible via a getter and changes to the value must be
40 * propagated via update() or recieveNotification().
41 *
42 * In other words, the update is split up into two paths: a short path where
43 * just the required minimal information is updated (e.g. new position of the
44 * atom as \a NDIM \a Vector), and a long path where the result of the
45 * information update is created (e.g. the atom is redrawn). The short path
46 * must be short, i.e. require only few instructions, while the long path may
47 * contain many and may even be taken in a lazy fashion, i.e. regardless of
48 * the numnber of updates this path is only called every 1/20 second.
49 *
50 * The general building blocks are then as follows:
51 * -# an instance with an ObservedValue<T> \ foo as member variable
52 * -# for convenience the instance may construct an internal references to
53 * the entitiy that is observed for information update (and that is passed
54 * on to ObservedValue<T> cstor)
55 * -# static member function \a updateFoo() - this obtains an updated value used
56 * by \a foo upon receiving Observable's update() or recieveNotification()
57 * (e.g. requesting getPosition() from the respective \a atom). It may be
58 * static as it is independent of the internal state of the instance
59 * containing \a foo
60 * -# member function \a resetFoo() that uses ObservedValue<int>::get() to
61 * obtain a (thread)safe copy of the value containing all information to
62 * internally update the instance (e.g. redraw an atom's sphere at the new
63 * position)
64 * -# the instance containing \a foo is subscribed to the same channels and
65 * calls resetFoo() upon receiving the signal.
66 *
67 * For an example code see the respective unit test and the stub class used
68 * there.
69 */
70template <typename T>
71class ObservedValue : public Observer
72{
73public:
74 ObservedValue(
75 const Observable * const _owner,
76 const boost::function<T()> &_recalcMethod,
77 const std::string &_name,
78 const T &_initialvalue,
79 const Observable::channels_t &_channels);
80 ObservedValue(const ObservedValue &);
81 virtual ~ObservedValue();
82
83 // methods implemented for base-class Observer
84 void update(Observable *publisher);
85 void recieveNotification(Observable *publisher, Notification_ptr notification);
86 virtual void subjectKilled(Observable *publisher);
87
88 const T& get() const;
89
90private:
91 void activateObserver();
92 void deactivateObserver();
93
94private:
95 const Observable * const owner;
96 const boost::function<T()> recalcMethod;
97
98 //!> contains list of possible channels to enlist, if empty we signOn globally
99 const Observable::channels_t channels;
100
101 //!> whether we are still signed on or not
102 bool signedOn;
103
104 //!> mutex to ensure access is only per-thread
105 mutable boost::recursive_mutex signedOnLock;
106
107 //!> mutex to ensure access is only per-thread
108 mutable boost::recursive_mutex valueLock;
109
110 //!> internal value associated with changes in Observable
111 T value;
112};
113
114template <typename T>
115ObservedValue<T>::ObservedValue(
116 const Observable * const _owner,
117 const boost::function<T()> &_recalcMethod,
118 const std::string &_name,
119 const T &_initialvalue,
120 const Observable::channels_t &_channels) :
121 Observer(_name + "(Cached)"),
122 owner(_owner),
123 recalcMethod(_recalcMethod),
124 channels(_channels),
125 signedOn(false),
126 value(_initialvalue)
127{
128 // we sign on with the best(=lowest) priority, so cached values are recalculated before
129 // anybody else might ask for updated values
130 activateObserver();
131}
132
133// de-activated copy constructor
134template <typename T>
135ObservedValue<T>::ObservedValue(const ObservedValue&)
136{
137 ASSERT(0,"ObservedValues should never be copied");
138}
139
140template <typename T>
141ObservedValue<T>::~ObservedValue()
142{
143 // make highest priorty such that ObservedValues are always updated first
144 deactivateObserver();
145}
146
147template <typename T>
148void ObservedValue<T>::activateObserver()
149{
150 boost::lock_guard<boost::recursive_mutex> guard(signedOnLock);
151 if ((owner != NULL) && (!signedOn)) {
152 if (channels.empty()) {
153 owner->signOn(this,GlobalObservableInfo::PriorityLevel(int(-20)));
154 } else {
155 for (Observable::channels_t::const_iterator iter = channels.begin();
156 iter != channels.end(); ++iter)
157 owner->signOn(this,*iter);
158 }
159 signedOn = true;
160 }
161}
162
163template <typename T>
164void ObservedValue<T>::deactivateObserver()
165{
166 boost::lock_guard<boost::recursive_mutex> guard(signedOnLock);
167 if ((owner != NULL) && (signedOn)) {
168 if (channels.empty()) {
169 owner->signOff(this);
170 } else {
171 for (Observable::channels_t::const_iterator iter = channels.begin();
172 iter != channels.end(); ++iter)
173 owner->signOff(this,*iter);
174 }
175 signedOn = false;
176 }
177}
178
179template <typename T>
180void ObservedValue<T>::update(Observable *publisher)
181{
182 ASSERT( channels.empty(),
183 "ObservedValue<T>::update() - received general update although we are channel-centric.");
184#ifdef LOG_OBSERVER
185 observerLog().addMessage() << "## ObservedValue " << observerLog().getName(this)
186 << " received global update.";
187#endif
188 boost::lock_guard<boost::recursive_mutex> guard(valueLock);
189 value = recalcMethod();
190}
191
192template <typename T>
193void ObservedValue<T>::recieveNotification(Observable *publisher, Notification_ptr notification)
194{
195 ASSERT( !channels.empty(),
196 "ObservedValue<T>::update() - received channel update although we are global.");
197
198 if (publisher == owner) {
199 const Observable::channels_t::const_iterator iter =
200 std::find(channels.begin(), channels.end(), notification->getChannelNo());
201 if (iter != channels.end()) {
202#ifdef LOG_OBSERVER
203 observerLog().addMessage() << "## ObservedValue " << observerLog().getName(this)
204 << " received local update in channel " << notification->getChannelNo() << ".";
205#endif
206 boost::lock_guard<boost::recursive_mutex> guard(valueLock);
207 value = recalcMethod();
208 }
209 }
210}
211
212template <typename T>
213void ObservedValue<T>::subjectKilled(Observable *publisher)
214{
215 boost::lock_guard<boost::recursive_mutex> guard(signedOnLock);
216 if (publisher == owner) {
217 signedOn = false;
218 }
219}
220
221template <typename T>
222const T& ObservedValue<T>::get() const
223{
224 boost::lock_guard<boost::recursive_mutex> guard(valueLock);
225 return value;
226}
227
228#endif /* OBSERVEDVALUE_HPP_ */
Note: See TracBrowser for help on using the repository browser.