1 #include <stdlib.h>
  2 #include <stdio.h>
  3 #include "memcache.h"
  4 #include "rh_string.h"
  5 
  6 #ifndef MEMCACHE_TIMEOUT
  7 # define MEMCACHE_TIMEOUT               5
  8 #endif
  9         
 10 #ifndef MEMCACHE_TIMEOUT_INTERVAL
 11 # define MEMCACHE_TIMEOUT_INTERVAL      2
 12 #endif
 13 
 14 #ifndef MEMCACHE_RESET_INTERVAL
 15 # define MEMCACHE_RESET_INTERVAL        60
 16 #endif
 17 
 18 static int memcache_resize (memcache_t *cache, int grow);
 19 
 20 
 21 void memcache_init    (memcache_t *cache, time_t *current_time)
 22 {
 23         TYPE_ZERO(cache);
 24 
 25         cache->time = current_time;
 26         TAILQ_INIT(&cache->timeout);
 27 
 28         cache->limit = 1024 * 1024 * 100; /* 100 mb */
 29 }
 30 
 31 void memcache_destroy (memcache_t *cache)
 32 {
 33         size_t  i;
 34                 
 35         if (NULL == cache->buckets)
 36                 return;
 37 
 38         for (i=0; i<cache->nbuckets; ++i) {
 39                 struct memcache_entry_list      *list;
 40                 memcache_entry_t                *entry;
 41 
 42                 list = &cache->buckets[i];
 43 
 44                 while ( (entry = TAILQ_FIRST(list)) ) {
 45                         TAILQ_REMOVE(list, entry, buckets);
 46                         TAILQ_REMOVE(&cache->timeout, entry, timeout);
 47                         free (entry);
 48                 }
 49         }
 50         
 51         free (cache->buckets);
 52         
 53         TYPE_ZERO(cache);
 54 }
 55 
 56 void * memcache_pop (memcache_t *cache, size_t size)
 57 {
 58         struct memcache_entry_list      *list;
 59         memcache_entry_t                *entry;
 60         
 61         if (0 == cache->nelements || size < sizeof(*entry))
 62                 goto out_malloc;
 63         
 64         list = &cache->buckets[size % cache->nbuckets];
 65 
 66         TAILQ_FOREACH(entry, list, buckets) {
 67                 if (entry->size != size)
 68                         continue;
 69         
 70                 TAILQ_REMOVE(list, entry, buckets);
 71                 TAILQ_REMOVE(&cache->timeout, entry, timeout);
 72         
 73                 --cache->nelements;
 74                 cache->usage -= size;
 75         
 76                 return (void *) entry;
 77         }
 78 
 79 out_malloc:
 80 #if 0
 81         printf ("%s(): malloc size(%zu) usage(%zu/%zu)\n",
 82                         __FUNCTION__,
 83                         size, cache->usage, cache->limit );
 84 #endif
 85         return malloc (size);
 86 }
 87 
 88 void memcache_push (memcache_t *cache, void *ptr, size_t size)
 89 {
 90         time_t  atime;
 91 #if 0
 92         printf ("%s(%p, %p): usage(%zu/%zu)\n",
 93                         __FUNCTION__,
 94                         (void*)cache, ptr,
 95                         cache->usage, cache->limit );
 96 #endif
 97 
 98         if (size < sizeof(memcache_entry_t))
 99                 goto out_free;
100         
101         if (0 == cache->nbuckets || cache->nelements / cache->nbuckets > 5) {
102                 if (0 != memcache_resize (cache, 1))
103                         goto out_free;
104         }
105 
106         atime = *(cache->time);
107 
108         if (cache->usage + size > cache->limit) {
109                 free (ptr);
110         } else {
111                 memcache_entry_t                *entry;
112                 struct memcache_entry_list      *list;
113         
114                 list = &cache->buckets[size % cache->nbuckets];
115                 
116                 entry = ptr;
117                 entry->size = size;
118                 entry->atime = atime;
119                 cache->usage += size;
120 
121                 ++cache->nelements;
122 
123                 TAILQ_INSERT_TAIL(list, entry, buckets);
124                 TAILQ_INSERT_TAIL(&cache->timeout, entry, timeout);
125         }
126 
127         /* timeout test */
128         if (atime - cache->last_timeout_test > MEMCACHE_TIMEOUT_INTERVAL) {
129                 memcache_entry_t                *entry;
130                 struct memcache_entry_list      *list;
131 
132                 for (;;) {
133                         entry = TAILQ_FIRST(&cache->timeout);
134                         if (NULL == entry)
135                                 break;
136                         
137                         if (atime - entry->atime < MEMCACHE_TIMEOUT)
138                                 break;
139 #if 0
140                         printf ("%s(): free(%p) size(%zu) usage(%zu/%zu)\n",
141                                         __FUNCTION__,
142                                         (void *)entry,
143                                         entry->size,
144                                         cache->usage, cache->limit);
145 #endif
146         
147                         list = &cache->buckets[entry->size % cache->nbuckets];
148 
149                         TAILQ_REMOVE(list, entry, buckets);
150                         TAILQ_REMOVE(&cache->timeout, entry, timeout);
151                 
152                         cache->usage -= entry->size;
153                         --cache->nelements;
154 
155                         free (entry);
156                 }
157                         
158                 
159                 cache->last_timeout_test = atime;
160                 
161         }
162         
163         if (atime - cache->last_reset > MEMCACHE_RESET_INTERVAL) {
164 #if 0
165                 printf ("%s(): reset() usage(%zu/%zu)\n",
166                                         __FUNCTION__,
167                                         cache->usage, cache->limit);
168 #endif
169                 
170                 if (cache->nbuckets > 15) {
171                         if (0 != memcache_resize (cache, 0)) {
172                         }
173                 }
174                 
175                 cache->last_reset = atime;
176         }
177                 
178         return;
179 
180 out_free:
181         free (ptr);
182         return;
183 }
184 
185 static int memcache_resize (memcache_t *cache, int grow)
186 {
187         size_t                          nbuckets;
188         struct memcache_entry_list      *buckets;
189         size_t                          i;
190 
191         if (cache->nbuckets) {
192                 if (grow)
193                         nbuckets = cache->nbuckets * 2;
194                 else
195                         nbuckets = 15;
196         } else {
197                 nbuckets = 15;
198         }
199         
200 #if 0
201         printf ("%s(): %zu -> %zu\n", __FUNCTION__, cache->nbuckets, nbuckets);
202 #endif
203 
204         /*
205          *
206          * init new buckets
207          *
208          */
209 
210         buckets = malloc (nbuckets * sizeof(*buckets) );
211         if (NULL == buckets)
212                 return -1;
213         
214         for (i=0; i<nbuckets; ++i) {
215                 TAILQ_INIT(&buckets[i]);
216         }
217 
218         /*
219          *
220          * rehash old buckets
221          *
222          */
223 
224         if (cache->buckets) {
225                 for (i=0; i<cache->nbuckets; ++i) {
226                         struct memcache_entry_list      *list_src, *list_dst;
227                         struct memcache_entry           *entry;
228                         
229                         list_src = &cache->buckets[i];
230 
231                         for (;;) {
232                                 entry = TAILQ_FIRST(list_src);
233                                 
234                                 if (NULL == entry)
235                                         break;
236                                 
237                                 TAILQ_REMOVE (list_src,entry,buckets);
238 
239                                 list_dst = &(buckets[entry->size % nbuckets]);
240                                 
241                                 TAILQ_INSERT_TAIL (list_dst,entry,buckets);
242                         }
243                 }
244                 
245                 free (cache->buckets);
246         }
247 
248         cache->buckets = buckets;
249         cache->nbuckets = nbuckets;
250 
251         return 0;
252 }


syntax highlighted by Code2HTML, v. 0.9.1