OLD | NEW |
1 #define _GNU_SOURCE | 1 #define _GNU_SOURCE |
2 #include <stdlib.h> | 2 #include <stdlib.h> |
3 #include <string.h> | 3 #include <string.h> |
4 #include <limits.h> | 4 #include <limits.h> |
5 #include <stdint.h> | 5 #include <stdint.h> |
6 #include <errno.h> | 6 #include <errno.h> |
7 #include <sys/mman.h> | 7 #include <sys/mman.h> |
8 #include "libc.h" | 8 #include "libc.h" |
9 #include "atomic.h" | 9 #include "atomic.h" |
10 #include "pthread_impl.h" | 10 #include "pthread_impl.h" |
11 | 11 |
12 #if defined(__GNUC__) && defined(__PIC__) | 12 #if defined(__GNUC__) && defined(__PIC__) |
13 #define inline inline __attribute__((always_inline)) | 13 #define inline inline __attribute__((always_inline)) |
14 #endif | 14 #endif |
15 | 15 |
16 void *__mmap(void *, size_t, int, int, int, off_t); | 16 void* __mmap(void*, size_t, int, int, int, off_t); |
17 int __munmap(void *, size_t); | 17 int __munmap(void*, size_t); |
18 void *__mremap(void *, size_t, size_t, int, ...); | 18 void* __mremap(void*, size_t, size_t, int, ...); |
19 int __madvise(void *, size_t, int); | 19 int __madvise(void*, size_t, int); |
20 | 20 |
21 struct chunk { | 21 struct chunk { |
22 » size_t psize, csize; | 22 size_t psize, csize; |
23 » struct chunk *next, *prev; | 23 struct chunk *next, *prev; |
24 }; | 24 }; |
25 | 25 |
26 struct bin { | 26 struct bin { |
27 » volatile int lock[2]; | 27 volatile int lock[2]; |
28 » struct chunk *head; | 28 struct chunk* head; |
29 » struct chunk *tail; | 29 struct chunk* tail; |
30 }; | 30 }; |
31 | 31 |
32 static struct { | 32 static struct { |
33 » volatile uint64_t binmap; | 33 volatile uint64_t binmap; |
34 » struct bin bins[64]; | 34 struct bin bins[64]; |
35 » volatile int free_lock[2]; | 35 volatile int free_lock[2]; |
36 } mal; | 36 } mal; |
37 | 37 |
38 | 38 #define SIZE_ALIGN (4 * sizeof(size_t)) |
39 #define SIZE_ALIGN (4*sizeof(size_t)) | |
40 #define SIZE_MASK (-SIZE_ALIGN) | 39 #define SIZE_MASK (-SIZE_ALIGN) |
41 #define OVERHEAD (2*sizeof(size_t)) | 40 #define OVERHEAD (2 * sizeof(size_t)) |
42 #define MMAP_THRESHOLD (0x1c00*SIZE_ALIGN) | 41 #define MMAP_THRESHOLD (0x1c00 * SIZE_ALIGN) |
43 #define DONTCARE 16 | 42 #define DONTCARE 16 |
44 #define RECLAIM 163840 | 43 #define RECLAIM 163840 |
45 | 44 |
46 #define CHUNK_SIZE(c) ((c)->csize & -2) | 45 #define CHUNK_SIZE(c) ((c)->csize & -2) |
47 #define CHUNK_PSIZE(c) ((c)->psize & -2) | 46 #define CHUNK_PSIZE(c) ((c)->psize & -2) |
48 #define PREV_CHUNK(c) ((struct chunk *)((char *)(c) - CHUNK_PSIZE(c))) | 47 #define PREV_CHUNK(c) ((struct chunk*)((char*)(c)-CHUNK_PSIZE(c))) |
49 #define NEXT_CHUNK(c) ((struct chunk *)((char *)(c) + CHUNK_SIZE(c))) | 48 #define NEXT_CHUNK(c) ((struct chunk*)((char*)(c) + CHUNK_SIZE(c))) |
50 #define MEM_TO_CHUNK(p) (struct chunk *)((char *)(p) - OVERHEAD) | 49 #define MEM_TO_CHUNK(p) (struct chunk*)((char*)(p)-OVERHEAD) |
51 #define CHUNK_TO_MEM(c) (void *)((char *)(c) + OVERHEAD) | 50 #define CHUNK_TO_MEM(c) (void*)((char*)(c) + OVERHEAD) |
52 #define BIN_TO_CHUNK(i) (MEM_TO_CHUNK(&mal.bins[i].head)) | 51 #define BIN_TO_CHUNK(i) (MEM_TO_CHUNK(&mal.bins[i].head)) |
53 | 52 |
54 #define C_INUSE ((size_t)1) | 53 #define C_INUSE ((size_t)1) |
55 | 54 |
56 #define IS_MMAPPED(c) !((c)->csize & (C_INUSE)) | 55 #define IS_MMAPPED(c) !((c)->csize & (C_INUSE)) |
57 | 56 |
58 | |
59 /* Synchronization tools */ | 57 /* Synchronization tools */ |
60 | 58 |
61 static inline void lock(volatile int *lk) | 59 static inline void lock(volatile int* lk) { |
62 { | 60 if (libc.threads_minus_1) |
63 » if (libc.threads_minus_1) | 61 while (a_swap(lk, 1)) |
64 » » while(a_swap(lk, 1)) __wait(lk, lk+1, 1, 1); | 62 __wait(lk, lk + 1, 1, 1); |
65 } | 63 } |
66 | 64 |
67 static inline void unlock(volatile int *lk) | 65 static inline void unlock(volatile int* lk) { |
68 { | 66 if (lk[0]) { |
69 » if (lk[0]) { | 67 a_store(lk, 0); |
70 » » a_store(lk, 0); | 68 if (lk[1]) |
71 » » if (lk[1]) __wake(lk, 1, 1); | 69 __wake(lk, 1, 1); |
72 » } | 70 } |
73 } | 71 } |
74 | 72 |
75 static inline void lock_bin(int i) | 73 static inline void lock_bin(int i) { |
76 { | 74 lock(mal.bins[i].lock); |
77 » lock(mal.bins[i].lock); | 75 if (!mal.bins[i].head) |
78 » if (!mal.bins[i].head) | 76 mal.bins[i].head = mal.bins[i].tail = BIN_TO_CHUNK(i); |
79 » » mal.bins[i].head = mal.bins[i].tail = BIN_TO_CHUNK(i); | |
80 } | 77 } |
81 | 78 |
82 static inline void unlock_bin(int i) | 79 static inline void unlock_bin(int i) { |
83 { | 80 unlock(mal.bins[i].lock); |
84 » unlock(mal.bins[i].lock); | |
85 } | 81 } |
86 | 82 |
87 static int first_set(uint64_t x) | 83 static int first_set(uint64_t x) { |
88 { | |
89 #if 1 | 84 #if 1 |
90 » return a_ctz_64(x); | 85 return a_ctz_64(x); |
91 #else | 86 #else |
92 » static const char debruijn64[64] = { | 87 static const char debruijn64[64] = { |
93 » » 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, | 88 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, |
94 » » 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, | 89 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, |
95 » » 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, | 90 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, |
96 » » 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 | 91 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12}; |
97 » }; | 92 static const char debruijn32[32] = { |
98 » static const char debruijn32[32] = { | 93 0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13, |
99 » » 0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13, | 94 31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14}; |
100 » » 31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14 | 95 if (sizeof(long) < 8) { |
101 » }; | 96 uint32_t y = x; |
102 » if (sizeof(long) < 8) { | 97 if (!y) { |
103 » » uint32_t y = x; | 98 y = x >> 32; |
104 » » if (!y) { | 99 return 32 + debruijn32[(y & -y) * 0x076be629 >> 27]; |
105 » » » y = x>>32; | 100 } |
106 » » » return 32 + debruijn32[(y&-y)*0x076be629 >> 27]; | 101 return debruijn32[(y & -y) * 0x076be629 >> 27]; |
107 » » } | 102 } |
108 » » return debruijn32[(y&-y)*0x076be629 >> 27]; | 103 return debruijn64[(x & -x) * 0x022fdd63cc95386dull >> 58]; |
109 » } | |
110 » return debruijn64[(x&-x)*0x022fdd63cc95386dull >> 58]; | |
111 #endif | 104 #endif |
112 } | 105 } |
113 | 106 |
114 static int bin_index(size_t x) | 107 static int bin_index(size_t x) { |
115 { | 108 x = x / SIZE_ALIGN - 1; |
116 » x = x / SIZE_ALIGN - 1; | 109 if (x <= 32) |
117 » if (x <= 32) return x; | 110 return x; |
118 » if (x > 0x1c00) return 63; | 111 if (x > 0x1c00) |
119 » return ((union { float v; uint32_t r; }){(int)x}.r>>21) - 496; | 112 return 63; |
| 113 return ((union { |
| 114 float v; |
| 115 uint32_t r; |
| 116 }){(int)x} |
| 117 .r >> |
| 118 21) - |
| 119 496; |
120 } | 120 } |
121 | 121 |
122 static int bin_index_up(size_t x) | 122 static int bin_index_up(size_t x) { |
123 { | 123 x = x / SIZE_ALIGN - 1; |
124 » x = x / SIZE_ALIGN - 1; | 124 if (x <= 32) |
125 » if (x <= 32) return x; | 125 return x; |
126 » return (((union { float v; uint32_t r; }){(int)x}.r+0x1fffff)>>21) - 496
; | 126 return (((union { |
| 127 float v; |
| 128 uint32_t r; |
| 129 }){(int)x} |
| 130 .r + |
| 131 0x1fffff) >> |
| 132 21) - |
| 133 496; |
127 } | 134 } |
128 | 135 |
129 #if 0 | 136 #if 0 |
130 void __dump_heap(int x) | 137 void __dump_heap(int x) |
131 { | 138 { |
132 struct chunk *c; | 139 struct chunk *c; |
133 int i; | 140 int i; |
134 for (c = (void *)mal.heap; CHUNK_SIZE(c); c = NEXT_CHUNK(c)) | 141 for (c = (void *)mal.heap; CHUNK_SIZE(c); c = NEXT_CHUNK(c)) |
135 fprintf(stderr, "base %p size %zu (%d) flags %d/%d\n", | 142 fprintf(stderr, "base %p size %zu (%d) flags %d/%d\n", |
136 c, CHUNK_SIZE(c), bin_index(CHUNK_SIZE(c)), | 143 c, CHUNK_SIZE(c), bin_index(CHUNK_SIZE(c)), |
137 c->csize & 15, | 144 c->csize & 15, |
138 NEXT_CHUNK(c)->psize & 15); | 145 NEXT_CHUNK(c)->psize & 15); |
139 for (i=0; i<64; i++) { | 146 for (i=0; i<64; i++) { |
140 if (mal.bins[i].head != BIN_TO_CHUNK(i) && mal.bins[i].head) { | 147 if (mal.bins[i].head != BIN_TO_CHUNK(i) && mal.bins[i].head) { |
141 fprintf(stderr, "bin %d: %p\n", i, mal.bins[i].head); | 148 fprintf(stderr, "bin %d: %p\n", i, mal.bins[i].head); |
142 if (!(mal.binmap & 1ULL<<i)) | 149 if (!(mal.binmap & 1ULL<<i)) |
143 fprintf(stderr, "missing from binmap!\n"); | 150 fprintf(stderr, "missing from binmap!\n"); |
144 } else if (mal.binmap & 1ULL<<i) | 151 } else if (mal.binmap & 1ULL<<i) |
145 fprintf(stderr, "binmap wrongly contains %d!\n", i); | 152 fprintf(stderr, "binmap wrongly contains %d!\n", i); |
146 } | 153 } |
147 } | 154 } |
148 #endif | 155 #endif |
149 | 156 |
150 void *__expand_heap(size_t *); | 157 void* __expand_heap(size_t*); |
151 | 158 |
152 static struct chunk *expand_heap(size_t n) | 159 static struct chunk* expand_heap(size_t n) { |
153 { | 160 static int heap_lock[2]; |
154 » static int heap_lock[2]; | 161 static void* end; |
155 » static void *end; | 162 void* p; |
156 » void *p; | 163 struct chunk* w; |
157 » struct chunk *w; | 164 |
158 | 165 /* The argument n already accounts for the caller's chunk |
159 » /* The argument n already accounts for the caller's chunk | 166 * overhead needs, but if the heap can't be extended in-place, |
160 » * overhead needs, but if the heap can't be extended in-place, | 167 * we need room for an extra zero-sized sentinel chunk. */ |
161 » * we need room for an extra zero-sized sentinel chunk. */ | 168 n += SIZE_ALIGN; |
162 » n += SIZE_ALIGN; | 169 |
163 | 170 lock(heap_lock); |
164 » lock(heap_lock); | 171 |
165 | 172 p = __expand_heap(&n); |
166 » p = __expand_heap(&n); | 173 if (!p) { |
167 » if (!p) { | 174 unlock(heap_lock); |
168 » » unlock(heap_lock); | 175 return 0; |
169 » » return 0; | 176 } |
170 » } | 177 |
171 | 178 /* If not just expanding existing space, we need to make a |
172 » /* If not just expanding existing space, we need to make a | 179 * new sentinel chunk below the allocated space. */ |
173 » * new sentinel chunk below the allocated space. */ | 180 if (p != end) { |
174 » if (p != end) { | 181 /* Valid/safe because of the prologue increment. */ |
175 » » /* Valid/safe because of the prologue increment. */ | 182 n -= SIZE_ALIGN; |
176 » » n -= SIZE_ALIGN; | 183 p = (char*)p + SIZE_ALIGN; |
177 » » p = (char *)p + SIZE_ALIGN; | 184 w = MEM_TO_CHUNK(p); |
178 » » w = MEM_TO_CHUNK(p); | 185 w->psize = 0 | C_INUSE; |
179 » » w->psize = 0 | C_INUSE; | 186 } |
180 » } | 187 |
181 | 188 /* Record new heap end and fill in footer. */ |
182 » /* Record new heap end and fill in footer. */ | 189 end = (char*)p + n; |
183 » end = (char *)p + n; | 190 w = MEM_TO_CHUNK(end); |
184 » w = MEM_TO_CHUNK(end); | 191 w->psize = n | C_INUSE; |
185 » w->psize = n | C_INUSE; | 192 w->csize = 0 | C_INUSE; |
186 » w->csize = 0 | C_INUSE; | 193 |
187 | 194 /* Fill in header, which may be new or may be replacing a |
188 » /* Fill in header, which may be new or may be replacing a | 195 * zero-size sentinel header at the old end-of-heap. */ |
189 » * zero-size sentinel header at the old end-of-heap. */ | 196 w = MEM_TO_CHUNK(p); |
190 » w = MEM_TO_CHUNK(p); | 197 w->csize = n | C_INUSE; |
191 » w->csize = n | C_INUSE; | 198 |
192 | 199 unlock(heap_lock); |
193 » unlock(heap_lock); | 200 |
194 | 201 return w; |
195 » return w; | 202 } |
196 } | 203 |
197 | 204 static int adjust_size(size_t* n) { |
198 static int adjust_size(size_t *n) | 205 /* Result of pointer difference must fit in ptrdiff_t. */ |
199 { | 206 if (*n - 1 > PTRDIFF_MAX - SIZE_ALIGN - PAGE_SIZE) { |
200 » /* Result of pointer difference must fit in ptrdiff_t. */ | 207 if (*n) { |
201 » if (*n-1 > PTRDIFF_MAX - SIZE_ALIGN - PAGE_SIZE) { | 208 errno = ENOMEM; |
202 » » if (*n) { | 209 return -1; |
203 » » » errno = ENOMEM; | 210 } else { |
204 » » » return -1; | 211 *n = SIZE_ALIGN; |
205 » » } else { | 212 return 0; |
206 » » » *n = SIZE_ALIGN; | 213 } |
207 » » » return 0; | 214 } |
208 » » } | 215 *n = (*n + OVERHEAD + SIZE_ALIGN - 1) & SIZE_MASK; |
209 » } | 216 return 0; |
210 » *n = (*n + OVERHEAD + SIZE_ALIGN - 1) & SIZE_MASK; | 217 } |
211 » return 0; | 218 |
212 } | 219 static void unbin(struct chunk* c, int i) { |
213 | 220 if (c->prev == c->next) |
214 static void unbin(struct chunk *c, int i) | 221 a_and_64(&mal.binmap, ~(1ULL << i)); |
215 { | 222 c->prev->next = c->next; |
216 » if (c->prev == c->next) | 223 c->next->prev = c->prev; |
217 » » a_and_64(&mal.binmap, ~(1ULL<<i)); | 224 c->csize |= C_INUSE; |
218 » c->prev->next = c->next; | 225 NEXT_CHUNK(c)->psize |= C_INUSE; |
219 » c->next->prev = c->prev; | 226 } |
220 » c->csize |= C_INUSE; | 227 |
221 » NEXT_CHUNK(c)->psize |= C_INUSE; | 228 static int alloc_fwd(struct chunk* c) { |
222 } | 229 int i; |
223 | 230 size_t k; |
224 static int alloc_fwd(struct chunk *c) | 231 while (!((k = c->csize) & C_INUSE)) { |
225 { | 232 i = bin_index(k); |
226 » int i; | 233 lock_bin(i); |
227 » size_t k; | 234 if (c->csize == k) { |
228 » while (!((k=c->csize) & C_INUSE)) { | 235 unbin(c, i); |
229 » » i = bin_index(k); | 236 unlock_bin(i); |
230 » » lock_bin(i); | 237 return 1; |
231 » » if (c->csize == k) { | 238 } |
232 » » » unbin(c, i); | 239 unlock_bin(i); |
233 » » » unlock_bin(i); | 240 } |
234 » » » return 1; | 241 return 0; |
235 » » } | 242 } |
236 » » unlock_bin(i); | 243 |
237 » } | 244 static int alloc_rev(struct chunk* c) { |
238 » return 0; | 245 int i; |
239 } | 246 size_t k; |
240 | 247 while (!((k = c->psize) & C_INUSE)) { |
241 static int alloc_rev(struct chunk *c) | 248 i = bin_index(k); |
242 { | 249 lock_bin(i); |
243 » int i; | 250 if (c->psize == k) { |
244 » size_t k; | 251 unbin(PREV_CHUNK(c), i); |
245 » while (!((k=c->psize) & C_INUSE)) { | 252 unlock_bin(i); |
246 » » i = bin_index(k); | 253 return 1; |
247 » » lock_bin(i); | 254 } |
248 » » if (c->psize == k) { | 255 unlock_bin(i); |
249 » » » unbin(PREV_CHUNK(c), i); | 256 } |
250 » » » unlock_bin(i); | 257 return 0; |
251 » » » return 1; | 258 } |
252 » » } | |
253 » » unlock_bin(i); | |
254 » } | |
255 » return 0; | |
256 } | |
257 | |
258 | 259 |
259 /* pretrim - trims a chunk _prior_ to removing it from its bin. | 260 /* pretrim - trims a chunk _prior_ to removing it from its bin. |
260 * Must be called with i as the ideal bin for size n, j the bin | 261 * Must be called with i as the ideal bin for size n, j the bin |
261 * for the _free_ chunk self, and bin j locked. */ | 262 * for the _free_ chunk self, and bin j locked. */ |
262 static int pretrim(struct chunk *self, size_t n, int i, int j) | 263 static int pretrim(struct chunk* self, size_t n, int i, int j) { |
263 { | 264 size_t n1; |
264 size_t n1; | 265 struct chunk *next, *split; |
265 struct chunk *next, *split; | 266 |
266 | 267 /* We cannot pretrim if it would require re-binning. */ |
267 /* We cannot pretrim if it would require re-binning. */ | 268 if (j < 40) |
268 if (j < 40) return 0; | 269 return 0; |
269 if (j < i+3) { | 270 if (j < i + 3) { |
270 if (j != 63) return 0; | 271 if (j != 63) |
271 n1 = CHUNK_SIZE(self); | 272 return 0; |
272 if (n1-n <= MMAP_THRESHOLD) return 0; | 273 n1 = CHUNK_SIZE(self); |
273 } else { | 274 if (n1 - n <= MMAP_THRESHOLD) |
274 n1 = CHUNK_SIZE(self); | 275 return 0; |
275 } | 276 } else { |
276 if (bin_index(n1-n) != j) return 0; | 277 n1 = CHUNK_SIZE(self); |
277 | 278 } |
278 next = NEXT_CHUNK(self); | 279 if (bin_index(n1 - n) != j) |
279 split = (void *)((char *)self + n); | 280 return 0; |
280 | 281 |
281 split->prev = self->prev; | 282 next = NEXT_CHUNK(self); |
282 split->next = self->next; | 283 split = (void*)((char*)self + n); |
283 split->prev->next = split; | 284 |
284 split->next->prev = split; | 285 split->prev = self->prev; |
285 split->psize = n | C_INUSE; | 286 split->next = self->next; |
286 split->csize = n1-n; | 287 split->prev->next = split; |
287 next->psize = n1-n; | 288 split->next->prev = split; |
288 self->csize = n | C_INUSE; | 289 split->psize = n | C_INUSE; |
289 return 1; | 290 split->csize = n1 - n; |
290 } | 291 next->psize = n1 - n; |
291 | 292 self->csize = n | C_INUSE; |
292 static void trim(struct chunk *self, size_t n) | 293 return 1; |
293 { | 294 } |
294 size_t n1 = CHUNK_SIZE(self); | 295 |
295 struct chunk *next, *split; | 296 static void trim(struct chunk* self, size_t n) { |
296 | 297 size_t n1 = CHUNK_SIZE(self); |
297 if (n >= n1 - DONTCARE) return; | 298 struct chunk *next, *split; |
298 | 299 |
299 next = NEXT_CHUNK(self); | 300 if (n >= n1 - DONTCARE) |
300 split = (void *)((char *)self + n); | 301 return; |
301 | 302 |
302 split->psize = n | C_INUSE; | 303 next = NEXT_CHUNK(self); |
303 split->csize = n1-n | C_INUSE; | 304 split = (void*)((char*)self + n); |
304 next->psize = n1-n | C_INUSE; | 305 |
305 self->csize = n | C_INUSE; | 306 split->psize = n | C_INUSE; |
306 | 307 split->csize = n1 - n | C_INUSE; |
307 free(CHUNK_TO_MEM(split)); | 308 next->psize = n1 - n | C_INUSE; |
308 } | 309 self->csize = n | C_INUSE; |
309 | 310 |
310 void *malloc(size_t n) | 311 free(CHUNK_TO_MEM(split)); |
311 { | 312 } |
312 struct chunk *c; | 313 |
313 int i, j; | 314 void* malloc(size_t n) { |
314 | 315 struct chunk* c; |
315 if (adjust_size(&n) < 0) return 0; | 316 int i, j; |
316 | 317 |
317 if (n > MMAP_THRESHOLD) { | 318 if (adjust_size(&n) < 0) |
318 size_t len = n + OVERHEAD + PAGE_SIZE - 1 & -PAGE_SIZE; | 319 return 0; |
319 char *base = __mmap(0, len, PROT_READ|PROT_WRITE, | 320 |
320 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); | 321 if (n > MMAP_THRESHOLD) { |
321 if (base == (void *)-1) return 0; | 322 size_t len = n + OVERHEAD + PAGE_SIZE - 1 & -PAGE_SIZE; |
322 c = (void *)(base + SIZE_ALIGN - OVERHEAD); | 323 char* base = __mmap(0, len, PROT_READ | PROT_WRITE, |
323 c->csize = len - (SIZE_ALIGN - OVERHEAD); | 324 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
324 c->psize = SIZE_ALIGN - OVERHEAD; | 325 if (base == (void*)-1) |
325 return CHUNK_TO_MEM(c); | 326 return 0; |
326 } | 327 c = (void*)(base + SIZE_ALIGN - OVERHEAD); |
327 | 328 c->csize = len - (SIZE_ALIGN - OVERHEAD); |
328 i = bin_index_up(n); | 329 c->psize = SIZE_ALIGN - OVERHEAD; |
329 for (;;) { | 330 return CHUNK_TO_MEM(c); |
330 uint64_t mask = mal.binmap & -(1ULL<<i); | 331 } |
331 if (!mask) { | 332 |
332 c = expand_heap(n); | 333 i = bin_index_up(n); |
333 if (!c) return 0; | 334 for (;;) { |
334 if (alloc_rev(c)) { | 335 uint64_t mask = mal.binmap & -(1ULL << i); |
335 struct chunk *x = c; | 336 if (!mask) { |
336 c = PREV_CHUNK(c); | 337 c = expand_heap(n); |
337 NEXT_CHUNK(x)->psize = c->csize = | 338 if (!c) |
338 x->csize + CHUNK_SIZE(c); | 339 return 0; |
339 } | 340 if (alloc_rev(c)) { |
340 break; | 341 struct chunk* x = c; |
341 } | 342 c = PREV_CHUNK(c); |
342 j = first_set(mask); | 343 NEXT_CHUNK(x)->psize = c->csize = x->csize + CHUNK_SIZE(c); |
343 lock_bin(j); | 344 } |
344 c = mal.bins[j].head; | 345 break; |
345 if (c != BIN_TO_CHUNK(j)) { | 346 } |
346 if (!pretrim(c, n, i, j)) unbin(c, j); | 347 j = first_set(mask); |
347 unlock_bin(j); | 348 lock_bin(j); |
348 break; | 349 c = mal.bins[j].head; |
349 } | 350 if (c != BIN_TO_CHUNK(j)) { |
350 unlock_bin(j); | 351 if (!pretrim(c, n, i, j)) |
351 } | 352 unbin(c, j); |
352 | 353 unlock_bin(j); |
353 /* Now patch up in case we over-allocated */ | 354 break; |
354 trim(c, n); | 355 } |
355 | 356 unlock_bin(j); |
356 return CHUNK_TO_MEM(c); | 357 } |
357 } | 358 |
358 | 359 /* Now patch up in case we over-allocated */ |
359 void *__malloc0(size_t n) | 360 trim(c, n); |
360 { | 361 |
361 void *p = malloc(n); | 362 return CHUNK_TO_MEM(c); |
362 if (p && !IS_MMAPPED(MEM_TO_CHUNK(p))) { | 363 } |
363 size_t *z; | 364 |
364 n = (n + sizeof *z - 1)/sizeof *z; | 365 void* __malloc0(size_t n) { |
365 for (z=p; n; n--, z++) if (*z) *z=0; | 366 void* p = malloc(n); |
366 } | 367 if (p && !IS_MMAPPED(MEM_TO_CHUNK(p))) { |
367 return p; | 368 size_t* z; |
368 } | 369 n = (n + sizeof *z - 1) / sizeof *z; |
369 | 370 for (z = p; n; n--, z++) |
370 void *realloc(void *p, size_t n) | 371 if (*z) |
371 { | 372 *z = 0; |
372 struct chunk *self, *next; | 373 } |
373 size_t n0, n1; | 374 return p; |
374 void *new; | 375 } |
375 | 376 |
376 if (!p) return malloc(n); | 377 void* realloc(void* p, size_t n) { |
377 | 378 struct chunk *self, *next; |
378 if (adjust_size(&n) < 0) return 0; | 379 size_t n0, n1; |
379 | 380 void* new; |
380 self = MEM_TO_CHUNK(p); | 381 |
381 n1 = n0 = CHUNK_SIZE(self); | 382 if (!p) |
382 | 383 return malloc(n); |
383 if (IS_MMAPPED(self)) { | 384 |
384 size_t extra = self->psize; | 385 if (adjust_size(&n) < 0) |
385 char *base = (char *)self - extra; | 386 return 0; |
386 size_t oldlen = n0 + extra; | 387 |
387 size_t newlen = n + extra; | 388 self = MEM_TO_CHUNK(p); |
388 /* Crash on realloc of freed chunk */ | 389 n1 = n0 = CHUNK_SIZE(self); |
389 if (extra & 1) a_crash(); | 390 |
390 if (newlen < PAGE_SIZE && (new = malloc(n))) { | 391 if (IS_MMAPPED(self)) { |
391 memcpy(new, p, n-OVERHEAD); | 392 size_t extra = self->psize; |
392 free(p); | 393 char* base = (char*)self - extra; |
393 return new; | 394 size_t oldlen = n0 + extra; |
394 } | 395 size_t newlen = n + extra; |
395 newlen = (newlen + PAGE_SIZE-1) & -PAGE_SIZE; | 396 /* Crash on realloc of freed chunk */ |
396 if (oldlen == newlen) return p; | 397 if (extra & 1) |
397 base = __mremap(base, oldlen, newlen, MREMAP_MAYMOVE); | 398 a_crash(); |
398 if (base == (void *)-1) | 399 if (newlen < PAGE_SIZE && (new = malloc(n))) { |
399 return newlen < oldlen ? p : 0; | 400 memcpy(new, p, n - OVERHEAD); |
400 self = (void *)(base + extra); | 401 free(p); |
401 self->csize = newlen - extra; | 402 return new; |
402 return CHUNK_TO_MEM(self); | 403 } |
403 } | 404 newlen = (newlen + PAGE_SIZE - 1) & -PAGE_SIZE; |
404 | 405 if (oldlen == newlen) |
405 next = NEXT_CHUNK(self); | 406 return p; |
406 | 407 base = __mremap(base, oldlen, newlen, MREMAP_MAYMOVE); |
407 /* Crash on corrupted footer (likely from buffer overflow) */ | 408 if (base == (void*)-1) |
408 if (next->psize != self->csize) a_crash(); | 409 return newlen < oldlen ? p : 0; |
409 | 410 self = (void*)(base + extra); |
410 /* Merge adjacent chunks if we need more space. This is not | 411 self->csize = newlen - extra; |
411 * a waste of time even if we fail to get enough space, because our | 412 return CHUNK_TO_MEM(self); |
412 * subsequent call to free would otherwise have to do the merge. */ | 413 } |
413 if (n > n1 && alloc_fwd(next)) { | 414 |
414 n1 += CHUNK_SIZE(next); | 415 next = NEXT_CHUNK(self); |
415 next = NEXT_CHUNK(next); | 416 |
416 } | 417 /* Crash on corrupted footer (likely from buffer overflow) */ |
417 /* FIXME: find what's wrong here and reenable it..? */ | 418 if (next->psize != self->csize) |
418 if (0 && n > n1 && alloc_rev(self)) { | 419 a_crash(); |
419 self = PREV_CHUNK(self); | 420 |
420 n1 += CHUNK_SIZE(self); | 421 /* Merge adjacent chunks if we need more space. This is not |
421 } | 422 * a waste of time even if we fail to get enough space, because our |
422 self->csize = n1 | C_INUSE; | 423 * subsequent call to free would otherwise have to do the merge. */ |
423 next->psize = n1 | C_INUSE; | 424 if (n > n1 && alloc_fwd(next)) { |
424 | 425 n1 += CHUNK_SIZE(next); |
425 /* If we got enough space, split off the excess and return */ | 426 next = NEXT_CHUNK(next); |
426 if (n <= n1) { | 427 } |
427 //memmove(CHUNK_TO_MEM(self), p, n0-OVERHEAD); | 428 /* FIXME: find what's wrong here and reenable it..? */ |
428 trim(self, n); | 429 if (0 && n > n1 && alloc_rev(self)) { |
429 return CHUNK_TO_MEM(self); | 430 self = PREV_CHUNK(self); |
430 } | 431 n1 += CHUNK_SIZE(self); |
431 | 432 } |
432 /* As a last resort, allocate a new chunk and copy to it. */ | 433 self->csize = n1 | C_INUSE; |
433 new = malloc(n-OVERHEAD); | 434 next->psize = n1 | C_INUSE; |
434 if (!new) return 0; | 435 |
435 memcpy(new, p, n0-OVERHEAD); | 436 /* If we got enough space, split off the excess and return */ |
436 free(CHUNK_TO_MEM(self)); | 437 if (n <= n1) { |
437 return new; | 438 // memmove(CHUNK_TO_MEM(self), p, n0-OVERHEAD); |
438 } | 439 trim(self, n); |
439 | 440 return CHUNK_TO_MEM(self); |
440 void free(void *p) | 441 } |
441 { | 442 |
442 struct chunk *self = MEM_TO_CHUNK(p); | 443 /* As a last resort, allocate a new chunk and copy to it. */ |
443 struct chunk *next; | 444 new = malloc(n - OVERHEAD); |
444 size_t final_size, new_size, size; | 445 if (!new) |
445 int reclaim=0; | 446 return 0; |
446 int i; | 447 memcpy(new, p, n0 - OVERHEAD); |
447 | 448 free(CHUNK_TO_MEM(self)); |
448 if (!p) return; | 449 return new; |
449 | 450 } |
450 if (IS_MMAPPED(self)) { | 451 |
451 size_t extra = self->psize; | 452 void free(void* p) { |
452 char *base = (char *)self - extra; | 453 struct chunk* self = MEM_TO_CHUNK(p); |
453 size_t len = CHUNK_SIZE(self) + extra; | 454 struct chunk* next; |
454 /* Crash on double free */ | 455 size_t final_size, new_size, size; |
455 if (extra & 1) a_crash(); | 456 int reclaim = 0; |
456 __munmap(base, len); | 457 int i; |
457 return; | 458 |
458 } | 459 if (!p) |
459 | 460 return; |
460 final_size = new_size = CHUNK_SIZE(self); | 461 |
461 next = NEXT_CHUNK(self); | 462 if (IS_MMAPPED(self)) { |
462 | 463 size_t extra = self->psize; |
463 /* Crash on corrupted footer (likely from buffer overflow) */ | 464 char* base = (char*)self - extra; |
464 if (next->psize != self->csize) a_crash(); | 465 size_t len = CHUNK_SIZE(self) + extra; |
465 | 466 /* Crash on double free */ |
466 for (;;) { | 467 if (extra & 1) |
467 if (self->psize & next->csize & C_INUSE) { | 468 a_crash(); |
468 self->csize = final_size | C_INUSE; | 469 __munmap(base, len); |
469 next->psize = final_size | C_INUSE; | 470 return; |
470 i = bin_index(final_size); | 471 } |
471 lock_bin(i); | 472 |
472 lock(mal.free_lock); | 473 final_size = new_size = CHUNK_SIZE(self); |
473 if (self->psize & next->csize & C_INUSE) | 474 next = NEXT_CHUNK(self); |
474 break; | 475 |
475 unlock(mal.free_lock); | 476 /* Crash on corrupted footer (likely from buffer overflow) */ |
476 unlock_bin(i); | 477 if (next->psize != self->csize) |
477 } | 478 a_crash(); |
478 | 479 |
479 if (alloc_rev(self)) { | 480 for (;;) { |
480 self = PREV_CHUNK(self); | 481 if (self->psize & next->csize & C_INUSE) { |
481 size = CHUNK_SIZE(self); | 482 self->csize = final_size | C_INUSE; |
482 final_size += size; | 483 next->psize = final_size | C_INUSE; |
483 if (new_size+size > RECLAIM && (new_size+size^size) > si
ze) | 484 i = bin_index(final_size); |
484 reclaim = 1; | 485 lock_bin(i); |
485 } | 486 lock(mal.free_lock); |
486 | 487 if (self->psize & next->csize & C_INUSE) |
487 if (alloc_fwd(next)) { | 488 break; |
488 size = CHUNK_SIZE(next); | 489 unlock(mal.free_lock); |
489 final_size += size; | 490 unlock_bin(i); |
490 if (new_size+size > RECLAIM && (new_size+size^size) > si
ze) | 491 } |
491 reclaim = 1; | 492 |
492 next = NEXT_CHUNK(next); | 493 if (alloc_rev(self)) { |
493 } | 494 self = PREV_CHUNK(self); |
494 } | 495 size = CHUNK_SIZE(self); |
495 | 496 final_size += size; |
496 if (!(mal.binmap & 1ULL<<i)) | 497 if (new_size + size > RECLAIM && (new_size + size ^ size) > size) |
497 a_or_64(&mal.binmap, 1ULL<<i); | 498 reclaim = 1; |
498 | 499 } |
499 self->csize = final_size; | 500 |
500 next->psize = final_size; | 501 if (alloc_fwd(next)) { |
501 unlock(mal.free_lock); | 502 size = CHUNK_SIZE(next); |
502 | 503 final_size += size; |
503 self->next = BIN_TO_CHUNK(i); | 504 if (new_size + size > RECLAIM && (new_size + size ^ size) > size) |
504 self->prev = mal.bins[i].tail; | 505 reclaim = 1; |
505 self->next->prev = self; | 506 next = NEXT_CHUNK(next); |
506 self->prev->next = self; | 507 } |
507 | 508 } |
508 /* Replace middle of large chunks with fresh zero pages */ | 509 |
509 if (reclaim) { | 510 if (!(mal.binmap & 1ULL << i)) |
510 uintptr_t a = (uintptr_t)self + SIZE_ALIGN+PAGE_SIZE-1 & -PAGE_S
IZE; | 511 a_or_64(&mal.binmap, 1ULL << i); |
511 uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE; | 512 |
| 513 self->csize = final_size; |
| 514 next->psize = final_size; |
| 515 unlock(mal.free_lock); |
| 516 |
| 517 self->next = BIN_TO_CHUNK(i); |
| 518 self->prev = mal.bins[i].tail; |
| 519 self->next->prev = self; |
| 520 self->prev->next = self; |
| 521 |
| 522 /* Replace middle of large chunks with fresh zero pages */ |
| 523 if (reclaim) { |
| 524 uintptr_t a = (uintptr_t)self + SIZE_ALIGN + PAGE_SIZE - 1 & -PAGE_SIZE; |
| 525 uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE; |
512 #if 1 | 526 #if 1 |
513 » » __madvise((void *)a, b-a, MADV_DONTNEED); | 527 __madvise((void*)a, b - a, MADV_DONTNEED); |
514 #else | 528 #else |
515 » » __mmap((void *)a, b-a, PROT_READ|PROT_WRITE, | 529 __mmap((void*)a, b - a, PROT_READ | PROT_WRITE, |
516 » » » MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); | 530 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); |
517 #endif | 531 #endif |
518 » } | 532 } |
519 | 533 |
520 » unlock_bin(i); | 534 unlock_bin(i); |
521 } | 535 } |
OLD | NEW |