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