10 #include "pthread_impl.h"
12 #if defined(__GNUC__) && defined(__PIC__)
13 #define inline inline __attribute__((always_inline))
16 uintptr_t __brk(uintptr_t);
17 void *__mmap(void *, size_t, int, int, int, off_t);
18 int __munmap(void *, size_t);
19 void *__mremap(void *, size_t, size_t, int, ...);
20 int __madvise(void *, size_t, int);
24 struct chunk *next, *prev;
43 #define SIZE_ALIGN (4*sizeof(size_t))
44 #define SIZE_MASK (-SIZE_ALIGN)
45 #define OVERHEAD (2*sizeof(size_t))
46 #define MMAP_THRESHOLD (0x1c00*SIZE_ALIGN)
48 #define RECLAIM 163840
50 #define CHUNK_SIZE(c) ((c)->csize & -2)
51 #define CHUNK_PSIZE(c) ((c)->psize & -2)
52 #define PREV_CHUNK(c) ((struct chunk *)((char *)(c) - CHUNK_PSIZE(c)))
53 #define NEXT_CHUNK(c) ((struct chunk *)((char *)(c) + CHUNK_SIZE(c)))
54 #define MEM_TO_CHUNK(p) (struct chunk *)((char *)(p) - OVERHEAD)
55 #define CHUNK_TO_MEM(c) (void *)((char *)(c) + OVERHEAD)
56 #define BIN_TO_CHUNK(i) (MEM_TO_CHUNK(&mal.bins[i].head))
58 #define C_INUSE ((size_t)1)
60 #define IS_MMAPPED(c) !((c)->csize & (C_INUSE))
63 /* Synchronization tools */
65 static inline void lock(volatile int *lk)
67 if (libc.threads_minus_1)
68 while(a_swap(lk, 1)) __wait(lk, lk+1, 1, 1);
71 static inline void unlock(volatile int *lk)
75 if (lk[1]) __wake(lk, 1, 1);
79 static inline void lock_bin(int i)
81 lock(mal.bins[i].lock);
82 if (!mal.bins[i].head)
83 mal.bins[i].head = mal.bins[i].tail = BIN_TO_CHUNK(i);
86 static inline void unlock_bin(int i)
88 unlock(mal.bins[i].lock);
91 static int first_set(uint64_t x)
96 static const char debruijn64[64] = {
97 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
98 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
99 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
100 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12
102 static const char debruijn32[32] = {
103 0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13,
104 31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14
106 if (sizeof(long) < 8) {
110 return 32 + debruijn32[(y&-y)*0x076be629 >> 27];
112 return debruijn32[(y&-y)*0x076be629 >> 27];
114 return debruijn64[(x&-x)*0x022fdd63cc95386dull >> 58];
118 static int bin_index(size_t x)
120 x = x / SIZE_ALIGN - 1;
121 if (x <= 32) return x;
122 if (x > 0x1c00) return 63;
123 return ((union { float v; uint32_t r; }){(int)x}.r>>21) - 496;
126 static int bin_index_up(size_t x)
128 x = x / SIZE_ALIGN - 1;
129 if (x <= 32) return x;
130 return ((union { float v; uint32_t r; }){(int)x}.r+0x1fffff>>21) - 496;
134 void __dump_heap(int x)
138 for (c = (void *)mal.heap; CHUNK_SIZE(c); c = NEXT_CHUNK(c))
139 fprintf(stderr, "base %p size %zu (%d) flags %d/%d\n",
140 c, CHUNK_SIZE(c), bin_index(CHUNK_SIZE(c)),
142 NEXT_CHUNK(c)->psize & 15);
143 for (i=0; i<64; i++) {
144 if (mal.bins[i].head != BIN_TO_CHUNK(i) && mal.bins[i].head) {
145 fprintf(stderr, "bin %d: %p\n", i, mal.bins[i].head);
146 if (!(mal.binmap & 1ULL<<i))
147 fprintf(stderr, "missing from binmap!\n");
148 } else if (mal.binmap & 1ULL<<i)
149 fprintf(stderr, "binmap wrongly contains %d!\n", i);
154 static struct chunk *expand_heap(size_t n)
161 if (n > SIZE_MAX - mal.brk - 2*PAGE_SIZE) goto fail;
162 new = mal.brk + n + SIZE_ALIGN + PAGE_SIZE - 1 & -PAGE_SIZE;
165 if (__brk(new) != new) goto fail;
167 w = MEM_TO_CHUNK(new);
168 w->psize = n | C_INUSE;
169 w->csize = 0 | C_INUSE;
171 w = MEM_TO_CHUNK(mal.brk);
172 w->csize = n | C_INUSE;
175 unlock(mal.brk_lock);
179 unlock(mal.brk_lock);
183 static int init_malloc(size_t n)
185 static int init, waiters;
189 if (init == 2) return 0;
191 while ((state=a_swap(&init, 1)) == 1)
192 __wait(&init, &waiters, 1, 1);
200 mal.brk = mal.brk + PAGE_SIZE-1 & -PAGE_SIZE;
202 mal.brk = mal.brk + 2*SIZE_ALIGN-1 & -SIZE_ALIGN;
208 if (waiters) __wake(&init, 1, 1);
212 mal.heap = (void *)c;
213 c->psize = 0 | C_INUSE;
214 free(CHUNK_TO_MEM(c));
217 if (waiters) __wake(&init, -1, 1);
221 static int adjust_size(size_t *n)
223 /* Result of pointer difference must fit in ptrdiff_t. */
224 if (*n-1 > PTRDIFF_MAX - SIZE_ALIGN - PAGE_SIZE) {
233 *n = (*n + OVERHEAD + SIZE_ALIGN - 1) & SIZE_MASK;
237 static void unbin(struct chunk *c, int i)
239 if (c->prev == c->next)
240 a_and_64(&mal.binmap, ~(1ULL<<i));
241 c->prev->next = c->next;
242 c->next->prev = c->prev;
244 NEXT_CHUNK(c)->psize |= C_INUSE;
247 static int alloc_fwd(struct chunk *c)
251 while (!((k=c->csize) & C_INUSE)) {
264 static int alloc_rev(struct chunk *c)
268 while (!((k=c->psize) & C_INUSE)) {
272 unbin(PREV_CHUNK(c), i);
282 /* pretrim - trims a chunk _prior_ to removing it from its bin.
283 * Must be called with i as the ideal bin for size n, j the bin
284 * for the _free_ chunk self, and bin j locked. */
285 static int pretrim(struct chunk *self, size_t n, int i, int j)
288 struct chunk *next, *split;
290 /* We cannot pretrim if it would require re-binning. */
291 if (j < 40) return 0;
293 if (j != 63) return 0;
294 n1 = CHUNK_SIZE(self);
295 if (n1-n <= MMAP_THRESHOLD) return 0;
297 n1 = CHUNK_SIZE(self);
299 if (bin_index(n1-n) != j) return 0;
301 next = NEXT_CHUNK(self);
302 split = (void *)((char *)self + n);
304 split->prev = self->prev;
305 split->next = self->next;
306 split->prev->next = split;
307 split->next->prev = split;
308 split->psize = n | C_INUSE;
311 self->csize = n | C_INUSE;
315 static void trim(struct chunk *self, size_t n)
317 size_t n1 = CHUNK_SIZE(self);
318 struct chunk *next, *split;
320 if (n >= n1 - DONTCARE) return;
322 next = NEXT_CHUNK(self);
323 split = (void *)((char *)self + n);
325 split->psize = n | C_INUSE;
326 split->csize = n1-n | C_INUSE;
327 next->psize = n1-n | C_INUSE;
328 self->csize = n | C_INUSE;
330 free(CHUNK_TO_MEM(split));
333 void *malloc(size_t n)
338 if (adjust_size(&n) < 0) return 0;
340 if (n > MMAP_THRESHOLD) {
341 size_t len = n + OVERHEAD + PAGE_SIZE - 1 & -PAGE_SIZE;
342 char *base = __mmap(0, len, PROT_READ|PROT_WRITE,
343 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
344 if (base == (void *)-1) return 0;
345 c = (void *)(base + SIZE_ALIGN - OVERHEAD);
346 c->csize = len - (SIZE_ALIGN - OVERHEAD);
347 c->psize = SIZE_ALIGN - OVERHEAD;
348 return CHUNK_TO_MEM(c);
353 uint64_t mask = mal.binmap & -(1ULL<<i);
355 if (init_malloc(n) > 0) continue;
361 NEXT_CHUNK(x)->psize = c->csize =
362 x->csize + CHUNK_SIZE(c);
368 c = mal.bins[j].head;
369 if (c != BIN_TO_CHUNK(j) && j == bin_index(c->csize)) {
370 if (!pretrim(c, n, i, j)) unbin(c, j);
377 /* Now patch up in case we over-allocated */
380 return CHUNK_TO_MEM(c);
383 void *realloc(void *p, size_t n)
385 struct chunk *self, *next;
389 if (!p) return malloc(n);
391 if (adjust_size(&n) < 0) return 0;
393 self = MEM_TO_CHUNK(p);
394 n1 = n0 = CHUNK_SIZE(self);
396 if (IS_MMAPPED(self)) {
397 size_t extra = self->psize;
398 char *base = (char *)self - extra;
399 size_t oldlen = n0 + extra;
400 size_t newlen = n + extra;
401 /* Crash on realloc of freed chunk */
402 if (extra & 1) a_crash();
403 if (newlen < PAGE_SIZE && (new = malloc(n))) {
404 memcpy(new, p, n-OVERHEAD);
408 newlen = (newlen + PAGE_SIZE-1) & -PAGE_SIZE;
409 if (oldlen == newlen) return p;
410 base = __mremap(base, oldlen, newlen, MREMAP_MAYMOVE);
411 if (base == (void *)-1)
412 return newlen < oldlen ? p : 0;
413 self = (void *)(base + extra);
414 self->csize = newlen - extra;
415 return CHUNK_TO_MEM(self);
418 next = NEXT_CHUNK(self);
420 /* Crash on corrupted footer (likely from buffer overflow) */
421 if (next->psize != self->csize) a_crash();
423 /* Merge adjacent chunks if we need more space. This is not
424 * a waste of time even if we fail to get enough space, because our
425 * subsequent call to free would otherwise have to do the merge. */
426 if (n > n1 && alloc_fwd(next)) {
427 n1 += CHUNK_SIZE(next);
428 next = NEXT_CHUNK(next);
430 /* FIXME: find what's wrong here and reenable it..? */
431 if (0 && n > n1 && alloc_rev(self)) {
432 self = PREV_CHUNK(self);
433 n1 += CHUNK_SIZE(self);
435 self->csize = n1 | C_INUSE;
436 next->psize = n1 | C_INUSE;
438 /* If we got enough space, split off the excess and return */
440 //memmove(CHUNK_TO_MEM(self), p, n0-OVERHEAD);
442 return CHUNK_TO_MEM(self);
445 /* As a last resort, allocate a new chunk and copy to it. */
446 new = malloc(n-OVERHEAD);
448 memcpy(new, p, n0-OVERHEAD);
449 free(CHUNK_TO_MEM(self));
455 struct chunk *self = MEM_TO_CHUNK(p);
457 size_t final_size, new_size, size;
463 if (IS_MMAPPED(self)) {
464 size_t extra = self->psize;
465 char *base = (char *)self - extra;
466 size_t len = CHUNK_SIZE(self) + extra;
467 /* Crash on double free */
468 if (extra & 1) a_crash();
473 final_size = new_size = CHUNK_SIZE(self);
474 next = NEXT_CHUNK(self);
476 /* Crash on corrupted footer (likely from buffer overflow) */
477 if (next->psize != self->csize) a_crash();
480 /* Replace middle of large chunks with fresh zero pages */
481 if (reclaim && (self->psize & next->csize & C_INUSE)) {
482 uintptr_t a = (uintptr_t)self + SIZE_ALIGN+PAGE_SIZE-1 & -PAGE_SIZE;
483 uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE;
485 __madvise((void *)a, b-a, MADV_DONTNEED);
487 __mmap((void *)a, b-a, PROT_READ|PROT_WRITE,
488 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
492 if (self->psize & next->csize & C_INUSE) {
493 self->csize = final_size | C_INUSE;
494 next->psize = final_size | C_INUSE;
495 i = bin_index(final_size);
498 if (self->psize & next->csize & C_INUSE)
500 unlock(mal.free_lock);
504 if (alloc_rev(self)) {
505 self = PREV_CHUNK(self);
506 size = CHUNK_SIZE(self);
508 if (new_size+size > RECLAIM && (new_size+size^size) > size)
512 if (alloc_fwd(next)) {
513 size = CHUNK_SIZE(next);
515 if (new_size+size > RECLAIM && (new_size+size^size) > size)
517 next = NEXT_CHUNK(next);
521 self->csize = final_size;
522 next->psize = final_size;
523 unlock(mal.free_lock);
525 self->next = BIN_TO_CHUNK(i);
526 self->prev = mal.bins[i].tail;
527 self->next->prev = self;
528 self->prev->next = self;
530 if (!(mal.binmap & 1ULL<<i))
531 a_or_64(&mal.binmap, 1ULL<<i);