source: src/CodePatterns/Cacheable.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: 9.7 KB
Line 
1/*
2 * Cacheable.hpp
3 *
4 * Created on: Feb 2, 2010
5 * Author: crueger
6 */
7
8#ifndef CACHEABLE_HPP_
9#define CACHEABLE_HPP_
10
11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include "Observer/Observable.hpp"
17#include "Observer/Observer.hpp"
18#include <boost/function.hpp>
19#include <boost/shared_ptr.hpp>
20#include <boost/thread/locks.hpp>
21#include <boost/thread/recursive_mutex.hpp>
22
23#include "CodePatterns/Assert.hpp"
24
25#include "CodePatterns/Observer/Notification.hpp"
26
27#ifndef NO_CACHING
28
29 template<typename T>
30 class Cacheable : public Observer
31 {
32 // we define the states of the cacheable so we can do very fast state-checks
33 class State{
34 public:
35 State(Cacheable * const _owner) :
36 busy(false),
37 owner(_owner)
38 {}
39 virtual T& getValue()=0;
40 virtual void invalidate()=0;
41 virtual bool isValid()=0;
42 virtual void enter()=0;
43 bool isBusy(){
44 return busy;
45 }
46 virtual std::string getName()=0;
47 protected:
48 bool busy;
49 Cacheable * const owner;
50 };
51
52 class InvalidState : public State{
53 public:
54 InvalidState(Cacheable * const _owner):
55 State(_owner)
56 {}
57
58 virtual T& getValue(){
59 // set the state to valid
60 State::owner->switchState(State::owner->validState);
61 // get the value from the now valid state
62 return State::owner->state->getValue();
63 }
64
65 virtual void invalidate(){
66 // nothing to do on this message
67 }
68
69 virtual bool isValid(){
70 return false;
71 }
72
73 virtual void enter(){
74 // nothing to do when entering this
75 }
76
77 virtual std::string getName(){
78 return "invalid";
79 }
80 };
81
82 class ValidState : public State{
83 public:
84 ValidState(Cacheable * const _owner) :
85 State(_owner)
86 {}
87
88 virtual T& getValue(){
89 return content;
90 }
91
92 virtual void invalidate(){
93 State::owner->switchState(State::owner->invalidState);
94 }
95
96 virtual bool isValid(){
97 return true;
98 }
99
100 virtual void enter(){
101 State::busy= true;
102 // as soon as we enter the valid state we recalculate
103 content = State::owner->recalcMethod();
104 State::busy = false;
105 }
106
107 virtual std::string getName(){
108 return "valid";
109 }
110 private:
111 T content;
112 };
113
114 class DestroyedState : public State {
115 public:
116 DestroyedState(Cacheable * const _owner) :
117 State(_owner)
118 {}
119
120 virtual T& getValue(){
121 ASSERT(0,"Cannot get a value from a Cacheable after it's Observable has died");
122 // we have to return a grossly invalid reference, because no value can be produced anymore
123 return *(static_cast<T*>(0));
124 }
125
126 virtual void invalidate(){
127 ASSERT(0,"Cannot invalidate a Cacheable after it's Observable has died");
128 }
129
130 virtual bool isValid(){
131 ASSERT(0,"Cannot check validity of a Cacheable after it's Observable has died");
132 return false;
133 }
134
135 virtual void enter(){
136 // nothing to do when entering this state
137 }
138
139 virtual std::string getName(){
140 return "destroyed";
141 }
142 };
143
144
145 typedef boost::shared_ptr<State> state_ptr;
146
147 public:
148 Cacheable(
149 const Observable * const _owner,
150 const boost::function<T()> &_recalcMethod,
151 const std::string &name,
152 const Observable::channels_t &_channels = Observable::channels_t());
153 virtual ~Cacheable();
154
155 const bool isValid() const;
156 const T operator*() const;
157
158 // methods implemented for base-class Observer
159 void update(Observable *subject);
160 void recieveNotification(Observable *publisher, Notification_ptr notification);
161 void subjectKilled(Observable *subject);
162 private:
163 void switchState(state_ptr newState);
164
165 mutable state_ptr state;
166 // pre-defined state so we don't have to construct to much
167 state_ptr invalidState;
168 state_ptr validState;
169 // destroyed state is not predefined, because we rarely enter that state and never leave
170
171 //!> mutex to ensure access is only per-thread
172 mutable boost::recursive_mutex ownerLock;
173
174 const Observable * const owner;
175 const boost::function<T()> recalcMethod;
176
177 //!> contains list of possible channels to enlist, if empty we signOn globally
178 const Observable::channels_t channels;
179
180 // de-activated copy constructor
181 Cacheable(const Cacheable&);
182 };
183
184
185 template<typename T>
186 Cacheable<T>::Cacheable(
187 const Observable * const _owner,
188 const boost::function<T()> &_recalcMethod,
189 const std::string &_name,
190 const Observable::channels_t &_channels) :
191 Observer(_name + "(Cached)"),
192 owner(_owner),
193 recalcMethod(_recalcMethod),
194 channels(_channels)
195 {
196 // create all states needed for this object
197 invalidState = state_ptr(new InvalidState(this));
198 validState = state_ptr(new ValidState(this));
199 state = invalidState;
200 // we sign on with the best(=lowest) priority, so cached values are recalculated before
201 // anybody else might ask for updated values
202 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
203 if (owner != NULL) {
204 if (channels.empty()) {
205 owner->signOn(this,GlobalObservableInfo::PriorityLevel(int(-20)));
206 } else {
207 for (Observable::channels_t::const_iterator iter = channels.begin();
208 iter != channels.end(); ++iter)
209 owner->signOn(this,*iter,GlobalObservableInfo::PriorityLevel(int(-20)));
210 }
211 }
212 }
213
214 // de-activated copy constructor
215 template<typename T>
216 Cacheable<T>::Cacheable(const Cacheable&){
217 ASSERT(0,"Cacheables should never be copied");
218 }
219
220 template<typename T>
221 const T Cacheable<T>::operator*() const{
222 // we can only use the cacheable when the owner is not changing at the moment
223 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
224 if ((owner == NULL) || (!owner->isBlocked())) {
225 return state->getValue();
226 }
227 else{
228 return recalcMethod();
229 }
230 }
231
232 template<typename T>
233 Cacheable<T>::~Cacheable()
234 {
235 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
236 if (owner != NULL) {
237 if (channels.empty()) {
238 owner->signOff(this);
239 } else {
240 for (Observable::channels_t::const_iterator iter = channels.begin();
241 iter != channels.end(); ++iter)
242 owner->signOff(this,*iter);
243 }
244 const_cast<const Observable *&>(owner) = NULL;
245 }
246 }
247
248 template<typename T>
249 const bool Cacheable<T>::isValid() const{
250 return state->isValid();
251 }
252
253 template<typename T>
254 void Cacheable<T>::update(Observable *subject) {
255 ASSERT( channels.empty(),
256 "Cacheable<T>::update() - we are listening only to global updates.");
257 state->invalidate();
258 }
259
260 template<typename T>
261 void Cacheable<T>::recieveNotification(Observable *publisher, Notification_ptr notification) {
262 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
263 if (publisher == owner) {
264 ASSERT( !channels.empty(),
265 "Cacheable<T>::update() - we are not listening to global updates.");
266 const Observable::channels_t::const_iterator iter = std::find(
267 channels.begin(), channels.end(), notification->getChannelNo());
268 if (iter != channels.end())
269 state->invalidate();
270 }
271 }
272
273 template<typename T>
274 void Cacheable<T>::subjectKilled(Observable *subject) {
275 state_ptr destroyed = state_ptr(new DestroyedState(this));
276 switchState(destroyed);
277 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
278 const_cast<const Observable *&>(owner) = NULL;
279 }
280
281 template<typename T>
282 void Cacheable<T>::switchState(state_ptr newState){
283 ASSERT(!state->isBusy(),"LOOP DETECTED: Cacheable state switched while recalculating.\nDid the recalculation trigger the Observable?");
284#ifdef LOG_OBSERVER
285 observerLog().addMessage() << "## Cacheable " << observerLog().getName(this) << " changed state (" << state->getName()
286 << "->" << newState->getName() << ")";
287#endif
288 state = newState;
289 state->enter();
290 }
291
292#else
293 template<typename T>
294 class Cacheable : public Observer
295 {
296 public:
297 Cacheable(
298 const Observable * const _owner,
299 boost::function<T()> &_recalcMethod,
300 std::string name,
301 const Observable::channels_t &_channels);
302 virtual ~Cacheable();
303
304 const bool isValid() const;
305 const T operator*() const;
306
307 // methods implemented for base-class Observer
308 void update(Observable *subject);
309 void recieveNotification(Observable *publisher, Notification_ptr notification);
310 void subjectKilled(Observable *subject);
311 private:
312
313 boost::function<T()> recalcMethod;
314 };
315
316 template<typename T>
317 Cacheable<T>::Cacheable(
318 const Observable * const _owner,
319 boost::function<T()> &_recalcMethod,
320 std::string name,
321 const Observable::channels_t &_channels) :
322 Observer(name),
323 recalcMethod(_recalcMethod)
324 {}
325
326 template<typename T>
327 const T Cacheable<T>::operator*() const{
328 return recalcMethod();
329 }
330
331 template<typename T>
332 Cacheable<T>::~Cacheable()
333 {}
334
335 template<typename T>
336 const bool Cacheable<T>::isValid() const{
337 return true;
338 }
339
340 template<typename T>
341 void Cacheable<T>::update(Observable *subject) {
342 ASSERT(0, "Cacheable::update should never be called when caching is disabled");
343 }
344
345 template<typename T>
346 void Cacheable<T>::recieveNotification(Observable *publisher, Notification_ptr notification) {
347 ASSERT(0, "Cacheable::recieveNotification should never be called when caching is disabled");
348 }
349
350 template<typename T>
351 void Cacheable<T>::subjectKilled(Observable *subject){
352 ASSERT(0, "Cacheable::subjectKilled should never be called when caching is disabled");
353 }
354#endif
355
356#endif /* CACHEABLE_HPP_ */
Note: See TracBrowser for help on using the repository browser.