github.com/afumu/libc@v0.0.6/musl/src/malloc/mallocng/free.c (about) 1 #define _BSD_SOURCE 2 #include <stdlib.h> 3 #include <sys/mman.h> 4 5 #include "meta.h" 6 7 struct mapinfo { 8 void *base; 9 size_t len; 10 }; 11 12 static struct mapinfo nontrivial_free(struct meta *, int); 13 14 static struct mapinfo free_group(struct meta *g) 15 { 16 struct mapinfo mi = { 0 }; 17 int sc = g->sizeclass; 18 if (sc < 48) { 19 ctx.usage_by_class[sc] -= g->last_idx+1; 20 } 21 if (g->maplen) { 22 step_seq(); 23 record_seq(sc); 24 mi.base = g->mem; 25 mi.len = g->maplen*4096UL; 26 } else { 27 void *p = g->mem; 28 struct meta *m = get_meta(p); 29 int idx = get_slot_index(p); 30 g->mem->meta = 0; 31 // not checking size/reserved here; it's intentionally invalid 32 mi = nontrivial_free(m, idx); 33 } 34 free_meta(g); 35 return mi; 36 } 37 38 static int okay_to_free(struct meta *g) 39 { 40 int sc = g->sizeclass; 41 42 if (!g->freeable) return 0; 43 44 // always free individual mmaps not suitable for reuse 45 if (sc >= 48 || get_stride(g) < UNIT*size_classes[sc]) 46 return 1; 47 48 // always free groups allocated inside another group's slot 49 // since recreating them should not be expensive and they 50 // might be blocking freeing of a much larger group. 51 if (!g->maplen) return 1; 52 53 // if there is another non-full group, free this one to 54 // consolidate future allocations, reduce fragmentation. 55 if (g->next != g) return 1; 56 57 // free any group in a size class that's not bouncing 58 if (!is_bouncing(sc)) return 1; 59 60 size_t cnt = g->last_idx+1; 61 size_t usage = ctx.usage_by_class[sc]; 62 63 // if usage is high enough that a larger count should be 64 // used, free the low-count group so a new one will be made. 65 if (9*cnt <= usage && cnt < 20) 66 return 1; 67 68 // otherwise, keep the last group in a bouncing class. 69 return 0; 70 } 71 72 static struct mapinfo nontrivial_free(struct meta *g, int i) 73 { 74 uint32_t self = 1u<<i; 75 int sc = g->sizeclass; 76 uint32_t mask = g->freed_mask | g->avail_mask; 77 78 if (mask+self == (2u<<g->last_idx)-1 && okay_to_free(g)) { 79 // any multi-slot group is necessarily on an active list 80 // here, but single-slot groups might or might not be. 81 if (g->next) { 82 assert(sc < 48); 83 int activate_new = (ctx.active[sc]==g); 84 dequeue(&ctx.active[sc], g); 85 if (activate_new && ctx.active[sc]) 86 activate_group(ctx.active[sc]); 87 } 88 return free_group(g); 89 } else if (!mask) { 90 assert(sc < 48); 91 // might still be active if there were no allocations 92 // after last available slot was taken. 93 if (ctx.active[sc] != g) { 94 queue(&ctx.active[sc], g); 95 } 96 } 97 a_or(&g->freed_mask, self); 98 return (struct mapinfo){ 0 }; 99 } 100 101 void free(void *p) 102 { 103 if (!p) return; 104 105 struct meta *g = get_meta(p); 106 int idx = get_slot_index(p); 107 size_t stride = get_stride(g); 108 unsigned char *start = g->mem->storage + stride*idx; 109 unsigned char *end = start + stride - IB; 110 get_nominal_size(p, end); 111 uint32_t self = 1u<<idx, all = (2u<<g->last_idx)-1; 112 ((unsigned char *)p)[-3] = 255; 113 // invalidate offset to group header, and cycle offset of 114 // used region within slot if current offset is zero. 115 *(uint16_t *)((char *)p-2) = 0; 116 117 // release any whole pages contained in the slot to be freed 118 // unless it's a single-slot group that will be unmapped. 119 if (((uintptr_t)(start-1) ^ (uintptr_t)end) >= 2*PGSZ && g->last_idx) { 120 unsigned char *base = start + (-(uintptr_t)start & (PGSZ-1)); 121 size_t len = (end-base) & -PGSZ; 122 if (len) madvise(base, len, MADV_FREE); 123 } 124 125 // atomic free without locking if this is neither first or last slot 126 for (;;) { 127 uint32_t freed = g->freed_mask; 128 uint32_t avail = g->avail_mask; 129 uint32_t mask = freed | avail; 130 assert(!(mask&self)); 131 if (!freed || mask+self==all) break; 132 if (!MT) 133 g->freed_mask = freed+self; 134 else if (a_cas(&g->freed_mask, freed, freed+self)!=freed) 135 continue; 136 return; 137 } 138 139 wrlock(); 140 struct mapinfo mi = nontrivial_free(g, idx); 141 unlock(); 142 if (mi.len) munmap(mi.base, mi.len); 143 }