1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "config.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdbool.h>
6 #include <inttypes.h>
7 
8 #ifndef NDEBUG
9 #include <signal.h>
10 #endif
11 
12 #include "cache.h"
13 
14 #ifndef NDEBUG
15 const uint64_t redzone_pattern = 0xdeadbeefcafebabe;
16 int cache_error = 0;
17 #endif
18 
19 const int initial_pool_size = 64;
20 
inFreeList(cache_t *cache, void *object)21 static bool inFreeList(cache_t *cache, void *object) {
22     bool rv = false;
23     size_t i;
24     for (i = 0; i < cache->freecurr; i++) {
25         rv |= cache->ptr[i] == object;
26     }
27     return rv;
28 }
29 
cache_create(const char *name, size_t bufsize, size_t align, cache_constructor_t* constructor, cache_destructor_t* destructor)30 cache_t* cache_create(const char *name, size_t bufsize, size_t align,
31                       cache_constructor_t* constructor,
32                       cache_destructor_t* destructor) {
33     cache_t* ret = calloc(1, sizeof(cache_t));
34     char* nm = strdup(name);
35     void** ptr = calloc(initial_pool_size, sizeof(void*));
36     if (ret == NULL || nm == NULL || ptr == NULL) {
37         free(ret);
38         free(nm);
39         free(ptr);
40         return NULL;
41     }
42 
43     cb_mutex_initialize(&ret->mutex);
44     ret->name = nm;
45     ret->ptr = ptr;
46     ret->freetotal = initial_pool_size;
47     ret->constructor = constructor;
48     ret->destructor = destructor;
49 
50 #ifndef NDEBUG
51     ret->bufsize = bufsize + 2 * sizeof(redzone_pattern);
52 #else
53     ret->bufsize = bufsize;
54 #endif
55 
56     return ret;
57 }
58 
get_object(void *ptr)59 static void* get_object(void *ptr) {
60 #ifndef NDEBUG
61     uint64_t *pre = ptr;
62     return pre + 1;
63 #else
64     return ptr;
65 #endif
66 }
67 
cache_destroy(cache_t *cache)68 void cache_destroy(cache_t *cache) {
69     while (cache->freecurr > 0) {
70         void *ptr = cache->ptr[--cache->freecurr];
71         if (cache->destructor) {
72             cache->destructor(get_object(ptr), NULL);
73         }
74         free(ptr);
75     }
76     free(cache->name);
77     free(cache->ptr);
78     cb_mutex_destroy(&cache->mutex);
79     free(cache);
80 }
81 
cache_alloc(cache_t *cache)82 void* cache_alloc(cache_t *cache) {
83     void *ret;
84     void *object;
85     cb_mutex_enter(&cache->mutex);
86     if (cache->freecurr > 0) {
87         ret = cache->ptr[--cache->freecurr];
88         object = get_object(ret);
89         cb_assert(!inFreeList(cache, ret));
90     } else {
91         object = ret = malloc(cache->bufsize);
92         if (ret != NULL) {
93             object = get_object(ret);
94 
95             if (cache->constructor != NULL &&
96                 cache->constructor(object, NULL, 0) != 0) {
97                 free(ret);
98                 object = NULL;
99             }
100         }
101     }
102     cb_mutex_exit(&cache->mutex);
103 
104 #ifndef NDEBUG
105     if (object != NULL) {
106         /* add a simple form of buffer-check */
107         uint64_t *pre = ret;
108         *pre = redzone_pattern;
109         ret = pre+1;
110         memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)),
111                &redzone_pattern, sizeof(redzone_pattern));
112     }
113 #endif
114 
115     return object;
116 }
117 
cache_free(cache_t *cache, void *object)118 void cache_free(cache_t *cache, void *object) {
119     void *ptr = object;
120 #ifndef NDEBUG
121     uint64_t *pre = ptr;
122 #endif
123 
124     cb_mutex_enter(&cache->mutex);
125 
126 #ifndef NDEBUG
127     /* validate redzone... */
128     if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)),
129                &redzone_pattern, sizeof(redzone_pattern)) != 0) {
130         raise(SIGABRT);
131         cache_error = 1;
132         cb_mutex_exit(&cache->mutex);
133         return;
134     }
135     --pre;
136     if (*pre != redzone_pattern) {
137         raise(SIGABRT);
138         cache_error = -1;
139         cb_mutex_exit(&cache->mutex);
140         return;
141     }
142     ptr = pre;
143 #endif
144     cb_assert(!inFreeList(cache, ptr));
145     if (cache->freecurr < cache->freetotal) {
146         cache->ptr[cache->freecurr++] = ptr;
147         cb_assert(inFreeList(cache, ptr));
148     } else {
149         /* try to enlarge free connections array */
150         size_t newtotal = cache->freetotal * 2;
151         void **new_free;
152         cb_assert(newtotal > 0);
153         new_free = realloc(cache->ptr, sizeof(void *) * newtotal);
154         if (new_free) {
155             cache->freetotal = newtotal;
156             cache->ptr = new_free;
157             cache->ptr[cache->freecurr++] = ptr;
158             cb_assert(inFreeList(cache, ptr));
159         } else {
160             if (cache->destructor) {
161                 cache->destructor(ptr, NULL);
162             }
163             free(ptr);
164             cb_assert(!inFreeList(cache, ptr));
165         }
166     }
167     cb_mutex_exit(&cache->mutex);
168 }
169