1 /***************************************************************************
2     begin       : Mon Jul 14 2008
3     copyright   : (C) 2008 by Martin Preuss
4     email       : martin@libchipcard.de
5 
6  ***************************************************************************
7  *                                                                         *
8  *   This library is free software; you can redistribute it and/or         *
9  *   modify it under the terms of the GNU Lesser General Public            *
10  *   License as published by the Free Software Foundation; either          *
11  *   version 2.1 of the License, or (at your option) any later version.    *
12  *                                                                         *
13  *   This library is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
16  *   Lesser General Public License for more details.                       *
17  *                                                                         *
18  *   You should have received a copy of the GNU Lesser General Public      *
19  *   License along with this library; if not, write to the Free Software   *
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
21  *   MA  02111-1307  USA                                                   *
22  *                                                                         *
23  ***************************************************************************/
24 
25 
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29 
30 #include "memcache_p.h"
31 #include <gwenhywfar/misc.h>
32 #include <gwenhywfar/debug.h>
33 
34 
35 
GWEN_IDMAP_FUNCTIONS(GWEN_MEMCACHE_ENTRY,GWEN_MemCacheEntry)36 GWEN_IDMAP_FUNCTIONS(GWEN_MEMCACHE_ENTRY, GWEN_MemCacheEntry)
37 
38 
39 
40 GWEN_MEMCACHE_ENTRY *GWEN_MemCacheEntry_new(GWEN_MEMCACHE *memCache,
41                                             uint32_t id,
42                                             void *dataPtr,
43                                             size_t dataLen)
44 {
45   GWEN_MEMCACHE_ENTRY *me;
46 
47   GWEN_NEW_OBJECT(GWEN_MEMCACHE_ENTRY, me);
48 
49   me->memCache=memCache;
50   me->id=id;
51   me->dataPtr=dataPtr;
52   me->dataLen=dataLen;
53   me->isValid=1;
54 
55   /* update memcache */
56   me->memCache->currentCacheEntries++;
57   me->memCache->currentCacheMemory+=me->dataLen;
58 
59   return me;
60 }
61 
62 
63 
GWEN_MemCacheEntry_free(GWEN_MEMCACHE_ENTRY * me)64 void GWEN_MemCacheEntry_free(GWEN_MEMCACHE_ENTRY *me)
65 {
66   if (me) {
67     assert(me->useCounter==0);
68     assert(me->memCache);
69 
70     /* update memcache */
71     me->memCache->currentCacheEntries--;
72     me->memCache->currentCacheMemory-=me->dataLen;
73 
74     if (me->dataPtr && me->dataLen)
75       free(me->dataPtr);
76 
77     GWEN_FREE_OBJECT(me);
78   }
79 }
80 
81 
82 
GWEN_MemCacheEntry_GetUseCounter(const GWEN_MEMCACHE_ENTRY * me)83 int GWEN_MemCacheEntry_GetUseCounter(const GWEN_MEMCACHE_ENTRY *me)
84 {
85   assert(me);
86   return me->useCounter;
87 }
88 
89 
90 
GWEN_MemCacheEntry_GetUnusedSince(GWEN_MEMCACHE_ENTRY * me)91 time_t GWEN_MemCacheEntry_GetUnusedSince(GWEN_MEMCACHE_ENTRY *me)
92 {
93   assert(me);
94   return me->unusedSince;
95 }
96 
97 
98 
GWEN_MemCacheEntry_GetId(GWEN_MEMCACHE_ENTRY * me)99 uint32_t GWEN_MemCacheEntry_GetId(GWEN_MEMCACHE_ENTRY *me)
100 {
101   assert(me);
102   return me->id;
103 }
104 
105 
106 
GWEN_MemCacheEntry_GetDataPtr(GWEN_MEMCACHE_ENTRY * me)107 void *GWEN_MemCacheEntry_GetDataPtr(GWEN_MEMCACHE_ENTRY *me)
108 {
109   assert(me);
110   return me->dataPtr;
111 }
112 
113 
114 
GWEN_MemCacheEntry_GetDataLen(GWEN_MEMCACHE_ENTRY * me)115 size_t GWEN_MemCacheEntry_GetDataLen(GWEN_MEMCACHE_ENTRY *me)
116 {
117   assert(me);
118   return me->dataLen;
119 }
120 
121 
122 
GWEN_MemCacheEntry_BeginUse(GWEN_MEMCACHE_ENTRY * me)123 void GWEN_MemCacheEntry_BeginUse(GWEN_MEMCACHE_ENTRY *me)
124 {
125   int rv;
126 
127   assert(me);
128   rv=GWEN_MemCache_Lock(me->memCache);
129   if (rv) {
130     DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
131     assert(0);
132   }
133   me->useCounter++;
134   GWEN_MemCache_Unlock(me->memCache);
135 }
136 
137 
138 
GWEN_MemCacheEntry_EndUse(GWEN_MEMCACHE_ENTRY * me)139 void GWEN_MemCacheEntry_EndUse(GWEN_MEMCACHE_ENTRY *me)
140 {
141   int rv;
142 
143   assert(me);
144   rv=GWEN_MemCache_Lock(me->memCache);
145   if (rv) {
146     DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
147     assert(0);
148   }
149   if (me->useCounter>0) {
150     me->useCounter--;
151     if (me->useCounter==0) {
152       if (!(me->isValid)) {
153         GWEN_MemCacheEntry_free(me);
154       }
155       else
156         me->unusedSince=time(0);
157     }
158   }
159   else {
160     DBG_ERROR(GWEN_LOGDOMAIN, "Use counter < 1, aborting");
161     GWEN_MemCache_Unlock(me->memCache);
162     assert(me->useCounter>0);
163   }
164   GWEN_MemCache_Unlock(me->memCache);
165 }
166 
167 
168 
169 
170 
171 
172 
GWEN_MemCache_new(size_t maxCacheMemory,uint32_t maxCacheEntries)173 GWEN_MEMCACHE *GWEN_MemCache_new(size_t maxCacheMemory,
174                                  uint32_t maxCacheEntries)
175 {
176   GWEN_MEMCACHE *mc;
177 
178   GWEN_NEW_OBJECT(GWEN_MEMCACHE, mc);
179   mc->mutex=GWEN_Mutex_new();
180   mc->idMap=GWEN_MemCacheEntry_IdMap_new(GWEN_IdMapAlgo_Hex4);
181   mc->maxCacheMemory=maxCacheMemory;
182   mc->maxCacheEntries=maxCacheEntries;
183 
184   return mc;
185 }
186 
187 
188 
GWEN_MemCache_free(GWEN_MEMCACHE * mc)189 void GWEN_MemCache_free(GWEN_MEMCACHE *mc)
190 {
191   if (mc) {
192     GWEN_MemCacheEntry_IdMap_free(mc->idMap);
193     GWEN_Mutex_free(mc->mutex);
194     GWEN_FREE_OBJECT(mc);
195   }
196 }
197 
198 
199 
GWEN_MemCache_FindEntry(GWEN_MEMCACHE * mc,uint32_t id)200 GWEN_MEMCACHE_ENTRY *GWEN_MemCache_FindEntry(GWEN_MEMCACHE *mc,
201                                              uint32_t id)
202 {
203   GWEN_MEMCACHE_ENTRY *me;
204 
205   assert(mc);
206   GWEN_MemCache_Lock(mc);
207   me=GWEN_MemCacheEntry_IdMap_Find(mc->idMap, id);
208   if (me) {
209     /* we can't call GWEN_MemCache_BeginUse() here because of the mutex */
210     me->useCounter++;
211   }
212   GWEN_MemCache_Unlock(mc);
213 
214   return me;
215 }
216 
217 
218 
GWEN_MemCache_PurgeEntry(GWEN_MEMCACHE * mc,uint32_t id)219 void GWEN_MemCache_PurgeEntry(GWEN_MEMCACHE *mc,
220                               uint32_t id)
221 {
222   GWEN_MEMCACHE_ENTRY *me;
223 
224   assert(mc);
225   GWEN_MemCache_Lock(mc);
226   me=GWEN_MemCacheEntry_IdMap_Find(mc->idMap, id);
227   if (me) {
228     me->isValid=0;
229     GWEN_MemCacheEntry_IdMap_Remove(mc->idMap, id);
230     if (me->useCounter==0)
231       GWEN_MemCacheEntry_free(me);
232   }
233   GWEN_MemCache_Unlock(mc);
234 }
235 
236 
237 
GWEN_MemCache__MakeRoom(GWEN_MEMCACHE * mc,size_t neededSize)238 int GWEN_MemCache__MakeRoom(GWEN_MEMCACHE *mc,
239                             size_t neededSize)
240 {
241   assert(mc);
242 
243   /* release unused entries until there is enough memory */
244   while (neededSize) {
245     GWEN_MEMCACHE_ENTRY *oldestEntry;
246     GWEN_IDMAP_RESULT res;
247     uint32_t currentId;
248 
249     /* get oldest entry */
250     oldestEntry=NULL;
251     res=GWEN_MemCacheEntry_IdMap_GetFirst(mc->idMap, &currentId);
252     while (res==GWEN_IdMapResult_Ok) {
253       GWEN_MEMCACHE_ENTRY *me;
254 
255       me=GWEN_MemCacheEntry_IdMap_Find(mc->idMap, currentId);
256       if (me) {
257         if (me->isValid && me->useCounter==0) {
258           if (oldestEntry==NULL)
259             oldestEntry=me;
260           else {
261             if (me->unusedSince<oldestEntry->unusedSince)
262               oldestEntry=me;
263           }
264         }
265       }
266       res=GWEN_MemCacheEntry_IdMap_GetNext(mc->idMap, &currentId);
267     }
268 
269     if (oldestEntry==NULL)
270       /* no unused entry found */
271       break;
272 
273     /* subtract size of to-be-removed entry from needed size */
274     if (neededSize<oldestEntry->dataLen)
275       neededSize=0;
276     else
277       neededSize-=oldestEntry->dataLen;
278 
279     /* remove oldest entry (it is unused, so we also delete it here) */
280     GWEN_MemCacheEntry_IdMap_Remove(mc->idMap, oldestEntry->id);
281     GWEN_MemCacheEntry_free(oldestEntry);
282   }
283 
284   return (neededSize==0)?0:GWEN_ERROR_MEMORY_FULL;
285 }
286 
287 
288 
GWEN_MemCache_CreateEntry(GWEN_MEMCACHE * mc,uint32_t id,void * dataPtr,size_t dataLen)289 GWEN_MEMCACHE_ENTRY *GWEN_MemCache_CreateEntry(GWEN_MEMCACHE *mc,
290                                                uint32_t id,
291                                                void *dataPtr,
292                                                size_t dataLen)
293 {
294   GWEN_MEMCACHE_ENTRY *me;
295 
296   assert(mc);
297   GWEN_MemCache_Lock(mc);
298 
299   /* invalidate possibly existing entry in any case */
300   me=GWEN_MemCacheEntry_IdMap_Find(mc->idMap, id);
301   if (me) {
302     me->isValid=0;
303     GWEN_MemCacheEntry_IdMap_Remove(mc->idMap, id);
304     if (me->useCounter==0)
305       GWEN_MemCacheEntry_free(me);
306   }
307 
308   /* check for limits: entry count */
309   if (mc->currentCacheEntries>=mc->maxCacheEntries) {
310     int rv;
311 
312     /* release unused entries (at least 1 byte) */
313     rv=GWEN_MemCache__MakeRoom(mc, 1);
314     if (rv) {
315       DBG_WARN(GWEN_LOGDOMAIN, "Too many entries in use");
316       GWEN_MemCache_Unlock(mc);
317       return NULL;
318     }
319   }
320 
321   /* check for limits: memory in use */
322   if ((mc->currentCacheMemory+dataLen)>=mc->maxCacheMemory) {
323     size_t diff;
324     int rv;
325 
326     diff=(mc->currentCacheMemory+dataLen)-mc->maxCacheMemory;
327     /* release unused entries */
328     rv=GWEN_MemCache__MakeRoom(mc, diff);
329     if (rv) {
330       DBG_WARN(GWEN_LOGDOMAIN, "Too much memory in use");
331       GWEN_MemCache_Unlock(mc);
332       return NULL;
333     }
334   }
335 
336   /* create new entry */
337   me=GWEN_MemCacheEntry_new(mc, id, dataPtr, dataLen);
338   assert(me);
339   me->useCounter++;
340   GWEN_MemCacheEntry_IdMap_Insert(mc->idMap, id, me);
341 
342   GWEN_MemCache_Unlock(mc);
343 
344   return me;
345 }
346 
347 
348 
GWEN_MemCache_PurgeEntries(GWEN_MEMCACHE * mc,uint32_t id,uint32_t mask)349 void GWEN_MemCache_PurgeEntries(GWEN_MEMCACHE *mc,
350                                 uint32_t id, uint32_t mask)
351 {
352   GWEN_IDMAP_RESULT res;
353   uint32_t currentId;
354 
355   assert(mc);
356   GWEN_MemCache_Lock(mc);
357 
358   res=GWEN_MemCacheEntry_IdMap_GetFirst(mc->idMap, &currentId);
359   while (res==GWEN_IdMapResult_Ok) {
360     uint32_t nextId;
361 
362     nextId=currentId;
363     res=GWEN_MemCacheEntry_IdMap_GetNext(mc->idMap, &nextId);
364     if ((currentId & mask)==id) {
365       GWEN_MEMCACHE_ENTRY *me;
366 
367       me=GWEN_MemCacheEntry_IdMap_Find(mc->idMap, currentId);
368       if (me) {
369         me->isValid=0;
370         GWEN_MemCacheEntry_IdMap_Remove(mc->idMap, currentId);
371         if (me->useCounter==0)
372           GWEN_MemCacheEntry_free(me);
373       }
374 
375     }
376     currentId=nextId;
377   }
378 
379   GWEN_MemCache_Unlock(mc);
380 }
381 
382 
383 
GWEN_MemCache_Purge(GWEN_MEMCACHE * mc)384 void GWEN_MemCache_Purge(GWEN_MEMCACHE *mc)
385 {
386   assert(mc);
387   GWEN_MemCache_PurgeEntries(mc, 0, 0);
388 }
389 
390 
391 
GWEN_MemCache_Lock(GWEN_MEMCACHE * mc)392 int GWEN_MemCache_Lock(GWEN_MEMCACHE *mc)
393 {
394   assert(mc);
395   return GWEN_Mutex_Lock(mc->mutex);
396 }
397 
398 
399 
GWEN_MemCache_Unlock(GWEN_MEMCACHE * mc)400 int GWEN_MemCache_Unlock(GWEN_MEMCACHE *mc)
401 {
402   assert(mc);
403   return GWEN_Mutex_Unlock(mc->mutex);
404 }
405 
406 
407 
408 
409 
410 
411 
412 
413