1#include "jemalloc/internal/jemalloc_internal.h" 2#ifndef JEMALLOC_ZONE 3# error "This source file is for zones on Darwin (OS X)." 4#endif 5 6/* 7 * The malloc_default_purgeable_zone function is only available on >= 10.6. 8 * We need to check whether it is present at runtime, thus the weak_import. 9 */ 10extern malloc_zone_t *malloc_default_purgeable_zone(void) 11JEMALLOC_ATTR(weak_import); 12 13/******************************************************************************/ 14/* Data. */ 15 16static malloc_zone_t zone; 17static struct malloc_introspection_t zone_introspect; 18 19/******************************************************************************/ 20/* Function prototypes for non-inline static functions. */ 21 22static size_t zone_size(malloc_zone_t *zone, void *ptr); 23static void *zone_malloc(malloc_zone_t *zone, size_t size); 24static void *zone_calloc(malloc_zone_t *zone, size_t num, size_t size); 25static void *zone_valloc(malloc_zone_t *zone, size_t size); 26static void zone_free(malloc_zone_t *zone, void *ptr); 27static void *zone_realloc(malloc_zone_t *zone, void *ptr, size_t size); 28#if (JEMALLOC_ZONE_VERSION >= 5) 29static void *zone_memalign(malloc_zone_t *zone, size_t alignment, 30#endif 31#if (JEMALLOC_ZONE_VERSION >= 6) 32 size_t size); 33static void zone_free_definite_size(malloc_zone_t *zone, void *ptr, 34 size_t size); 35#endif 36static void *zone_destroy(malloc_zone_t *zone); 37static size_t zone_good_size(malloc_zone_t *zone, size_t size); 38static void zone_force_lock(malloc_zone_t *zone); 39static void zone_force_unlock(malloc_zone_t *zone); 40 41/******************************************************************************/ 42/* 43 * Functions. 44 */ 45 46static size_t 47zone_size(malloc_zone_t *zone, void *ptr) 48{ 49 50 /* 51 * There appear to be places within Darwin (such as setenv(3)) that 52 * cause calls to this function with pointers that *no* zone owns. If 53 * we knew that all pointers were owned by *some* zone, we could split 54 * our zone into two parts, and use one as the default allocator and 55 * the other as the default deallocator/reallocator. Since that will 56 * not work in practice, we must check all pointers to assure that they 57 * reside within a mapped chunk before determining size. 58 */ 59 return (ivsalloc(ptr, config_prof)); 60} 61 62static void * 63zone_malloc(malloc_zone_t *zone, size_t size) 64{ 65 66 return (je_malloc(size)); 67} 68 69static void * 70zone_calloc(malloc_zone_t *zone, size_t num, size_t size) 71{ 72 73 return (je_calloc(num, size)); 74} 75 76static void * 77zone_valloc(malloc_zone_t *zone, size_t size) 78{ 79 void *ret = NULL; /* Assignment avoids useless compiler warning. */ 80 81 je_posix_memalign(&ret, PAGE, size); 82 83 return (ret); 84} 85 86static void 87zone_free(malloc_zone_t *zone, void *ptr) 88{ 89 90 if (ivsalloc(ptr, config_prof) != 0) { 91 je_free(ptr); 92 return; 93 } 94 95 free(ptr); 96} 97 98static void * 99zone_realloc(malloc_zone_t *zone, void *ptr, size_t size) 100{ 101 102 if (ivsalloc(ptr, config_prof) != 0) 103 return (je_realloc(ptr, size)); 104 105 return (realloc(ptr, size)); 106} 107 108#if (JEMALLOC_ZONE_VERSION >= 5) 109static void * 110zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) 111{ 112 void *ret = NULL; /* Assignment avoids useless compiler warning. */ 113 114 je_posix_memalign(&ret, alignment, size); 115 116 return (ret); 117} 118#endif 119 120#if (JEMALLOC_ZONE_VERSION >= 6) 121static void 122zone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) 123{ 124 125 if (ivsalloc(ptr, config_prof) != 0) { 126 assert(ivsalloc(ptr, config_prof) == size); 127 je_free(ptr); 128 return; 129 } 130 131 free(ptr); 132} 133#endif 134 135static void * 136zone_destroy(malloc_zone_t *zone) 137{ 138 139 /* This function should never be called. */ 140 not_reached(); 141 return (NULL); 142} 143 144static size_t 145zone_good_size(malloc_zone_t *zone, size_t size) 146{ 147 148 if (size == 0) 149 size = 1; 150 return (s2u(size)); 151} 152 153static void 154zone_force_lock(malloc_zone_t *zone) 155{ 156 157 if (isthreaded) 158 jemalloc_prefork(); 159} 160 161static void 162zone_force_unlock(malloc_zone_t *zone) 163{ 164 165 if (isthreaded) 166 jemalloc_postfork_parent(); 167} 168 169JEMALLOC_ATTR(constructor) 170void 171register_zone(void) 172{ 173 174 /* 175 * If something else replaced the system default zone allocator, don't 176 * register jemalloc's. 177 */ 178 malloc_zone_t *default_zone = malloc_default_zone(); 179 malloc_zone_t *purgeable_zone = NULL; 180 if (!default_zone->zone_name || 181 strcmp(default_zone->zone_name, "DefaultMallocZone") != 0) { 182 return; 183 } 184 185 zone.size = (void *)zone_size; 186 zone.malloc = (void *)zone_malloc; 187 zone.calloc = (void *)zone_calloc; 188 zone.valloc = (void *)zone_valloc; 189 zone.free = (void *)zone_free; 190 zone.realloc = (void *)zone_realloc; 191 zone.destroy = (void *)zone_destroy; 192 zone.zone_name = "jemalloc_zone"; 193 zone.batch_malloc = NULL; 194 zone.batch_free = NULL; 195 zone.introspect = &zone_introspect; 196 zone.version = JEMALLOC_ZONE_VERSION; 197#if (JEMALLOC_ZONE_VERSION >= 5) 198 zone.memalign = zone_memalign; 199#endif 200#if (JEMALLOC_ZONE_VERSION >= 6) 201 zone.free_definite_size = zone_free_definite_size; 202#endif 203#if (JEMALLOC_ZONE_VERSION >= 8) 204 zone.pressure_relief = NULL; 205#endif 206 207 zone_introspect.enumerator = NULL; 208 zone_introspect.good_size = (void *)zone_good_size; 209 zone_introspect.check = NULL; 210 zone_introspect.print = NULL; 211 zone_introspect.log = NULL; 212 zone_introspect.force_lock = (void *)zone_force_lock; 213 zone_introspect.force_unlock = (void *)zone_force_unlock; 214 zone_introspect.statistics = NULL; 215#if (JEMALLOC_ZONE_VERSION >= 6) 216 zone_introspect.zone_locked = NULL; 217#endif 218#if (JEMALLOC_ZONE_VERSION >= 7) 219 zone_introspect.enable_discharge_checking = NULL; 220 zone_introspect.disable_discharge_checking = NULL; 221 zone_introspect.discharge = NULL; 222#ifdef __BLOCKS__ 223 zone_introspect.enumerate_discharged_pointers = NULL; 224#else 225 zone_introspect.enumerate_unavailable_without_blocks = NULL; 226#endif 227#endif 228 229 /* 230 * The default purgeable zone is created lazily by OSX's libc. It uses 231 * the default zone when it is created for "small" allocations 232 * (< 15 KiB), but assumes the default zone is a scalable_zone. This 233 * obviously fails when the default zone is the jemalloc zone, so 234 * malloc_default_purgeable_zone is called beforehand so that the 235 * default purgeable zone is created when the default zone is still 236 * a scalable_zone. As purgeable zones only exist on >= 10.6, we need 237 * to check for the existence of malloc_default_purgeable_zone() at 238 * run time. 239 */ 240 if (malloc_default_purgeable_zone != NULL) 241 purgeable_zone = malloc_default_purgeable_zone(); 242 243 /* Register the custom zone. At this point it won't be the default. */ 244 malloc_zone_register(&zone); 245 246 do { 247 default_zone = malloc_default_zone(); 248 /* 249 * Unregister and reregister the default zone. On OSX >= 10.6, 250 * unregistering takes the last registered zone and places it 251 * at the location of the specified zone. Unregistering the 252 * default zone thus makes the last registered one the default. 253 * On OSX < 10.6, unregistering shifts all registered zones. 254 * The first registered zone then becomes the default. 255 */ 256 malloc_zone_unregister(default_zone); 257 malloc_zone_register(default_zone); 258 /* 259 * On OSX 10.6, having the default purgeable zone appear before 260 * the default zone makes some things crash because it thinks it 261 * owns the default zone allocated pointers. We thus unregister/ 262 * re-register it in order to ensure it's always after the 263 * default zone. On OSX < 10.6, there is no purgeable zone, so 264 * this does nothing. On OSX >= 10.6, unregistering replaces the 265 * purgeable zone with the last registered zone above, i.e the 266 * default zone. Registering it again then puts it at the end, 267 * obviously after the default zone. 268 */ 269 if (purgeable_zone) { 270 malloc_zone_unregister(purgeable_zone); 271 malloc_zone_register(purgeable_zone); 272 } 273 } while (malloc_default_zone() != &zone); 274} 275