source: src/CodePatterns/Cacheable.hpp@ 6e2f3b

Last change on this file since 6e2f3b was 959c82, checked in by Frederik Heber <heber@…>, 10 years ago

Extracted all static Observable maps (and mutex) into singleton GlobalObservableInfo.

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