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