microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/allocator/mimalloc-sys/mimalloc/src/alloc.c
692lines · modeblame
91589c3aIan Davis2 years ago | 1 | /* ---------------------------------------------------------------------------- |
ce21e681Ian Davis1 years ago | 2 | Copyright (c) 2018-2024, Microsoft Research, Daan Leijen |
91589c3aIan Davis2 years ago | 3 | This is free software; you can redistribute it and/or modify it under the |
| 4 | terms of the MIT license. A copy of the license can be found in the file | |
| 5 | "LICENSE" at the root of this distribution. | |
| 6 | -----------------------------------------------------------------------------*/ | |
| 7 | #ifndef _DEFAULT_SOURCE | |
| 8 | #define _DEFAULT_SOURCE // for realpath() on Linux | |
| 9 | #endif | |
| 10 | | |
| 11 | #include "mimalloc.h" | |
| 12 | #include "mimalloc/internal.h" | |
| 13 | #include "mimalloc/atomic.h" | |
| 14 | #include "mimalloc/prim.h" // _mi_prim_thread_id() | |
| 15 | | |
| 16 | #include <string.h> // memset, strlen (for mi_strdup) | |
| 17 | #include <stdlib.h> // malloc, abort | |
| 18 | | |
| 19 | #define MI_IN_ALLOC_C | |
| 20 | #include "alloc-override.c" | |
ce21e681Ian Davis1 years ago | 21 | #include "free.c" |
91589c3aIan Davis2 years ago | 22 | #undef MI_IN_ALLOC_C |
| 23 | | |
| 24 | // ------------------------------------------------------ | |
| 25 | // Allocation | |
| 26 | // ------------------------------------------------------ | |
| 27 | | |
| 28 | // Fast allocation in a page: just pop from the free list. | |
| 29 | // Fall back to generic allocation only if the list is empty. | |
ce21e681Ian Davis1 years ago | 30 | // Note: in release mode the (inlined) routine is about 7 instructions with a single test. |
| 31 | extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept | |
| 32 | { | |
| 33 | mi_assert_internal(size >= MI_PADDING_SIZE); | |
| 34 | mi_assert_internal(page->block_size == 0 /* empty heap */ || mi_page_block_size(page) >= size); | |
| 35 | | |
| 36 | // check the free list | |
91589c3aIan Davis2 years ago | 37 | mi_block_t* const block = page->free; |
| 38 | if mi_unlikely(block == NULL) { | |
| 39 | return _mi_malloc_generic(heap, size, zero, 0); | |
| 40 | } | |
| 41 | mi_assert_internal(block != NULL && _mi_ptr_page(block) == page); | |
ce21e681Ian Davis1 years ago | 42 | |
91589c3aIan Davis2 years ago | 43 | // pop from the free list |
| 44 | page->free = mi_block_next(page, block); | |
ce21e681Ian Davis1 years ago | 45 | page->used++; |
91589c3aIan Davis2 years ago | 46 | mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page); |
ce21e681Ian Davis1 years ago | 47 | mi_assert_internal(page->block_size < MI_MAX_ALIGN_SIZE || _mi_is_aligned(block, MI_MAX_ALIGN_SIZE)); |
| 48 | | |
91589c3aIan Davis2 years ago | 49 | #if MI_DEBUG>3 |
ce21e681Ian Davis1 years ago | 50 | if (page->free_is_zero && size > sizeof(*block)) { |
91589c3aIan Davis2 years ago | 51 | mi_assert_expensive(mi_mem_is_zero(block+1,size - sizeof(*block))); |
| 52 | } | |
| 53 | #endif | |
| 54 | | |
| 55 | // allow use of the block internally | |
| 56 | // note: when tracking we need to avoid ever touching the MI_PADDING since | |
| 57 | // that is tracked by valgrind etc. as non-accessible (through the red-zone, see `mimalloc/track.h`) | |
| 58 | mi_track_mem_undefined(block, mi_page_usable_block_size(page)); | |
| 59 | | |
| 60 | // zero the block? note: we need to zero the full block size (issue #63) | |
| 61 | if mi_unlikely(zero) { | |
ce21e681Ian Davis1 years ago | 62 | mi_assert_internal(page->block_size != 0); // do not call with zero'ing for huge blocks (see _mi_malloc_generic) |
| 63 | mi_assert_internal(!mi_page_is_huge(page)); | |
| 64 | #if MI_PADDING | |
| 65 | mi_assert_internal(page->block_size >= MI_PADDING_SIZE); | |
| 66 | #endif | |
91589c3aIan Davis2 years ago | 67 | if (page->free_is_zero) { |
| 68 | block->next = 0; | |
ce21e681Ian Davis1 years ago | 69 | mi_track_mem_defined(block, page->block_size - MI_PADDING_SIZE); |
91589c3aIan Davis2 years ago | 70 | } |
| 71 | else { | |
ce21e681Ian Davis1 years ago | 72 | _mi_memzero_aligned(block, page->block_size - MI_PADDING_SIZE); |
| 73 | } | |
91589c3aIan Davis2 years ago | 74 | } |
| 75 | | |
ce21e681Ian Davis1 years ago | 76 | #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN |
91589c3aIan Davis2 years ago | 77 | if (!zero && !mi_page_is_huge(page)) { |
| 78 | memset(block, MI_DEBUG_UNINIT, mi_page_usable_block_size(page)); | |
| 79 | } | |
ce21e681Ian Davis1 years ago | 80 | #elif (MI_SECURE!=0) |
91589c3aIan Davis2 years ago | 81 | if (!zero) { block->next = 0; } // don't leak internal data |
ce21e681Ian Davis1 years ago | 82 | #endif |
91589c3aIan Davis2 years ago | 83 | |
ce21e681Ian Davis1 years ago | 84 | #if (MI_STAT>0) |
91589c3aIan Davis2 years ago | 85 | const size_t bsize = mi_page_usable_block_size(page); |
| 86 | if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) { | |
ce21e681Ian Davis1 years ago | 87 | mi_heap_stat_increase(heap, malloc_normal, bsize); |
| 88 | mi_heap_stat_counter_increase(heap, malloc_normal_count, 1); | |
| 89 | #if (MI_STAT>1) | |
91589c3aIan Davis2 years ago | 90 | const size_t bin = _mi_bin(bsize); |
ce21e681Ian Davis1 years ago | 91 | mi_heap_stat_increase(heap, malloc_bins[bin], 1); |
| 92 | mi_heap_stat_increase(heap, malloc_requested, size - MI_PADDING_SIZE); | |
| 93 | #endif | |
91589c3aIan Davis2 years ago | 94 | } |
| 95 | #endif | |
ce21e681Ian Davis1 years ago | 96 | |
| 97 | #if MI_PADDING // && !MI_TRACK_ENABLED | |
| 98 | mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + mi_page_usable_block_size(page)); | |
| 99 | ptrdiff_t delta = ((uint8_t*)padding - (uint8_t*)block - (size - MI_PADDING_SIZE)); | |
| 100 | #if (MI_DEBUG>=2) | |
| 101 | mi_assert_internal(delta >= 0 && mi_page_usable_block_size(page) >= (size - MI_PADDING_SIZE + delta)); | |
| 102 | #endif | |
| 103 | mi_track_mem_defined(padding,sizeof(mi_padding_t)); // note: re-enable since mi_page_usable_block_size may set noaccess | |
| 104 | padding->canary = mi_ptr_encode_canary(page,block,page->keys); | |
| 105 | padding->delta = (uint32_t)(delta); | |
| 106 | #if MI_PADDING_CHECK | |
| 107 | if (!mi_page_is_huge(page)) { | |
| 108 | uint8_t* fill = (uint8_t*)padding - delta; | |
| 109 | const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // set at most N initial padding bytes | |
| 110 | for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; } | |
| 111 | } | |
| 112 | #endif | |
91589c3aIan Davis2 years ago | 113 | #endif |
| 114 | | |
| 115 | return block; | |
| 116 | } | |
| 117 | | |
ce21e681Ian Davis1 years ago | 118 | // extra entries for improved efficiency in `alloc-aligned.c`. |
| 119 | extern void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept { | |
| 120 | return _mi_page_malloc_zero(heap,page,size,false); | |
| 121 | } | |
| 122 | extern void* _mi_page_malloc_zeroed(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept { | |
| 123 | return _mi_page_malloc_zero(heap,page,size,true); | |
| 124 | } | |
| 125 | | |
| 126 | #if MI_GUARDED | |
| 127 | mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept; | |
| 128 | #endif | |
| 129 | | |
91589c3aIan Davis2 years ago | 130 | static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { |
| 131 | mi_assert(heap != NULL); | |
ce21e681Ian Davis1 years ago | 132 | mi_assert(size <= MI_SMALL_SIZE_MAX); |
91589c3aIan Davis2 years ago | 133 | #if MI_DEBUG |
| 134 | const uintptr_t tid = _mi_thread_id(); | |
| 135 | mi_assert(heap->thread_id == 0 || heap->thread_id == tid); // heaps are thread local | |
| 136 | #endif | |
ce21e681Ian Davis1 years ago | 137 | #if (MI_PADDING || MI_GUARDED) |
91589c3aIan Davis2 years ago | 138 | if (size == 0) { size = sizeof(void*); } |
| 139 | #endif | |
ce21e681Ian Davis1 years ago | 140 | #if MI_GUARDED |
| 141 | if (mi_heap_malloc_use_guarded(heap,size)) { | |
| 142 | return _mi_heap_malloc_guarded(heap, size, zero); | |
91589c3aIan Davis2 years ago | 143 | } |
| 144 | #endif | |
ce21e681Ian Davis1 years ago | 145 | |
| 146 | // get page in constant time, and allocate from it | |
| 147 | mi_page_t* page = _mi_heap_get_free_small_page(heap, size + MI_PADDING_SIZE); | |
| 148 | void* const p = _mi_page_malloc_zero(heap, page, size + MI_PADDING_SIZE, zero); | |
| 149 | mi_track_malloc(p,size,zero); | |
| 150 | | |
91589c3aIan Davis2 years ago | 151 | #if MI_DEBUG>3 |
| 152 | if (p != NULL && zero) { | |
| 153 | mi_assert_expensive(mi_mem_is_zero(p, size)); | |
| 154 | } | |
| 155 | #endif | |
| 156 | return p; | |
| 157 | } | |
| 158 | | |
| 159 | // allocate a small block | |
| 160 | mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { | |
| 161 | return mi_heap_malloc_small_zero(heap, size, false); | |
| 162 | } | |
| 163 | | |
| 164 | mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept { | |
| 165 | return mi_heap_malloc_small(mi_prim_get_default_heap(), size); | |
| 166 | } | |
| 167 | | |
| 168 | // The main allocation function | |
| 169 | extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept { | |
ce21e681Ian Davis1 years ago | 170 | // fast path for small objects |
91589c3aIan Davis2 years ago | 171 | if mi_likely(size <= MI_SMALL_SIZE_MAX) { |
| 172 | mi_assert_internal(huge_alignment == 0); | |
| 173 | return mi_heap_malloc_small_zero(heap, size, zero); | |
| 174 | } | |
ce21e681Ian Davis1 years ago | 175 | #if MI_GUARDED |
| 176 | else if (huge_alignment==0 && mi_heap_malloc_use_guarded(heap,size)) { | |
| 177 | return _mi_heap_malloc_guarded(heap, size, zero); | |
| 178 | } | |
| 179 | #endif | |
91589c3aIan Davis2 years ago | 180 | else { |
ce21e681Ian Davis1 years ago | 181 | // regular allocation |
91589c3aIan Davis2 years ago | 182 | mi_assert(heap!=NULL); |
| 183 | mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local | |
| 184 | void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment); // note: size can overflow but it is detected in malloc_generic | |
| 185 | mi_track_malloc(p,size,zero); | |
ce21e681Ian Davis1 years ago | 186 | |
91589c3aIan Davis2 years ago | 187 | #if MI_DEBUG>3 |
| 188 | if (p != NULL && zero) { | |
| 189 | mi_assert_expensive(mi_mem_is_zero(p, size)); | |
| 190 | } | |
| 191 | #endif | |
| 192 | return p; | |
| 193 | } | |
| 194 | } | |
| 195 | | |
| 196 | extern inline void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { | |
| 197 | return _mi_heap_malloc_zero_ex(heap, size, zero, 0); | |
| 198 | } | |
| 199 | | |
| 200 | mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { | |
| 201 | return _mi_heap_malloc_zero(heap, size, false); | |
| 202 | } | |
| 203 | | |
| 204 | mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept { | |
| 205 | return mi_heap_malloc(mi_prim_get_default_heap(), size); | |
| 206 | } | |
| 207 | | |
| 208 | // zero initialized small block | |
| 209 | mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept { | |
| 210 | return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true); | |
| 211 | } | |
| 212 | | |
| 213 | mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { | |
| 214 | return _mi_heap_malloc_zero(heap, size, true); | |
| 215 | } | |
| 216 | | |
| 217 | mi_decl_nodiscard mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept { | |
| 218 | return mi_heap_zalloc(mi_prim_get_default_heap(),size); | |
| 219 | } | |
| 220 | | |
| 221 | | |
| 222 | mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { | |
| 223 | size_t total; | |
| 224 | if (mi_count_size_overflow(count,size,&total)) return NULL; | |
| 225 | return mi_heap_zalloc(heap,total); | |
| 226 | } | |
| 227 | | |
| 228 | mi_decl_nodiscard mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept { | |
| 229 | return mi_heap_calloc(mi_prim_get_default_heap(),count,size); | |
| 230 | } | |
| 231 | | |
| 232 | // Uninitialized `calloc` | |
| 233 | mi_decl_nodiscard extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { | |
| 234 | size_t total; | |
| 235 | if (mi_count_size_overflow(count, size, &total)) return NULL; | |
| 236 | return mi_heap_malloc(heap, total); | |
| 237 | } | |
| 238 | | |
| 239 | mi_decl_nodiscard mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept { | |
| 240 | return mi_heap_mallocn(mi_prim_get_default_heap(),count,size); | |
| 241 | } | |
| 242 | | |
| 243 | // Expand (or shrink) in place (or fail) | |
| 244 | void* mi_expand(void* p, size_t newsize) mi_attr_noexcept { | |
| 245 | #if MI_PADDING | |
| 246 | // we do not shrink/expand with padding enabled | |
| 247 | MI_UNUSED(p); MI_UNUSED(newsize); | |
| 248 | return NULL; | |
| 249 | #else | |
| 250 | if (p == NULL) return NULL; | |
| 251 | const size_t size = _mi_usable_size(p,"mi_expand"); | |
| 252 | if (newsize > size) return NULL; | |
| 253 | return p; // it fits | |
| 254 | #endif | |
| 255 | } | |
| 256 | | |
| 257 | void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept { | |
| 258 | // if p == NULL then behave as malloc. | |
| 259 | // else if size == 0 then reallocate to a zero-sized block (and don't return NULL, just as mi_malloc(0)). | |
| 260 | // (this means that returning NULL always indicates an error, and `p` will not have been freed in that case.) | |
| 261 | const size_t size = _mi_usable_size(p,"mi_realloc"); // also works if p == NULL (with size 0) | |
| 262 | if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0) | |
| 263 | mi_assert_internal(p!=NULL); | |
| 264 | // todo: do not track as the usable size is still the same in the free; adjust potential padding? | |
| 265 | // mi_track_resize(p,size,newsize) | |
| 266 | // if (newsize < size) { mi_track_mem_noaccess((uint8_t*)p + newsize, size - newsize); } | |
| 267 | return p; // reallocation still fits and not more than 50% waste | |
| 268 | } | |
| 269 | void* newp = mi_heap_malloc(heap,newsize); | |
| 270 | if mi_likely(newp != NULL) { | |
| 271 | if (zero && newsize > size) { | |
| 272 | // also set last word in the previous allocation to zero to ensure any padding is zero-initialized | |
| 273 | const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); | |
| 274 | _mi_memzero((uint8_t*)newp + start, newsize - start); | |
| 275 | } | |
| 276 | else if (newsize == 0) { | |
| 277 | ((uint8_t*)newp)[0] = 0; // work around for applications that expect zero-reallocation to be zero initialized (issue #725) | |
| 278 | } | |
| 279 | if mi_likely(p != NULL) { | |
| 280 | const size_t copysize = (newsize > size ? size : newsize); | |
| 281 | mi_track_mem_defined(p,copysize); // _mi_useable_size may be too large for byte precise memory tracking.. | |
| 282 | _mi_memcpy(newp, p, copysize); | |
| 283 | mi_free(p); // only free the original pointer if successful | |
| 284 | } | |
| 285 | } | |
| 286 | return newp; | |
| 287 | } | |
| 288 | | |
| 289 | mi_decl_nodiscard void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { | |
| 290 | return _mi_heap_realloc_zero(heap, p, newsize, false); | |
| 291 | } | |
| 292 | | |
| 293 | mi_decl_nodiscard void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { | |
| 294 | size_t total; | |
| 295 | if (mi_count_size_overflow(count, size, &total)) return NULL; | |
| 296 | return mi_heap_realloc(heap, p, total); | |
| 297 | } | |
| 298 | | |
| 299 | | |
| 300 | // Reallocate but free `p` on errors | |
| 301 | mi_decl_nodiscard void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { | |
| 302 | void* newp = mi_heap_realloc(heap, p, newsize); | |
| 303 | if (newp==NULL && p!=NULL) mi_free(p); | |
| 304 | return newp; | |
| 305 | } | |
| 306 | | |
| 307 | mi_decl_nodiscard void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { | |
| 308 | return _mi_heap_realloc_zero(heap, p, newsize, true); | |
| 309 | } | |
| 310 | | |
| 311 | mi_decl_nodiscard void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { | |
| 312 | size_t total; | |
| 313 | if (mi_count_size_overflow(count, size, &total)) return NULL; | |
| 314 | return mi_heap_rezalloc(heap, p, total); | |
| 315 | } | |
| 316 | | |
| 317 | | |
| 318 | mi_decl_nodiscard void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept { | |
| 319 | return mi_heap_realloc(mi_prim_get_default_heap(),p,newsize); | |
| 320 | } | |
| 321 | | |
| 322 | mi_decl_nodiscard void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept { | |
| 323 | return mi_heap_reallocn(mi_prim_get_default_heap(),p,count,size); | |
| 324 | } | |
| 325 | | |
| 326 | // Reallocate but free `p` on errors | |
| 327 | mi_decl_nodiscard void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept { | |
| 328 | return mi_heap_reallocf(mi_prim_get_default_heap(),p,newsize); | |
| 329 | } | |
| 330 | | |
| 331 | mi_decl_nodiscard void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept { | |
| 332 | return mi_heap_rezalloc(mi_prim_get_default_heap(), p, newsize); | |
| 333 | } | |
| 334 | | |
| 335 | mi_decl_nodiscard void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { | |
| 336 | return mi_heap_recalloc(mi_prim_get_default_heap(), p, count, size); | |
| 337 | } | |
| 338 | | |
| 339 | | |
| 340 | | |
| 341 | // ------------------------------------------------------ | |
| 342 | // strdup, strndup, and realpath | |
| 343 | // ------------------------------------------------------ | |
| 344 | | |
| 345 | // `strdup` using mi_malloc | |
| 346 | mi_decl_nodiscard mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept { | |
| 347 | if (s == NULL) return NULL; | |
ce21e681Ian Davis1 years ago | 348 | size_t len = _mi_strlen(s); |
| 349 | char* t = (char*)mi_heap_malloc(heap,len+1); | |
91589c3aIan Davis2 years ago | 350 | if (t == NULL) return NULL; |
ce21e681Ian Davis1 years ago | 351 | _mi_memcpy(t, s, len); |
| 352 | t[len] = 0; | |
91589c3aIan Davis2 years ago | 353 | return t; |
| 354 | } | |
| 355 | | |
| 356 | mi_decl_nodiscard mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept { | |
| 357 | return mi_heap_strdup(mi_prim_get_default_heap(), s); | |
| 358 | } | |
| 359 | | |
| 360 | // `strndup` using mi_malloc | |
| 361 | mi_decl_nodiscard mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept { | |
| 362 | if (s == NULL) return NULL; | |
ce21e681Ian Davis1 years ago | 363 | const size_t len = _mi_strnlen(s,n); // len <= n |
| 364 | char* t = (char*)mi_heap_malloc(heap, len+1); | |
91589c3aIan Davis2 years ago | 365 | if (t == NULL) return NULL; |
ce21e681Ian Davis1 years ago | 366 | _mi_memcpy(t, s, len); |
| 367 | t[len] = 0; | |
91589c3aIan Davis2 years ago | 368 | return t; |
| 369 | } | |
| 370 | | |
| 371 | mi_decl_nodiscard mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { | |
| 372 | return mi_heap_strndup(mi_prim_get_default_heap(),s,n); | |
| 373 | } | |
| 374 | | |
| 375 | #ifndef __wasi__ | |
| 376 | // `realpath` using mi_malloc | |
| 377 | #ifdef _WIN32 | |
| 378 | #ifndef PATH_MAX | |
| 379 | #define PATH_MAX MAX_PATH | |
| 380 | #endif | |
ce21e681Ian Davis1 years ago | 381 | |
91589c3aIan Davis2 years ago | 382 | mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { |
| 383 | // todo: use GetFullPathNameW to allow longer file names | |
| 384 | char buf[PATH_MAX]; | |
| 385 | DWORD res = GetFullPathNameA(fname, PATH_MAX, (resolved_name == NULL ? buf : resolved_name), NULL); | |
| 386 | if (res == 0) { | |
| 387 | errno = GetLastError(); return NULL; | |
| 388 | } | |
| 389 | else if (res > PATH_MAX) { | |
| 390 | errno = EINVAL; return NULL; | |
| 391 | } | |
| 392 | else if (resolved_name != NULL) { | |
| 393 | return resolved_name; | |
| 394 | } | |
| 395 | else { | |
| 396 | return mi_heap_strndup(heap, buf, PATH_MAX); | |
| 397 | } | |
| 398 | } | |
| 399 | #else | |
| 400 | /* | |
| 401 | #include <unistd.h> // pathconf | |
| 402 | static size_t mi_path_max(void) { | |
| 403 | static size_t path_max = 0; | |
| 404 | if (path_max <= 0) { | |
| 405 | long m = pathconf("/",_PC_PATH_MAX); | |
| 406 | if (m <= 0) path_max = 4096; // guess | |
| 407 | else if (m < 256) path_max = 256; // at least 256 | |
| 408 | else path_max = m; | |
| 409 | } | |
| 410 | return path_max; | |
| 411 | } | |
| 412 | */ | |
| 413 | char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { | |
| 414 | if (resolved_name != NULL) { | |
| 415 | return realpath(fname,resolved_name); | |
| 416 | } | |
| 417 | else { | |
| 418 | char* rname = realpath(fname, NULL); | |
| 419 | if (rname == NULL) return NULL; | |
| 420 | char* result = mi_heap_strdup(heap, rname); | |
ce21e681Ian Davis1 years ago | 421 | mi_cfree(rname); // use checked free (which may be redirected to our free but that's ok) |
| 422 | // note: with ASAN realpath is intercepted and mi_cfree may leak the returned pointer :-( | |
91589c3aIan Davis2 years ago | 423 | return result; |
| 424 | } | |
| 425 | /* | |
| 426 | const size_t n = mi_path_max(); | |
| 427 | char* buf = (char*)mi_malloc(n+1); | |
| 428 | if (buf == NULL) { | |
| 429 | errno = ENOMEM; | |
| 430 | return NULL; | |
| 431 | } | |
| 432 | char* rname = realpath(fname,buf); | |
| 433 | char* result = mi_heap_strndup(heap,rname,n); // ok if `rname==NULL` | |
| 434 | mi_free(buf); | |
| 435 | return result; | |
| 436 | } | |
| 437 | */ | |
| 438 | } | |
| 439 | #endif | |
| 440 | | |
| 441 | mi_decl_nodiscard mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept { | |
| 442 | return mi_heap_realpath(mi_prim_get_default_heap(),fname,resolved_name); | |
| 443 | } | |
| 444 | #endif | |
| 445 | | |
| 446 | /*------------------------------------------------------- | |
| 447 | C++ new and new_aligned | |
| 448 | The standard requires calling into `get_new_handler` and | |
| 449 | throwing the bad_alloc exception on failure. If we compile | |
| 450 | with a C++ compiler we can implement this precisely. If we | |
| 451 | use a C compiler we cannot throw a `bad_alloc` exception | |
| 452 | but we call `exit` instead (i.e. not returning). | |
| 453 | -------------------------------------------------------*/ | |
| 454 | | |
| 455 | #ifdef __cplusplus | |
| 456 | #include <new> | |
| 457 | static bool mi_try_new_handler(bool nothrow) { | |
| 458 | #if defined(_MSC_VER) || (__cplusplus >= 201103L) | |
| 459 | std::new_handler h = std::get_new_handler(); | |
| 460 | #else | |
| 461 | std::new_handler h = std::set_new_handler(); | |
| 462 | std::set_new_handler(h); | |
| 463 | #endif | |
| 464 | if (h==NULL) { | |
| 465 | _mi_error_message(ENOMEM, "out of memory in 'new'"); | |
ce21e681Ian Davis1 years ago | 466 | #if defined(_CPPUNWIND) || defined(__cpp_exceptions) // exceptions are not always enabled |
91589c3aIan Davis2 years ago | 467 | if (!nothrow) { |
| 468 | throw std::bad_alloc(); | |
| 469 | } | |
ce21e681Ian Davis1 years ago | 470 | #else |
| 471 | MI_UNUSED(nothrow); | |
| 472 | #endif | |
91589c3aIan Davis2 years ago | 473 | return false; |
| 474 | } | |
| 475 | else { | |
| 476 | h(); | |
| 477 | return true; | |
| 478 | } | |
| 479 | } | |
| 480 | #else | |
| 481 | typedef void (*std_new_handler_t)(void); | |
| 482 | | |
| 483 | #if (defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER))) // exclude clang-cl, see issue #631 | |
| 484 | std_new_handler_t __attribute__((weak)) _ZSt15get_new_handlerv(void) { | |
| 485 | return NULL; | |
| 486 | } | |
| 487 | static std_new_handler_t mi_get_new_handler(void) { | |
| 488 | return _ZSt15get_new_handlerv(); | |
| 489 | } | |
| 490 | #else | |
| 491 | // note: on windows we could dynamically link to `?get_new_handler@std@@YAP6AXXZXZ`. | |
| 492 | static std_new_handler_t mi_get_new_handler() { | |
| 493 | return NULL; | |
| 494 | } | |
| 495 | #endif | |
| 496 | | |
| 497 | static bool mi_try_new_handler(bool nothrow) { | |
| 498 | std_new_handler_t h = mi_get_new_handler(); | |
| 499 | if (h==NULL) { | |
| 500 | _mi_error_message(ENOMEM, "out of memory in 'new'"); | |
| 501 | if (!nothrow) { | |
| 502 | abort(); // cannot throw in plain C, use abort | |
| 503 | } | |
| 504 | return false; | |
| 505 | } | |
| 506 | else { | |
| 507 | h(); | |
| 508 | return true; | |
| 509 | } | |
| 510 | } | |
| 511 | #endif | |
| 512 | | |
| 513 | mi_decl_export mi_decl_noinline void* mi_heap_try_new(mi_heap_t* heap, size_t size, bool nothrow ) { | |
| 514 | void* p = NULL; | |
| 515 | while(p == NULL && mi_try_new_handler(nothrow)) { | |
| 516 | p = mi_heap_malloc(heap,size); | |
| 517 | } | |
| 518 | return p; | |
| 519 | } | |
| 520 | | |
| 521 | static mi_decl_noinline void* mi_try_new(size_t size, bool nothrow) { | |
| 522 | return mi_heap_try_new(mi_prim_get_default_heap(), size, nothrow); | |
| 523 | } | |
| 524 | | |
| 525 | | |
| 526 | mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) { | |
| 527 | void* p = mi_heap_malloc(heap,size); | |
| 528 | if mi_unlikely(p == NULL) return mi_heap_try_new(heap, size, false); | |
| 529 | return p; | |
| 530 | } | |
| 531 | | |
| 532 | mi_decl_nodiscard mi_decl_restrict void* mi_new(size_t size) { | |
| 533 | return mi_heap_alloc_new(mi_prim_get_default_heap(), size); | |
| 534 | } | |
| 535 | | |
| 536 | | |
| 537 | mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new_n(mi_heap_t* heap, size_t count, size_t size) { | |
| 538 | size_t total; | |
| 539 | if mi_unlikely(mi_count_size_overflow(count, size, &total)) { | |
| 540 | mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc | |
| 541 | return NULL; | |
| 542 | } | |
| 543 | else { | |
| 544 | return mi_heap_alloc_new(heap,total); | |
| 545 | } | |
| 546 | } | |
| 547 | | |
| 548 | mi_decl_nodiscard mi_decl_restrict void* mi_new_n(size_t count, size_t size) { | |
ce21e681Ian Davis1 years ago | 549 | return mi_heap_alloc_new_n(mi_prim_get_default_heap(), count, size); |
91589c3aIan Davis2 years ago | 550 | } |
| 551 | | |
| 552 | | |
| 553 | mi_decl_nodiscard mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept { | |
| 554 | void* p = mi_malloc(size); | |
| 555 | if mi_unlikely(p == NULL) return mi_try_new(size, true); | |
| 556 | return p; | |
| 557 | } | |
| 558 | | |
| 559 | mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) { | |
| 560 | void* p; | |
| 561 | do { | |
| 562 | p = mi_malloc_aligned(size, alignment); | |
| 563 | } | |
| 564 | while(p == NULL && mi_try_new_handler(false)); | |
| 565 | return p; | |
| 566 | } | |
| 567 | | |
| 568 | mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept { | |
| 569 | void* p; | |
| 570 | do { | |
| 571 | p = mi_malloc_aligned(size, alignment); | |
| 572 | } | |
| 573 | while(p == NULL && mi_try_new_handler(true)); | |
| 574 | return p; | |
| 575 | } | |
| 576 | | |
| 577 | mi_decl_nodiscard void* mi_new_realloc(void* p, size_t newsize) { | |
| 578 | void* q; | |
| 579 | do { | |
| 580 | q = mi_realloc(p, newsize); | |
| 581 | } while (q == NULL && mi_try_new_handler(false)); | |
| 582 | return q; | |
| 583 | } | |
| 584 | | |
| 585 | mi_decl_nodiscard void* mi_new_reallocn(void* p, size_t newcount, size_t size) { | |
| 586 | size_t total; | |
| 587 | if mi_unlikely(mi_count_size_overflow(newcount, size, &total)) { | |
| 588 | mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc | |
| 589 | return NULL; | |
| 590 | } | |
| 591 | else { | |
| 592 | return mi_new_realloc(p, total); | |
| 593 | } | |
| 594 | } | |
| 595 | | |
ce21e681Ian Davis1 years ago | 596 | #if MI_GUARDED |
| 597 | // We always allocate a guarded allocation at an offset (`mi_page_has_aligned` will be true). | |
| 598 | // We then set the first word of the block to `0` for regular offset aligned allocations (in `alloc-aligned.c`) | |
| 599 | // and the first word to `~0` for guarded allocations to have a correct `mi_usable_size` | |
| 600 | | |
| 601 | static void* mi_block_ptr_set_guarded(mi_block_t* block, size_t obj_size) { | |
| 602 | // TODO: we can still make padding work by moving it out of the guard page area | |
| 603 | mi_page_t* const page = _mi_ptr_page(block); | |
| 604 | mi_page_set_has_aligned(page, true); | |
| 605 | block->next = MI_BLOCK_TAG_GUARDED; | |
| 606 | | |
| 607 | // set guard page at the end of the block | |
| 608 | mi_segment_t* const segment = _mi_page_segment(page); | |
| 609 | const size_t block_size = mi_page_block_size(page); // must use `block_size` to match `mi_free_local` | |
| 610 | const size_t os_page_size = _mi_os_page_size(); | |
| 611 | mi_assert_internal(block_size >= obj_size + os_page_size + sizeof(mi_block_t)); | |
| 612 | if (block_size < obj_size + os_page_size + sizeof(mi_block_t)) { | |
| 613 | // should never happen | |
| 614 | mi_free(block); | |
| 615 | return NULL; | |
| 616 | } | |
| 617 | uint8_t* guard_page = (uint8_t*)block + block_size - os_page_size; | |
| 618 | mi_assert_internal(_mi_is_aligned(guard_page, os_page_size)); | |
| 619 | if (segment->allow_decommit && _mi_is_aligned(guard_page, os_page_size)) { | |
| 620 | _mi_os_protect(guard_page, os_page_size); | |
| 621 | } | |
| 622 | else { | |
| 623 | _mi_warning_message("unable to set a guard page behind an object due to pinned memory (large OS pages?) (object %p of size %zu)\n", block, block_size); | |
| 624 | } | |
| 625 | | |
| 626 | // align pointer just in front of the guard page | |
| 627 | size_t offset = block_size - os_page_size - obj_size; | |
| 628 | mi_assert_internal(offset > sizeof(mi_block_t)); | |
| 629 | if (offset > MI_BLOCK_ALIGNMENT_MAX) { | |
| 630 | // give up to place it right in front of the guard page if the offset is too large for unalignment | |
| 631 | offset = MI_BLOCK_ALIGNMENT_MAX; | |
| 632 | } | |
| 633 | void* p = (uint8_t*)block + offset; | |
| 634 | mi_track_align(block, p, offset, obj_size); | |
| 635 | mi_track_mem_defined(block, sizeof(mi_block_t)); | |
| 636 | return p; | |
| 637 | } | |
| 638 | | |
| 639 | mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept | |
| 640 | { | |
| 641 | #if defined(MI_PADDING_SIZE) | |
| 642 | mi_assert(MI_PADDING_SIZE==0); | |
| 643 | #endif | |
| 644 | // allocate multiple of page size ending in a guard page | |
| 645 | // ensure minimal alignment requirement? | |
| 646 | const size_t os_page_size = _mi_os_page_size(); | |
| 647 | const size_t obj_size = (mi_option_is_enabled(mi_option_guarded_precise) ? size : _mi_align_up(size, MI_MAX_ALIGN_SIZE)); | |
| 648 | const size_t bsize = _mi_align_up(_mi_align_up(obj_size, MI_MAX_ALIGN_SIZE) + sizeof(mi_block_t), MI_MAX_ALIGN_SIZE); | |
| 649 | const size_t req_size = _mi_align_up(bsize + os_page_size, os_page_size); | |
| 650 | mi_block_t* const block = (mi_block_t*)_mi_malloc_generic(heap, req_size, zero, 0 /* huge_alignment */); | |
| 651 | if (block==NULL) return NULL; | |
| 652 | void* const p = mi_block_ptr_set_guarded(block, obj_size); | |
| 653 | | |
| 654 | // stats | |
| 655 | mi_track_malloc(p, size, zero); | |
| 656 | if (p != NULL) { | |
| 657 | if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); } | |
| 658 | #if MI_STAT>1 | |
| 659 | mi_heap_stat_adjust_decrease(heap, malloc_requested, req_size); | |
| 660 | mi_heap_stat_increase(heap, malloc_requested, size); | |
| 661 | #endif | |
| 662 | _mi_stat_counter_increase(&heap->tld->stats.malloc_guarded_count, 1); | |
| 663 | } | |
| 664 | #if MI_DEBUG>3 | |
| 665 | if (p != NULL && zero) { | |
| 666 | mi_assert_expensive(mi_mem_is_zero(p, size)); | |
| 667 | } | |
| 668 | #endif | |
| 669 | return p; | |
| 670 | } | |
| 671 | #endif | |
| 672 | | |
91589c3aIan Davis2 years ago | 673 | // ------------------------------------------------------ |
| 674 | // ensure explicit external inline definitions are emitted! | |
| 675 | // ------------------------------------------------------ | |
| 676 | | |
| 677 | #ifdef __cplusplus | |
| 678 | void* _mi_externs[] = { | |
| 679 | (void*)&_mi_page_malloc, | |
ce21e681Ian Davis1 years ago | 680 | (void*)&_mi_page_malloc_zero, |
91589c3aIan Davis2 years ago | 681 | (void*)&_mi_heap_malloc_zero, |
| 682 | (void*)&_mi_heap_malloc_zero_ex, | |
| 683 | (void*)&mi_malloc, | |
| 684 | (void*)&mi_malloc_small, | |
| 685 | (void*)&mi_zalloc_small, | |
| 686 | (void*)&mi_heap_malloc, | |
| 687 | (void*)&mi_heap_zalloc, | |
| 688 | (void*)&mi_heap_malloc_small, | |
| 689 | // (void*)&mi_heap_alloc_new, | |
| 690 | // (void*)&mi_heap_alloc_new_n | |
| 691 | }; | |
| 692 | #endif |