Patch [#33196] Warning Fixes 11-16-2012
[blender.git] / intern / memutil / MEM_CacheLimiter.h
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Peter Schlaile <peter@schlaile.de> 2005
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file memutil/MEM_CacheLimiter.h
24  *  \ingroup memutil
25  */
26
27
28 #ifndef __MEM_CACHELIMITER_H__
29 #define __MEM_CACHELIMITER_H__
30
31 /**
32  * @section MEM_CacheLimiter
33  * This class defines a generic memory cache management system
34  * to limit memory usage to a fixed global maximum.
35  *
36  * Please use the C-API in MEM_CacheLimiterC-Api.h for code written in C.
37  *
38  * Usage example:
39  *
40  * class BigFatImage {
41  * public:
42  *       ~BigFatImage() { tell_everyone_we_are_gone(this); }
43  * };
44  *
45  * void doit() {
46  *     MEM_Cache<BigFatImage> BigFatImages;
47  *
48  *     MEM_Cache_Handle<BigFatImage>* h = BigFatImages.insert(new BigFatImage);
49  *
50  *     BigFatImages.enforce_limits();
51  *     h->ref();
52  *
53  *     work with image...
54  *
55  *     h->unref();
56  *
57  *     leave image in cache.
58  */
59
60 #include <list>
61 #include <queue>
62 #include <vector>
63 #include "MEM_Allocator.h"
64
65 template<class T>
66 class MEM_CacheLimiter;
67
68 #ifndef __MEM_CACHELIMITERC_API_H__
69 extern "C" {
70         void MEM_CacheLimiter_set_maximum(size_t m);
71         size_t MEM_CacheLimiter_get_maximum();
72 };
73 #endif
74
75 template<class T>
76 class MEM_CacheLimiterHandle {
77 public:
78         explicit MEM_CacheLimiterHandle(T * data_,MEM_CacheLimiter<T> *parent_) :
79                 data(data_),
80                 refcount(0),
81                 parent(parent_)
82         { }
83
84         void ref() {
85                 refcount++;
86         }
87
88         void unref() {
89                 refcount--;
90         }
91
92         T *get() {
93                 return data;
94         }
95
96         const T *get() const {
97                 return data;
98         }
99
100         int get_refcount() const {
101                 return refcount;
102         }
103
104         bool can_destroy() const {
105                 return !data || !refcount;
106         }
107
108         bool destroy_if_possible() {
109                 if (can_destroy()) {
110                         delete data;
111                         data = 0;
112                         unmanage();
113                         return true;
114                 }
115                 return false;
116         }
117
118         void unmanage() {
119                 parent->unmanage(this);
120         }
121
122         void touch() {
123                 parent->touch(this);
124         }
125
126 private:
127         friend class MEM_CacheLimiter<T>;
128
129         T * data;
130         int refcount;
131         typename std::list<MEM_CacheLimiterHandle<T> *, MEM_Allocator<MEM_CacheLimiterHandle<T> *> >::iterator me;
132         MEM_CacheLimiter<T> * parent;
133 };
134
135 template<class T>
136 class MEM_CacheLimiter {
137 public:
138         typedef size_t (*MEM_CacheLimiter_DataSize_Func) (void *data);
139         typedef int    (*MEM_CacheLimiter_ItemPriority_Func) (void *item, int default_priority);
140
141         MEM_CacheLimiter(MEM_CacheLimiter_DataSize_Func getDataSize_)
142                 : getDataSize(getDataSize_) {
143         }
144
145         ~MEM_CacheLimiter() {
146                 for (iterator it = queue.begin(); it != queue.end(); it++) {
147                         delete *it;
148                 }
149         }
150
151         MEM_CacheLimiterHandle<T> *insert(T * elem) {
152                 queue.push_back(new MEM_CacheLimiterHandle<T>(elem, this));
153                 iterator it = queue.end();
154                 --it;
155                 queue.back()->me = it;
156                 return queue.back();
157         }
158
159         void unmanage(MEM_CacheLimiterHandle<T> *handle) {
160                 queue.erase(handle->me);
161                 delete handle;
162         }
163
164         void enforce_limits() {
165                 size_t max = MEM_CacheLimiter_get_maximum();
166                 size_t mem_in_use, cur_size;
167
168                 if (max == 0) {
169                         return;
170                 }
171
172                 if (getDataSize) {
173                         mem_in_use = total_size();
174                 }
175                 else {
176                         mem_in_use = MEM_get_memory_in_use();
177                 }
178
179                 if (mem_in_use <= max) {
180                         return;
181                 }
182
183                 while (!queue.empty() && mem_in_use > max) {
184                         MEM_CacheElementPtr elem = get_least_priority_destroyable_element();
185
186                         if (!elem)
187                                 break;
188
189                         if (getDataSize) {
190                                 cur_size = getDataSize(elem->get()->get_data());
191                         }
192                         else {
193                                 cur_size = mem_in_use;
194                         }
195
196                         if (elem->destroy_if_possible()) {
197                                 if (getDataSize) {
198                                         mem_in_use -= cur_size;
199                                 }
200                                 else {
201                                         mem_in_use -= cur_size - MEM_get_memory_in_use();
202                                 }
203                         }
204                 }
205         }
206
207         void touch(MEM_CacheLimiterHandle<T> * handle) {
208                 queue.push_back(handle);
209                 queue.erase(handle->me);
210                 iterator it = queue.end();
211                 --it;
212                 handle->me = it;
213         }
214
215         void set_item_priority_func(MEM_CacheLimiter_ItemPriority_Func item_priority_func) {
216                 getItemPriority = item_priority_func;
217         }
218
219 private:
220         typedef MEM_CacheLimiterHandle<T> *MEM_CacheElementPtr;
221         typedef std::list<MEM_CacheElementPtr, MEM_Allocator<MEM_CacheElementPtr> > MEM_CacheQueue;
222         typedef typename MEM_CacheQueue::iterator iterator;
223
224         size_t total_size() {
225                 size_t size = 0;
226                 for (iterator it = queue.begin(); it != queue.end(); it++) {
227                         size+= getDataSize((*it)->get()->get_data());
228                 }
229                 return size;
230         }
231
232         MEM_CacheElementPtr get_least_priority_destroyable_element(void) {
233                 if (queue.empty())
234                         return NULL;
235
236                 if (!getItemPriority)
237                         return *queue.begin();
238
239                 MEM_CacheElementPtr best_match_elem = NULL;
240                 int best_match_priority = 0;
241                 iterator it;
242                 int i;
243
244                 for (it = queue.begin(), i = 0; it != queue.end(); it++, i++) {
245                         MEM_CacheElementPtr elem = *it;
246
247                         if (!elem->can_destroy())
248                                 continue;
249
250                         /* by default 0 means highest priority element */
251                         /* casting a size type to int is questionable,
252                            but unlikely to cause problems */
253                         int priority = -((int)(queue.size()) - i - 1);
254                         priority = getItemPriority(elem->get()->get_data(), priority);
255
256                         if (priority < best_match_priority || best_match_elem == NULL) {
257                                 best_match_priority = priority;
258                                 best_match_elem = elem;
259                         }
260                 }
261
262                 return best_match_elem;
263         }
264
265         MEM_CacheQueue queue;
266         MEM_CacheLimiter_DataSize_Func getDataSize;
267         MEM_CacheLimiter_ItemPriority_Func getItemPriority;
268 };
269
270 #endif // __MEM_CACHELIMITER_H__