]> git.saurik.com Git - redis.git/blame - deps/jemalloc.orig/src/chunk_dss.c
Jemalloc updated to 3.0.0.
[redis.git] / deps / jemalloc.orig / src / chunk_dss.c
CommitLineData
4934f93d 1#define JEMALLOC_CHUNK_DSS_C_
2#include "jemalloc/internal/jemalloc_internal.h"
3#ifdef JEMALLOC_DSS
4/******************************************************************************/
5/* Data. */
6
7malloc_mutex_t dss_mtx;
8
9/* Base address of the DSS. */
10static void *dss_base;
11/* Current end of the DSS, or ((void *)-1) if the DSS is exhausted. */
12static void *dss_prev;
13/* Current upper limit on DSS addresses. */
14static void *dss_max;
15
16/*
17 * Trees of chunks that were previously allocated (trees differ only in node
18 * ordering). These are used when allocating chunks, in an attempt to re-use
19 * address space. Depending on function, different tree orderings are needed,
20 * which is why there are two trees with the same contents.
21 */
22static extent_tree_t dss_chunks_szad;
23static extent_tree_t dss_chunks_ad;
24
25/******************************************************************************/
26/* Function prototypes for non-inline static functions. */
27
28static void *chunk_recycle_dss(size_t size, bool *zero);
29static extent_node_t *chunk_dealloc_dss_record(void *chunk, size_t size);
30
31/******************************************************************************/
32
33static void *
34chunk_recycle_dss(size_t size, bool *zero)
35{
36 extent_node_t *node, key;
37
38 key.addr = NULL;
39 key.size = size;
40 malloc_mutex_lock(&dss_mtx);
41 node = extent_tree_szad_nsearch(&dss_chunks_szad, &key);
42 if (node != NULL) {
43 void *ret = node->addr;
44
45 /* Remove node from the tree. */
46 extent_tree_szad_remove(&dss_chunks_szad, node);
47 if (node->size == size) {
48 extent_tree_ad_remove(&dss_chunks_ad, node);
49 base_node_dealloc(node);
50 } else {
51 /*
52 * Insert the remainder of node's address range as a
53 * smaller chunk. Its position within dss_chunks_ad
54 * does not change.
55 */
56 assert(node->size > size);
57 node->addr = (void *)((uintptr_t)node->addr + size);
58 node->size -= size;
59 extent_tree_szad_insert(&dss_chunks_szad, node);
60 }
61 malloc_mutex_unlock(&dss_mtx);
62
63 if (*zero)
64 memset(ret, 0, size);
65 return (ret);
66 }
67 malloc_mutex_unlock(&dss_mtx);
68
69 return (NULL);
70}
71
72void *
73chunk_alloc_dss(size_t size, bool *zero)
74{
75 void *ret;
76
77 ret = chunk_recycle_dss(size, zero);
78 if (ret != NULL)
79 return (ret);
80
81 /*
82 * sbrk() uses a signed increment argument, so take care not to
83 * interpret a huge allocation request as a negative increment.
84 */
85 if ((intptr_t)size < 0)
86 return (NULL);
87
88 malloc_mutex_lock(&dss_mtx);
89 if (dss_prev != (void *)-1) {
90 intptr_t incr;
91
92 /*
93 * The loop is necessary to recover from races with other
94 * threads that are using the DSS for something other than
95 * malloc.
96 */
97 do {
98 /* Get the current end of the DSS. */
99 dss_max = sbrk(0);
100
101 /*
102 * Calculate how much padding is necessary to
103 * chunk-align the end of the DSS.
104 */
105 incr = (intptr_t)size
106 - (intptr_t)CHUNK_ADDR2OFFSET(dss_max);
107 if (incr == (intptr_t)size)
108 ret = dss_max;
109 else {
110 ret = (void *)((intptr_t)dss_max + incr);
111 incr += size;
112 }
113
114 dss_prev = sbrk(incr);
115 if (dss_prev == dss_max) {
116 /* Success. */
117 dss_max = (void *)((intptr_t)dss_prev + incr);
118 malloc_mutex_unlock(&dss_mtx);
119 *zero = true;
120 return (ret);
121 }
122 } while (dss_prev != (void *)-1);
123 }
124 malloc_mutex_unlock(&dss_mtx);
125
126 return (NULL);
127}
128
129static extent_node_t *
130chunk_dealloc_dss_record(void *chunk, size_t size)
131{
132 extent_node_t *xnode, *node, *prev, key;
133
134 xnode = NULL;
135 while (true) {
136 key.addr = (void *)((uintptr_t)chunk + size);
137 node = extent_tree_ad_nsearch(&dss_chunks_ad, &key);
138 /* Try to coalesce forward. */
139 if (node != NULL && node->addr == key.addr) {
140 /*
141 * Coalesce chunk with the following address range.
142 * This does not change the position within
143 * dss_chunks_ad, so only remove/insert from/into
144 * dss_chunks_szad.
145 */
146 extent_tree_szad_remove(&dss_chunks_szad, node);
147 node->addr = chunk;
148 node->size += size;
149 extent_tree_szad_insert(&dss_chunks_szad, node);
150 break;
151 } else if (xnode == NULL) {
152 /*
153 * It is possible that base_node_alloc() will cause a
154 * new base chunk to be allocated, so take care not to
155 * deadlock on dss_mtx, and recover if another thread
156 * deallocates an adjacent chunk while this one is busy
157 * allocating xnode.
158 */
159 malloc_mutex_unlock(&dss_mtx);
160 xnode = base_node_alloc();
161 malloc_mutex_lock(&dss_mtx);
162 if (xnode == NULL)
163 return (NULL);
164 } else {
165 /* Coalescing forward failed, so insert a new node. */
166 node = xnode;
167 xnode = NULL;
168 node->addr = chunk;
169 node->size = size;
170 extent_tree_ad_insert(&dss_chunks_ad, node);
171 extent_tree_szad_insert(&dss_chunks_szad, node);
172 break;
173 }
174 }
175 /* Discard xnode if it ended up unused do to a race. */
176 if (xnode != NULL)
177 base_node_dealloc(xnode);
178
179 /* Try to coalesce backward. */
180 prev = extent_tree_ad_prev(&dss_chunks_ad, node);
181 if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==
182 chunk) {
183 /*
184 * Coalesce chunk with the previous address range. This does
185 * not change the position within dss_chunks_ad, so only
186 * remove/insert node from/into dss_chunks_szad.
187 */
188 extent_tree_szad_remove(&dss_chunks_szad, prev);
189 extent_tree_ad_remove(&dss_chunks_ad, prev);
190
191 extent_tree_szad_remove(&dss_chunks_szad, node);
192 node->addr = prev->addr;
193 node->size += prev->size;
194 extent_tree_szad_insert(&dss_chunks_szad, node);
195
196 base_node_dealloc(prev);
197 }
198
199 return (node);
200}
201
202bool
203chunk_in_dss(void *chunk)
204{
205 bool ret;
206
207 malloc_mutex_lock(&dss_mtx);
208 if ((uintptr_t)chunk >= (uintptr_t)dss_base
209 && (uintptr_t)chunk < (uintptr_t)dss_max)
210 ret = true;
211 else
212 ret = false;
213 malloc_mutex_unlock(&dss_mtx);
214
215 return (ret);
216}
217
218bool
219chunk_dealloc_dss(void *chunk, size_t size)
220{
221 bool ret;
222
223 malloc_mutex_lock(&dss_mtx);
224 if ((uintptr_t)chunk >= (uintptr_t)dss_base
225 && (uintptr_t)chunk < (uintptr_t)dss_max) {
226 extent_node_t *node;
227
228 /* Try to coalesce with other unused chunks. */
229 node = chunk_dealloc_dss_record(chunk, size);
230 if (node != NULL) {
231 chunk = node->addr;
232 size = node->size;
233 }
234
235 /* Get the current end of the DSS. */
236 dss_max = sbrk(0);
237
238 /*
239 * Try to shrink the DSS if this chunk is at the end of the
240 * DSS. The sbrk() call here is subject to a race condition
241 * with threads that use brk(2) or sbrk(2) directly, but the
242 * alternative would be to leak memory for the sake of poorly
243 * designed multi-threaded programs.
244 */
245 if ((void *)((uintptr_t)chunk + size) == dss_max
246 && (dss_prev = sbrk(-(intptr_t)size)) == dss_max) {
247 /* Success. */
248 dss_max = (void *)((intptr_t)dss_prev - (intptr_t)size);
249
250 if (node != NULL) {
251 extent_tree_szad_remove(&dss_chunks_szad, node);
252 extent_tree_ad_remove(&dss_chunks_ad, node);
253 base_node_dealloc(node);
254 }
255 } else
256 madvise(chunk, size, MADV_DONTNEED);
257
258 ret = false;
259 goto RETURN;
260 }
261
262 ret = true;
263RETURN:
264 malloc_mutex_unlock(&dss_mtx);
265 return (ret);
266}
267
268bool
269chunk_dss_boot(void)
270{
271
272 if (malloc_mutex_init(&dss_mtx))
273 return (true);
274 dss_base = sbrk(0);
275 dss_prev = dss_base;
276 dss_max = dss_base;
277 extent_tree_szad_new(&dss_chunks_szad);
278 extent_tree_ad_new(&dss_chunks_ad);
279
280 return (false);
281}
282
283/******************************************************************************/
284#endif /* JEMALLOC_DSS */