1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <assert.h>
6
7 #ifndef NDEBUG
8 #include <signal.h>
9 #endif
10
11 #include "cache.h"
12
13 #ifndef NDEBUG
14 const uint64_t redzone_pattern = 0xdeadbeefcafedeed;
15 int cache_error = 0;
16 #endif
17
cache_create(const char * name,size_t bufsize,size_t align)18 cache_t* cache_create(const char *name, size_t bufsize, size_t align) {
19 cache_t* ret = calloc(1, sizeof(cache_t));
20 char* nm = strdup(name);
21 if (ret == NULL || nm == NULL ||
22 pthread_mutex_init(&ret->mutex, NULL) == -1) {
23 free(ret);
24 free(nm);
25 return NULL;
26 }
27
28 ret->name = nm;
29 STAILQ_INIT(&ret->head);
30
31 #ifndef NDEBUG
32 ret->bufsize = bufsize + 2 * sizeof(redzone_pattern);
33 #else
34 ret->bufsize = bufsize;
35 #endif
36 assert(ret->bufsize >= sizeof(struct cache_free_s));
37
38 return ret;
39 }
40
cache_set_limit(cache_t * cache,int limit)41 void cache_set_limit(cache_t *cache, int limit) {
42 pthread_mutex_lock(&cache->mutex);
43 cache->limit = limit;
44 pthread_mutex_unlock(&cache->mutex);
45 }
46
get_object(void * ptr)47 static inline void* get_object(void *ptr) {
48 #ifndef NDEBUG
49 uint64_t *pre = ptr;
50 return pre + 1;
51 #else
52 return ptr;
53 #endif
54 }
55
cache_destroy(cache_t * cache)56 void cache_destroy(cache_t *cache) {
57 while (!STAILQ_EMPTY(&cache->head)) {
58 struct cache_free_s *o = STAILQ_FIRST(&cache->head);
59 STAILQ_REMOVE_HEAD(&cache->head, c_next);
60 free(o);
61 }
62 free(cache->name);
63 pthread_mutex_destroy(&cache->mutex);
64 free(cache);
65 }
66
cache_alloc(cache_t * cache)67 void* cache_alloc(cache_t *cache) {
68 void *ret;
69 pthread_mutex_lock(&cache->mutex);
70 ret = do_cache_alloc(cache);
71 pthread_mutex_unlock(&cache->mutex);
72 return ret;
73 }
74
do_cache_alloc(cache_t * cache)75 void* do_cache_alloc(cache_t *cache) {
76 void *ret;
77 void *object;
78 if (cache->freecurr > 0) {
79 ret = STAILQ_FIRST(&cache->head);
80 STAILQ_REMOVE_HEAD(&cache->head, c_next);
81 object = get_object(ret);
82 cache->freecurr--;
83 } else if (cache->limit == 0 || cache->total < cache->limit) {
84 object = ret = malloc(cache->bufsize);
85 if (ret != NULL) {
86 object = get_object(ret);
87
88 cache->total++;
89 }
90 } else {
91 object = NULL;
92 }
93
94 #ifndef NDEBUG
95 if (object != NULL) {
96 /* add a simple form of buffer-check */
97 uint64_t *pre = ret;
98 *pre = redzone_pattern;
99 ret = pre+1;
100 memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)),
101 &redzone_pattern, sizeof(redzone_pattern));
102 }
103 #endif
104
105 return object;
106 }
107
cache_free(cache_t * cache,void * ptr)108 void cache_free(cache_t *cache, void *ptr) {
109 pthread_mutex_lock(&cache->mutex);
110 do_cache_free(cache, ptr);
111 pthread_mutex_unlock(&cache->mutex);
112 }
113
do_cache_free(cache_t * cache,void * ptr)114 void do_cache_free(cache_t *cache, void *ptr) {
115 #ifndef NDEBUG
116 /* validate redzone... */
117 if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)),
118 &redzone_pattern, sizeof(redzone_pattern)) != 0) {
119 raise(SIGABRT);
120 cache_error = 1;
121 return;
122 }
123 uint64_t *pre = ptr;
124 --pre;
125 if (*pre != redzone_pattern) {
126 raise(SIGABRT);
127 cache_error = -1;
128 return;
129 }
130 ptr = pre;
131 #endif
132 if (cache->limit != 0 && cache->limit < cache->total) {
133 free(ptr);
134 cache->total--;
135 } else {
136 STAILQ_INSERT_TAIL(&cache->head, (struct cache_free_s *)ptr, c_next);
137 cache->freecurr++;
138 }
139 }
140
141