1699f73513348647c551b2bf1ca1feba48abd641
[m6w6/libmemcached] / src / libmemcachedprotocol / cache.c
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "mem_config.h"
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <inttypes.h>
21
22 #ifndef NDEBUG
23 # include <signal.h>
24 #endif
25
26 #include "libmemcachedprotocol/common.h"
27
28 #ifndef NDEBUG
29 const uint64_t redzone_pattern = 0xdeadbeefcafebabe;
30 int cache_error = 0;
31 #endif
32
33 const size_t initial_pool_size = 64;
34
35 cache_t *cache_create(const char *name, size_t bufsize, size_t align,
36 cache_constructor_t *constructor, cache_destructor_t *destructor) {
37 cache_t *ret = calloc(1, sizeof(cache_t));
38 size_t name_length = strlen(name);
39 char *nm = calloc(1, (sizeof(char) * name_length) + 1);
40 memcpy(nm, name, name_length);
41 void **ptr = calloc(initial_pool_size, bufsize);
42 if (ret == NULL || nm == NULL || ptr == NULL || pthread_mutex_init(&ret->mutex, NULL) == -1) {
43 free(ret);
44 free(nm);
45 free(ptr);
46 return NULL;
47 }
48
49 ret->name = nm;
50 ret->ptr = ptr;
51 ret->freetotal = initial_pool_size;
52 ret->constructor = constructor;
53 ret->destructor = destructor;
54
55 #ifndef NDEBUG
56 ret->bufsize = bufsize + 2 * sizeof(redzone_pattern);
57 #else
58 ret->bufsize = bufsize;
59 #endif
60
61 (void) align;
62
63 return ret;
64 }
65
66 static inline void *get_object(void *ptr) {
67 #ifndef NDEBUG
68 uint64_t *pre = ptr;
69 return pre + 1;
70 #else
71 return ptr;
72 #endif
73 }
74
75 void cache_destroy(cache_t *cache) {
76 while (cache->freecurr > 0) {
77 void *ptr = cache->ptr[--cache->freecurr];
78 if (cache->destructor) {
79 cache->destructor(get_object(ptr), NULL);
80 }
81 free(ptr);
82 }
83 free(cache->name);
84 free(cache->ptr);
85 pthread_mutex_destroy(&cache->mutex);
86 }
87
88 void *cache_alloc(cache_t *cache) {
89 void *ret;
90 void *object;
91 pthread_mutex_lock(&cache->mutex);
92 if (cache->freecurr > 0) {
93 ret = cache->ptr[--cache->freecurr];
94 object = get_object(ret);
95 } else {
96 object = ret = malloc(cache->bufsize);
97 if (ret) {
98 object = get_object(ret);
99
100 if (cache->constructor && cache->constructor(object, NULL, 0)) {
101 free(ret);
102 object = NULL;
103 }
104 }
105 }
106 pthread_mutex_unlock(&cache->mutex);
107
108 #ifndef NDEBUG
109 if (object) {
110 /* add a simple form of buffer-check */
111 uint64_t *pre = ret;
112 *pre = redzone_pattern;
113 ret = pre + 1;
114 memcpy(((char *) ret) + cache->bufsize - (2 * sizeof(redzone_pattern)), &redzone_pattern,
115 sizeof(redzone_pattern));
116 }
117 #endif
118
119 return object;
120 }
121
122 void cache_free(cache_t *cache, void *ptr) {
123 pthread_mutex_lock(&cache->mutex);
124
125 #ifndef NDEBUG
126 /* validate redzone... */
127 if (memcmp(((char *) ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)), &redzone_pattern,
128 sizeof(redzone_pattern))
129 )
130 {
131 raise(SIGABRT);
132 cache_error = 1;
133 pthread_mutex_unlock(&cache->mutex);
134 return;
135 }
136 uint64_t *pre = ptr;
137 --pre;
138 if (*pre != redzone_pattern) {
139 raise(SIGABRT);
140 cache_error = -1;
141 pthread_mutex_unlock(&cache->mutex);
142 return;
143 }
144 ptr = pre;
145 #endif
146 if (cache->freecurr < cache->freetotal) {
147 cache->ptr[cache->freecurr++] = ptr;
148 } else {
149 /* try to enlarge free connections array */
150 size_t newtotal = cache->freetotal * 2;
151 void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal);
152 if (new_free) {
153 cache->freetotal = newtotal;
154 cache->ptr = new_free;
155 cache->ptr[cache->freecurr++] = ptr;
156 } else {
157 if (cache->destructor) {
158 cache->destructor(ptr, NULL);
159 }
160 free(ptr);
161 }
162 }
163 pthread_mutex_unlock(&cache->mutex);
164 }