source: src/CodePatterns/Cacheable.hpp@ b09709

Last change on this file since b09709 was 3681dd, checked in by Frederik Heber <heber@…>, 10 years ago

FIX: Equipped Cacheable with mutex locking read&write to owner.

  • Property mode set to 100644
File size: 9.7 KB
RevLine 
[a80f419]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
[70672e3]11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
[e2e035e]16#include "Observer/Observable.hpp"
17#include "Observer/Observer.hpp"
[a80f419]18#include <boost/function.hpp>
19#include <boost/shared_ptr.hpp>
[3681dd]20#include <boost/thread/locks.hpp>
21#include <boost/thread/recursive_mutex.hpp>
[a80f419]22
[9b8fa4]23#include "CodePatterns/Assert.hpp"
[a80f419]24
[454bc54]25#include "CodePatterns/Observer/Notification.hpp"
26
[a80f419]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:
[e24dde]35 State(Cacheable * const _owner) :
[a80f419]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;
[e24dde]49 Cacheable * const owner;
[a80f419]50 };
51
52 class InvalidState : public State{
53 public:
[e24dde]54 InvalidState(Cacheable * const _owner):
[a80f419]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:
[e24dde]84 ValidState(Cacheable * const _owner) :
[a80f419]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:
[e24dde]116 DestroyedState(Cacheable * const _owner) :
[a80f419]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:
[454bc54]148 Cacheable(
[e24dde]149 const Observable * const _owner,
150 const boost::function<T()> &_recalcMethod,
[454bc54]151 const std::string &name,
152 const Observable::channels_t &_channels = Observable::channels_t());
[a80f419]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);
[454bc54]160 void recieveNotification(Observable *publisher, Notification_ptr notification);
[a80f419]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
[3681dd]171 //!> mutex to ensure access is only per-thread
172 mutable boost::recursive_mutex ownerLock;
173
[e24dde]174 const Observable * const owner;
[454bc54]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;
[a80f419]179
180 // de-activated copy constructor
181 Cacheable(const Cacheable&);
182 };
183
184
185 template<typename T>
[454bc54]186 Cacheable<T>::Cacheable(
[e24dde]187 const Observable * const _owner,
188 const boost::function<T()> &_recalcMethod,
[454bc54]189 const std::string &_name,
190 const Observable::channels_t &_channels) :
191 Observer(_name + "(Cached)"),
[a80f419]192 owner(_owner),
[454bc54]193 recalcMethod(_recalcMethod),
194 channels(_channels)
[a80f419]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
[3681dd]202 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
[454bc54]203 if (owner != NULL) {
204 if (channels.empty()) {
[959c82]205 owner->signOn(this,GlobalObservableInfo::PriorityLevel(int(-20)));
[454bc54]206 } else {
207 for (Observable::channels_t::const_iterator iter = channels.begin();
208 iter != channels.end(); ++iter)
[959c82]209 owner->signOn(this,*iter,GlobalObservableInfo::PriorityLevel(int(-20)));
[454bc54]210 }
211 }
[a80f419]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
[3681dd]223 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
[37d941]224 if ((owner == NULL) || (!owner->isBlocked())) {
[a80f419]225 return state->getValue();
226 }
227 else{
228 return recalcMethod();
229 }
230 }
231
232 template<typename T>
233 Cacheable<T>::~Cacheable()
234 {
[3681dd]235 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
[454bc54]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 }
[e24dde]244 const_cast<const Observable *&>(owner) = NULL;
[454bc54]245 }
[a80f419]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) {
[454bc54]255 ASSERT( channels.empty(),
256 "Cacheable<T>::update() - we are listening only to global updates.");
[a80f419]257 state->invalidate();
258 }
259
[454bc54]260 template<typename T>
261 void Cacheable<T>::recieveNotification(Observable *publisher, Notification_ptr notification) {
[3681dd]262 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
[454bc54]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
[a80f419]273 template<typename T>
274 void Cacheable<T>::subjectKilled(Observable *subject) {
275 state_ptr destroyed = state_ptr(new DestroyedState(this));
276 switchState(destroyed);
[3681dd]277 boost::lock_guard<boost::recursive_mutex> guard(ownerLock);
278 const_cast<const Observable *&>(owner) = NULL;
[a80f419]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()
[2c11c1]286 << "->" << newState->getName() << ")";
[a80f419]287#endif
288 state = newState;
289 state->enter();
290 }
291
292#else
293 template<typename T>
294 class Cacheable : public Observer
295 {
296 public:
[e24dde]297 Cacheable(
298 const Observable * const _owner,
299 boost::function<T()> &_recalcMethod,
300 std::string name,
301 const Observable::channels_t &_channels);
[a80f419]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);
[454bc54]309 void recieveNotification(Observable *publisher, Notification_ptr notification);
[a80f419]310 void subjectKilled(Observable *subject);
311 private:
312
313 boost::function<T()> recalcMethod;
314 };
315
316 template<typename T>
[e24dde]317 Cacheable<T>::Cacheable(
318 const Observable * const _owner,
319 boost::function<T()> &_recalcMethod,
320 std::string name,
321 const Observable::channels_t &_channels) :
[a80f419]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
[454bc54]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
[a80f419]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.