2019-06-19 16:26:12 -07:00
/* ----------------------------------------------------------------------------
Copyright ( c ) 2018 , Microsoft Research , Daan Leijen
This is free software ; you can redistribute it and / or modify it under the
terms of the MIT license . A copy of the license can be found in the file
2019-06-23 19:53:34 +08:00
" LICENSE " at the root of this distribution .
2019-06-19 16:26:12 -07:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# pragma once
2019-06-26 12:57:13 +08:00
# ifndef MIMALLOC_INTERNAL_H
# define MIMALLOC_INTERNAL_H
2019-06-19 16:26:12 -07:00
# include "mimalloc-types.h"
2019-11-25 14:30:12 -08:00
# if defined(MI_MALLOC_OVERRIDE) && (defined(__APPLE__) || defined(__OpenBSD__) || defined(__DragonFly__))
2019-06-19 16:26:12 -07:00
# define MI_TLS_RECURSE_GUARD
# endif
2019-07-04 09:28:22 -07:00
# if (MI_DEBUG>0)
# define mi_trace_message(...) _mi_trace_message(__VA_ARGS__)
# else
2019-11-12 10:16:59 -08:00
# define mi_trace_message(...)
2019-07-04 09:28:22 -07:00
# endif
2020-01-24 19:02:13 -08:00
# define MI_CACHE_LINE 64
2019-10-18 18:11:04 -07:00
# if defined(_MSC_VER)
2020-01-23 11:06:25 -08:00
# pragma warning(disable:4127) // suppress constant conditional warning (due to MI_SECURE paths)
# define mi_decl_noinline __declspec(noinline)
# define mi_decl_thread __declspec(thread)
2020-01-24 19:02:13 -08:00
# define mi_decl_cache_align __declspec(align(MI_CACHE_LINE))
2020-01-23 10:31:47 -08:00
# elif (defined(__GNUC__) && (__GNUC__>=3)) // includes clang and icc
2020-01-23 11:06:25 -08:00
# define mi_decl_noinline __attribute__((noinline))
# define mi_decl_thread __thread
2020-01-24 19:02:13 -08:00
# define mi_decl_cache_align __attribute__((aligned(MI_CACHE_LINE)))
2019-10-18 18:11:04 -07:00
# else
# define mi_decl_noinline
2020-01-23 11:06:25 -08:00
# define mi_decl_thread __thread // hope for the best :-)
2020-01-29 22:46:44 -08:00
# define mi_decl_cache_align
2019-10-18 18:11:04 -07:00
# endif
2019-06-19 16:26:12 -07:00
// "options.c"
2020-01-15 10:53:54 -08:00
void _mi_fputs ( mi_output_fun * out , void * arg , const char * prefix , const char * message ) ;
void _mi_fprintf ( mi_output_fun * out , void * arg , const char * fmt , . . . ) ;
2019-06-19 16:26:12 -07:00
void _mi_warning_message ( const char * fmt , . . . ) ;
void _mi_verbose_message ( const char * fmt , . . . ) ;
2019-07-04 09:28:22 -07:00
void _mi_trace_message ( const char * fmt , . . . ) ;
2019-08-26 11:44:41 -07:00
void _mi_options_init ( void ) ;
2020-01-17 19:59:55 -08:00
void _mi_error_message ( int err , const char * fmt , . . . ) ;
2019-06-19 16:26:12 -07:00
2019-12-22 17:07:01 -08:00
// random.c
void _mi_random_init ( mi_random_ctx_t * ctx ) ;
void _mi_random_split ( mi_random_ctx_t * ctx , mi_random_ctx_t * new_ctx ) ;
uintptr_t _mi_random_next ( mi_random_ctx_t * ctx ) ;
uintptr_t _mi_heap_random_next ( mi_heap_t * heap ) ;
2020-01-29 22:46:44 -08:00
uintptr_t _os_random_weak ( uintptr_t extra_seed ) ;
2019-12-22 17:07:01 -08:00
static inline uintptr_t _mi_random_shuffle ( uintptr_t x ) ;
// init.c
2019-06-19 16:26:12 -07:00
extern mi_stats_t _mi_stats_main ;
extern const mi_page_t _mi_page_empty ;
2019-06-25 12:16:36 +02:00
bool _mi_is_main_thread ( void ) ;
2019-07-18 18:59:32 -07:00
bool _mi_preloading ( ) ; // true while the C runtime is not ready
2019-06-19 16:26:12 -07:00
2019-07-02 07:23:24 -07:00
// os.c
2019-06-25 12:16:36 +02:00
size_t _mi_os_page_size ( void ) ;
2019-07-02 07:23:24 -07:00
void _mi_os_init ( void ) ; // called from process init
void * _mi_os_alloc ( size_t size , mi_stats_t * stats ) ; // to allocate thread local data
void _mi_os_free ( void * p , size_t size , mi_stats_t * stats ) ; // to free thread local data
2019-09-10 13:26:51 -07:00
size_t _mi_os_good_alloc_size ( size_t size ) ;
2019-10-31 15:35:10 -07:00
2019-07-02 07:23:24 -07:00
// memory.c
2019-09-02 13:16:52 -07:00
void * _mi_mem_alloc_aligned ( size_t size , size_t alignment , bool * commit , bool * large , bool * is_zero , size_t * id , mi_os_tld_t * tld ) ;
2019-11-20 14:55:12 -08:00
void _mi_mem_free ( void * p , size_t size , size_t id , bool fully_committed , bool any_reset , mi_os_tld_t * tld ) ;
2019-07-02 07:23:24 -07:00
2019-11-04 11:48:41 -08:00
bool _mi_mem_reset ( void * p , size_t size , mi_os_tld_t * tld ) ;
bool _mi_mem_unreset ( void * p , size_t size , bool * is_zero , mi_os_tld_t * tld ) ;
bool _mi_mem_commit ( void * p , size_t size , bool * is_zero , mi_os_tld_t * tld ) ;
2019-07-02 07:23:24 -07:00
bool _mi_mem_protect ( void * addr , size_t size ) ;
bool _mi_mem_unprotect ( void * addr , size_t size ) ;
2019-06-19 16:26:12 -07:00
2019-11-04 11:48:41 -08:00
void _mi_mem_collect ( mi_os_tld_t * tld ) ;
2019-07-15 14:53:03 -07:00
2019-06-19 16:26:12 -07:00
// "segment.c"
2020-01-23 19:50:35 -08:00
mi_page_t * _mi_segment_page_alloc ( mi_heap_t * heap , size_t block_wsize , mi_segments_tld_t * tld , mi_os_tld_t * os_tld ) ;
2019-06-19 16:26:12 -07:00
void _mi_segment_page_free ( mi_page_t * page , bool force , mi_segments_tld_t * tld ) ;
void _mi_segment_page_abandon ( mi_page_t * page , mi_segments_tld_t * tld ) ;
2019-11-20 14:55:12 -08:00
uint8_t * _mi_segment_page_start ( const mi_segment_t * segment , const mi_page_t * page , size_t block_size , size_t * page_size , size_t * pre_size ) ; // page start for any page
2020-01-23 19:50:35 -08:00
void _mi_segment_thread_collect ( mi_segments_tld_t * tld ) ;
void _mi_abandoned_reclaim_all ( mi_heap_t * heap , mi_segments_tld_t * tld ) ;
void _mi_abandoned_await_readers ( void ) ;
2019-06-19 16:26:12 -07:00
// "page.c"
void * _mi_malloc_generic ( mi_heap_t * heap , size_t size ) mi_attr_noexcept mi_attr_malloc ;
void _mi_page_retire ( mi_page_t * page ) ; // free the page if there are no other pages with many free blocks
void _mi_page_unfull ( mi_page_t * page ) ;
void _mi_page_free ( mi_page_t * page , mi_page_queue_t * pq , bool force ) ; // free the page
void _mi_page_abandon ( mi_page_t * page , mi_page_queue_t * pq ) ; // abandon the page, to be picked up by another thread...
void _mi_heap_delayed_free ( mi_heap_t * heap ) ;
2020-01-03 21:39:18 -08:00
void _mi_heap_collect_retired ( mi_heap_t * heap , bool force ) ;
2019-06-19 16:26:12 -07:00
2020-01-08 19:00:03 -08:00
void _mi_page_use_delayed_free ( mi_page_t * page , mi_delayed_t delay , bool override_never ) ;
2019-06-27 13:29:55 -07:00
size_t _mi_page_queue_append ( mi_heap_t * heap , mi_page_queue_t * pq , mi_page_queue_t * append ) ;
2019-06-19 16:26:12 -07:00
void _mi_deferred_free ( mi_heap_t * heap , bool force ) ;
2019-08-14 07:46:38 -07:00
void _mi_page_free_collect ( mi_page_t * page , bool force ) ;
2019-06-19 16:26:12 -07:00
void _mi_page_reclaim ( mi_heap_t * heap , mi_page_t * page ) ; // callback from segments
size_t _mi_bin_size ( uint8_t bin ) ; // for stats
uint8_t _mi_bin ( size_t size ) ; // for stats
uint8_t _mi_bsr ( uintptr_t x ) ; // bit-scan-right, used on BSD in "os.c"
// "heap.c"
void _mi_heap_destroy_pages ( mi_heap_t * heap ) ;
void _mi_heap_collect_abandon ( mi_heap_t * heap ) ;
2019-11-13 17:22:03 -08:00
void _mi_heap_set_default_direct ( mi_heap_t * heap ) ;
2019-06-19 16:26:12 -07:00
// "stats.c"
void _mi_stats_done ( mi_stats_t * stats ) ;
2019-11-04 08:44:40 -08:00
mi_msecs_t _mi_clock_now ( void ) ;
mi_msecs_t _mi_clock_end ( mi_msecs_t start ) ;
mi_msecs_t _mi_clock_start ( void ) ;
2019-06-19 16:26:12 -07:00
// "alloc.c"
void * _mi_page_malloc ( mi_heap_t * heap , mi_page_t * page , size_t size ) mi_attr_noexcept ; // called from `_mi_malloc_generic`
void * _mi_heap_malloc_zero ( mi_heap_t * heap , size_t size , bool zero ) ;
2019-07-08 17:17:07 -07:00
void * _mi_heap_realloc_zero ( mi_heap_t * heap , void * p , size_t newsize , bool zero ) ;
2019-07-08 12:00:59 -07:00
mi_block_t * _mi_page_ptr_unalign ( const mi_segment_t * segment , const mi_page_t * page , const void * p ) ;
2019-07-14 16:48:53 -07:00
bool _mi_free_delayed_block ( mi_block_t * block ) ;
2019-09-03 10:11:24 -07:00
void _mi_block_zero_init ( const mi_page_t * page , void * p , size_t size ) ;
2019-06-19 16:26:12 -07:00
# if MI_DEBUG>1
bool _mi_page_is_valid ( mi_page_t * page ) ;
# endif
// ------------------------------------------------------
// Branches
// ------------------------------------------------------
# if defined(__GNUC__) || defined(__clang__)
# define mi_unlikely(x) __builtin_expect((x),0)
# define mi_likely(x) __builtin_expect((x),1)
# else
# define mi_unlikely(x) (x)
# define mi_likely(x) (x)
# endif
2019-07-07 12:56:40 +08:00
# ifndef __has_builtin
# define __has_builtin(x) 0
# endif
2019-06-19 16:26:12 -07:00
2020-01-17 19:59:55 -08:00
/* -----------------------------------------------------------
Error codes passed to ` _mi_fatal_error `
All are recoverable but EFAULT is a serious error and aborts by default in secure mode .
For portability define undefined error codes using common Unix codes :
< https : //www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include <errno.h>
# ifndef EAGAIN // double free
# define EAGAIN (11)
# endif
# ifndef ENOMEM // out of memory
# define ENOMEM (12)
# endif
# ifndef EFAULT // corrupted free-list or meta-data
# define EFAULT (14)
# endif
# ifndef EINVAL // trying to free an invalid pointer
# define EINVAL (22)
# endif
# ifndef EOVERFLOW // count*size overflow
# define EOVERFLOW (75)
# endif
2019-06-19 16:26:12 -07:00
/* -----------------------------------------------------------
Inlined definitions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# define UNUSED(x) (void)(x)
2019-11-12 10:16:59 -08:00
# if (MI_DEBUG>0)
# define UNUSED_RELEASE(x)
2019-07-04 09:28:22 -07:00
# else
# define UNUSED_RELEASE(x) UNUSED(x)
# endif
2019-06-19 16:26:12 -07:00
# define MI_INIT4(x) x(),x(),x(),x()
# define MI_INIT8(x) MI_INIT4(x),MI_INIT4(x)
# define MI_INIT16(x) MI_INIT8(x),MI_INIT8(x)
# define MI_INIT32(x) MI_INIT16(x),MI_INIT16(x)
# define MI_INIT64(x) MI_INIT32(x),MI_INIT32(x)
# define MI_INIT128(x) MI_INIT64(x),MI_INIT64(x)
# define MI_INIT256(x) MI_INIT128(x),MI_INIT128(x)
2019-09-11 17:49:28 -07:00
// Is `x` a power of two? (0 is considered a power of two)
2019-09-09 08:12:50 -07:00
static inline bool _mi_is_power_of_two ( uintptr_t x ) {
2019-07-22 20:51:12 -07:00
return ( ( x & ( x - 1 ) ) = = 0 ) ;
}
2019-09-09 08:12:50 -07:00
// Align upwards
2019-07-22 20:51:12 -07:00
static inline uintptr_t _mi_align_up ( uintptr_t sz , size_t alignment ) {
2019-11-07 10:26:52 -08:00
mi_assert_internal ( alignment ! = 0 ) ;
2019-07-22 20:51:12 -07:00
uintptr_t mask = alignment - 1 ;
if ( ( alignment & mask ) = = 0 ) { // power of two?
return ( ( sz + mask ) & ~ mask ) ;
}
else {
return ( ( ( sz + mask ) / alignment ) * alignment ) ;
}
}
2019-11-07 10:26:52 -08:00
// Divide upwards: `s <= _mi_divide_up(s,d)*d < s+d`.
static inline uintptr_t _mi_divide_up ( uintptr_t size , size_t divider ) {
mi_assert_internal ( divider ! = 0 ) ;
return ( divider = = 0 ? size : ( ( size + divider - 1 ) / divider ) ) ;
}
2019-09-03 10:11:24 -07:00
// Is memory zero initialized?
static inline bool mi_mem_is_zero ( void * p , size_t size ) {
for ( size_t i = 0 ; i < size ; i + + ) {
if ( ( ( uint8_t * ) p ) [ i ] ! = 0 ) return false ;
}
return true ;
}
2019-06-19 16:26:12 -07:00
// Align a byte size to a size in _machine words_,
// i.e. byte size == `wsize*sizeof(void*)`.
static inline size_t _mi_wsize_from_size ( size_t size ) {
2019-06-27 13:29:55 -07:00
mi_assert_internal ( size < = SIZE_MAX - sizeof ( uintptr_t ) ) ;
2019-06-19 16:26:12 -07:00
return ( size + sizeof ( uintptr_t ) - 1 ) / sizeof ( uintptr_t ) ;
}
2019-09-03 10:11:24 -07:00
2020-01-17 19:59:55 -08:00
// Overflow detecting multiply
2020-01-29 22:46:44 -08:00
static inline bool mi_mul_overflow ( size_t count , size_t size , size_t * total ) {
2020-01-17 19:59:55 -08:00
# if __has_builtin(__builtin_umul_overflow) || __GNUC__ >= 5
# include <limits.h> // UINT_MAX, ULONG_MAX
# if (SIZE_MAX == UINT_MAX)
return __builtin_umul_overflow ( count , size , total ) ;
# elif (SIZE_MAX == ULONG_MAX)
return __builtin_umull_overflow ( count , size , total ) ;
# else
return __builtin_umulll_overflow ( count , size , total ) ;
# endif
# else /* __builtin_umul_overflow is unavailable */
# define MI_MUL_NO_OVERFLOW ((size_t)1 << (4*sizeof(size_t))) // sqrt(SIZE_MAX)
* total = count * size ;
return ( ( size > = MI_MUL_NO_OVERFLOW | | count > = MI_MUL_NO_OVERFLOW )
& & size > 0 & & ( SIZE_MAX / size ) < count ) ;
# endif
}
// Safe multiply `count*size` into `total`; return `true` on overflow.
static inline bool mi_count_size_overflow ( size_t count , size_t size , size_t * total ) {
if ( count = = 1 ) { // quick check for the case where count is one (common for C++ allocators)
* total = size ;
return false ;
}
else if ( mi_unlikely ( mi_mul_overflow ( count , size , total ) ) ) {
_mi_error_message ( EOVERFLOW , " allocation request too large (%zu * %zu bytes) \n " , count , size ) ;
* total = SIZE_MAX ;
return true ;
}
else return false ;
}
2019-09-03 10:11:24 -07:00
/* -----------------------------------------------------------
The thread local default heap
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2019-06-19 16:26:12 -07:00
extern const mi_heap_t _mi_heap_empty ; // read-only empty heap, initial value of the thread local default heap
extern mi_heap_t _mi_heap_main ; // statically allocated main backing heap
extern bool _mi_process_is_initialized ;
2020-01-29 23:08:12 -08:00
extern mi_decl_thread mi_heap_t * _mi_heap_default ; // default heap to allocate from
2019-06-19 16:26:12 -07:00
# ifdef MI_TLS_RECURSE_GUARD
2020-01-29 22:46:44 -08:00
extern mi_heap_t * _mi_get_default_heap_tls_safe ( void ) ;
2020-01-29 23:08:12 -08:00
extern size_t _mi_tls_recurse ;
# endif
2020-01-29 22:46:44 -08:00
static inline mi_heap_t * mi_get_default_heap ( void ) {
2020-01-29 23:08:12 -08:00
# ifdef MI_TLS_RECURSE_GUARD
if ( _mi_tls_recurse + + > 100 ) {
2019-09-01 01:06:01 -07:00
// on some BSD platforms, like macOS, the dynamic loader calls `malloc`
2019-06-19 16:26:12 -07:00
// to initialize thread local data. To avoid recursion, we need to avoid
// accessing the thread local `_mi_default_heap` until our module is loaded
// and use the statically allocated main heap until that time.
// TODO: patch ourselves dynamically to avoid this check every time?
2020-01-29 23:08:12 -08:00
mi_heap_t * heap = _mi_get_default_heap_tls_safe ( ) ;
_mi_tls_recurse = 0 ;
return heap ;
}
# endif
2019-06-22 11:50:03 -07:00
return _mi_heap_default ;
2019-06-19 16:26:12 -07:00
}
static inline bool mi_heap_is_default ( const mi_heap_t * heap ) {
return ( heap = = mi_get_default_heap ( ) ) ;
}
static inline bool mi_heap_is_backing ( const mi_heap_t * heap ) {
return ( heap - > tld - > heap_backing = = heap ) ;
}
static inline bool mi_heap_is_initialized ( mi_heap_t * heap ) {
mi_assert_internal ( heap ! = NULL ) ;
return ( heap ! = & _mi_heap_empty ) ;
}
2019-10-19 08:34:18 -07:00
static inline uintptr_t _mi_ptr_cookie ( const void * p ) {
2020-01-29 22:46:44 -08:00
mi_assert_internal ( _mi_heap_main . cookie ! = 0 ) ;
2019-10-19 08:34:18 -07:00
return ( ( uintptr_t ) p ^ _mi_heap_main . cookie ) ;
}
2019-09-03 10:11:24 -07:00
/* -----------------------------------------------------------
Pages
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2019-06-19 16:26:12 -07:00
static inline mi_page_t * _mi_heap_get_free_small_page ( mi_heap_t * heap , size_t size ) {
mi_assert_internal ( size < = MI_SMALL_SIZE_MAX ) ;
return heap - > pages_free_direct [ _mi_wsize_from_size ( size ) ] ;
}
// Get the page belonging to a certain size class
static inline mi_page_t * _mi_get_free_small_page ( size_t size ) {
return _mi_heap_get_free_small_page ( mi_get_default_heap ( ) , size ) ;
}
// Segment that contains the pointer
static inline mi_segment_t * _mi_ptr_segment ( const void * p ) {
// mi_assert_internal(p != NULL);
return ( mi_segment_t * ) ( ( uintptr_t ) p & ~ MI_SEGMENT_MASK ) ;
}
// Segment belonging to a page
static inline mi_segment_t * _mi_page_segment ( const mi_page_t * page ) {
mi_segment_t * segment = _mi_ptr_segment ( page ) ;
2019-06-27 13:29:55 -07:00
mi_assert_internal ( segment = = NULL | | page = = & segment - > pages [ page - > segment_idx ] ) ;
2019-06-19 16:26:12 -07:00
return segment ;
}
2019-11-21 15:21:23 -08:00
// used internally
static inline uintptr_t _mi_segment_page_idx_of ( const mi_segment_t * segment , const void * p ) {
2019-06-19 16:26:12 -07:00
// if (segment->page_size > MI_SEGMENT_SIZE) return &segment->pages[0]; // huge pages
ptrdiff_t diff = ( uint8_t * ) p - ( uint8_t * ) segment ;
2019-11-07 10:26:52 -08:00
mi_assert_internal ( diff > = 0 & & ( size_t ) diff < MI_SEGMENT_SIZE ) ;
2019-06-19 16:26:12 -07:00
uintptr_t idx = ( uintptr_t ) diff > > segment - > page_shift ;
mi_assert_internal ( idx < segment - > capacity ) ;
2019-07-12 20:11:39 -07:00
mi_assert_internal ( segment - > page_kind < = MI_PAGE_MEDIUM | | idx = = 0 ) ;
2019-11-21 15:21:23 -08:00
return idx ;
}
// Get the page containing the pointer
static inline mi_page_t * _mi_segment_page_of ( const mi_segment_t * segment , const void * p ) {
2020-01-29 22:46:44 -08:00
uintptr_t idx = _mi_segment_page_idx_of ( segment , p ) ;
2019-06-19 16:26:12 -07:00
return & ( ( mi_segment_t * ) segment ) - > pages [ idx ] ;
}
// Quick page start for initialized pages
static inline uint8_t * _mi_page_start ( const mi_segment_t * segment , const mi_page_t * page , size_t * page_size ) {
2020-01-15 17:19:01 -08:00
const size_t bsize = page - > xblock_size ;
2019-11-20 14:55:12 -08:00
mi_assert_internal ( bsize > 0 & & ( bsize % sizeof ( void * ) ) = = 0 ) ;
return _mi_segment_page_start ( segment , page , bsize , page_size , NULL ) ;
2019-06-19 16:26:12 -07:00
}
// Get the page containing the pointer
static inline mi_page_t * _mi_ptr_page ( void * p ) {
return _mi_segment_page_of ( _mi_ptr_segment ( p ) , p ) ;
}
2020-01-15 17:19:01 -08:00
// Get the block size of a page (special cased for huge objects)
static inline size_t mi_page_block_size ( const mi_page_t * page ) {
const size_t bsize = page - > xblock_size ;
mi_assert_internal ( bsize > 0 ) ;
if ( mi_likely ( bsize < MI_HUGE_BLOCK_SIZE ) ) {
return bsize ;
}
else {
size_t psize ;
_mi_segment_page_start ( _mi_page_segment ( page ) , page , bsize , & psize , NULL ) ;
return psize ;
}
}
2019-07-14 16:20:27 -07:00
// Thread free access
2020-01-15 17:19:01 -08:00
static inline mi_block_t * mi_page_thread_free ( const mi_page_t * page ) {
return ( mi_block_t * ) ( mi_atomic_read_relaxed ( & page - > xthread_free ) & ~ 3 ) ;
}
static inline mi_delayed_t mi_page_thread_free_flag ( const mi_page_t * page ) {
return ( mi_delayed_t ) ( mi_atomic_read_relaxed ( & page - > xthread_free ) & 3 ) ;
}
// Heap access
static inline mi_heap_t * mi_page_heap ( const mi_page_t * page ) {
return ( mi_heap_t * ) ( mi_atomic_read_relaxed ( & page - > xheap ) ) ;
}
static inline void mi_page_set_heap ( mi_page_t * page , mi_heap_t * heap ) {
mi_assert_internal ( mi_page_thread_free_flag ( page ) ! = MI_DELAYED_FREEING ) ;
mi_atomic_write ( & page - > xheap , ( uintptr_t ) heap ) ;
}
// Thread free flag helpers
2019-07-14 16:20:27 -07:00
static inline mi_block_t * mi_tf_block ( mi_thread_free_t tf ) {
return ( mi_block_t * ) ( tf & ~ 0x03 ) ;
}
static inline mi_delayed_t mi_tf_delayed ( mi_thread_free_t tf ) {
return ( mi_delayed_t ) ( tf & 0x03 ) ;
}
static inline mi_thread_free_t mi_tf_make ( mi_block_t * block , mi_delayed_t delayed ) {
return ( mi_thread_free_t ) ( ( uintptr_t ) block | ( uintptr_t ) delayed ) ;
}
static inline mi_thread_free_t mi_tf_set_delayed ( mi_thread_free_t tf , mi_delayed_t delayed ) {
return mi_tf_make ( mi_tf_block ( tf ) , delayed ) ;
}
static inline mi_thread_free_t mi_tf_set_block ( mi_thread_free_t tf , mi_block_t * block ) {
return mi_tf_make ( block , mi_tf_delayed ( tf ) ) ;
}
2020-01-29 22:46:44 -08:00
// are all blocks in a page freed?
2020-01-26 12:39:11 -08:00
// note: needs up-to-date used count, (as the `xthread_free` list may not be empty). see `_mi_page_collect_free`.
2019-06-19 16:26:12 -07:00
static inline bool mi_page_all_free ( const mi_page_t * page ) {
mi_assert_internal ( page ! = NULL ) ;
2020-01-15 17:19:01 -08:00
return ( page - > used = = 0 ) ;
2019-06-19 16:26:12 -07:00
}
2020-01-29 22:46:44 -08:00
// are there any available blocks?
2020-01-26 12:39:11 -08:00
static inline bool mi_page_has_any_available ( const mi_page_t * page ) {
mi_assert_internal ( page ! = NULL & & page - > reserved > 0 ) ;
return ( page - > used < page - > reserved | | ( mi_page_thread_free ( page ) ! = NULL ) ) ;
2019-06-19 16:26:12 -07:00
}
2020-01-26 12:39:11 -08:00
// are there immediately available blocks, i.e. blocks available on the free list.
static inline bool mi_page_immediate_available ( const mi_page_t * page ) {
2019-06-19 16:26:12 -07:00
mi_assert_internal ( page ! = NULL ) ;
2020-01-26 12:39:11 -08:00
return ( page - > free ! = NULL ) ;
2019-06-19 16:26:12 -07:00
}
// is more than 7/8th of a page in use?
static inline bool mi_page_mostly_used ( const mi_page_t * page ) {
if ( page = = NULL ) return true ;
uint16_t frac = page - > reserved / 8U ;
2020-01-15 17:19:01 -08:00
return ( page - > reserved - page - > used < = frac ) ;
2019-06-19 16:26:12 -07:00
}
static inline mi_page_queue_t * mi_page_queue ( const mi_heap_t * heap , size_t size ) {
return & ( ( mi_heap_t * ) heap ) - > pages [ _mi_bin ( size ) ] ;
}
2019-08-10 17:48:00 -07:00
2019-08-23 14:08:00 -07:00
2019-08-10 17:48:00 -07:00
//-----------------------------------------------------------
// Page flags
//-----------------------------------------------------------
static inline bool mi_page_is_in_full ( const mi_page_t * page ) {
2019-10-17 16:48:16 -07:00
return page - > flags . x . in_full ;
2019-08-10 17:48:00 -07:00
}
static inline void mi_page_set_in_full ( mi_page_t * page , bool in_full ) {
2019-10-17 16:48:16 -07:00
page - > flags . x . in_full = in_full ;
2019-08-08 15:23:18 -07:00
}
2019-08-10 17:48:00 -07:00
static inline bool mi_page_has_aligned ( const mi_page_t * page ) {
2019-10-17 16:48:16 -07:00
return page - > flags . x . has_aligned ;
2019-08-10 17:48:00 -07:00
}
static inline void mi_page_set_has_aligned ( mi_page_t * page , bool has_aligned ) {
2019-10-17 16:48:16 -07:00
page - > flags . x . has_aligned = has_aligned ;
2019-08-10 17:48:00 -07:00
}
2019-12-27 23:33:50 -08:00
/* -------------------------------------------------------------------
Encoding / Decoding the free list next pointers
2020-01-29 22:46:44 -08:00
This is to protect against buffer overflow exploits where the
free list is mutated . Many hardened allocators xor the next pointer ` p `
2019-12-28 15:17:49 -08:00
with a secret key ` k1 ` , as ` p ^ k1 ` . This prevents overwriting with known
2020-01-29 22:46:44 -08:00
values but might be still too weak : if the attacker can guess
the pointer ` p ` this can reveal ` k1 ` ( since ` p ^ k1 ^ p = = k1 ` ) .
2019-12-28 15:17:49 -08:00
Moreover , if multiple blocks can be read as well , the attacker can
2019-12-27 23:33:50 -08:00
xor both as ` ( p1 ^ k1 ) ^ ( p2 ^ k1 ) = = p1 ^ p2 ` which may reveal a lot
about the pointers ( and subsequently ` k1 ` ) .
2019-12-28 15:17:49 -08:00
Instead mimalloc uses an extra key ` k2 ` and encodes as ` ( ( p ^ k2 ) < < < k1 ) + k1 ` .
2019-12-27 23:33:50 -08:00
Since these operations are not associative , the above approaches do not
2019-12-28 15:17:49 -08:00
work so well any more even if the ` p ` can be guesstimated . For example ,
2020-01-29 22:46:44 -08:00
for the read case we can subtract two entries to discard the ` + k1 ` term ,
2019-12-28 15:17:49 -08:00
but that leads to ` ( ( p1 ^ k2 ) < < < k1 ) - ( ( p2 ^ k2 ) < < < k1 ) ` at best .
2020-01-29 22:46:44 -08:00
We include the left - rotation since xor and addition are otherwise linear
2019-12-28 15:17:49 -08:00
in the lowest bit . Finally , both keys are unique per page which reduces
the re - use of keys by a large factor .
2019-12-27 23:33:50 -08:00
We also pass a separate ` null ` value to be used as ` NULL ` or otherwise
2019-12-28 15:17:49 -08:00
` ( k2 < < < k1 ) + k1 ` would appear ( too ) often as a sentinel value .
2019-12-27 23:33:50 -08:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2019-10-18 18:11:04 -07:00
static inline bool mi_is_in_same_segment ( const void * p , const void * q ) {
return ( _mi_ptr_segment ( p ) = = _mi_ptr_segment ( q ) ) ;
}
2019-11-21 15:21:23 -08:00
static inline bool mi_is_in_same_page ( const void * p , const void * q ) {
mi_segment_t * segmentp = _mi_ptr_segment ( p ) ;
mi_segment_t * segmentq = _mi_ptr_segment ( q ) ;
if ( segmentp ! = segmentq ) return false ;
uintptr_t idxp = _mi_segment_page_idx_of ( segmentp , p ) ;
uintptr_t idxq = _mi_segment_page_idx_of ( segmentq , q ) ;
return ( idxp = = idxq ) ;
}
2019-12-27 23:33:50 -08:00
static inline uintptr_t mi_rotl ( uintptr_t x , uintptr_t shift ) {
2019-12-28 15:17:49 -08:00
shift % = MI_INTPTR_BITS ;
2019-12-27 23:33:50 -08:00
return ( ( x < < shift ) | ( x > > ( MI_INTPTR_BITS - shift ) ) ) ;
}
static inline uintptr_t mi_rotr ( uintptr_t x , uintptr_t shift ) {
2019-12-28 15:17:49 -08:00
shift % = MI_INTPTR_BITS ;
2019-12-27 23:33:50 -08:00
return ( ( x > > shift ) | ( x < < ( MI_INTPTR_BITS - shift ) ) ) ;
}
2019-12-28 15:17:49 -08:00
2019-12-27 23:33:50 -08:00
static inline mi_block_t * mi_block_nextx ( const void * null , const mi_block_t * block , uintptr_t key1 , uintptr_t key2 ) {
2019-10-28 15:54:33 -07:00
# ifdef MI_ENCODE_FREELIST
2019-12-28 15:17:49 -08:00
mi_block_t * b = ( mi_block_t * ) ( mi_rotr ( block - > next - key1 , key1 ) ^ key2 ) ;
2019-11-21 15:21:23 -08:00
if ( mi_unlikely ( ( void * ) b = = null ) ) { b = NULL ; }
return b ;
2019-06-19 16:26:12 -07:00
# else
2019-12-27 23:33:50 -08:00
UNUSED ( key1 ) ; UNUSED ( key2 ) ; UNUSED ( null ) ;
2019-06-19 16:26:12 -07:00
return ( mi_block_t * ) block - > next ;
# endif
}
2019-12-27 23:33:50 -08:00
static inline void mi_block_set_nextx ( const void * null , mi_block_t * block , const mi_block_t * next , uintptr_t key1 , uintptr_t key2 ) {
2019-10-28 15:54:33 -07:00
# ifdef MI_ENCODE_FREELIST
2019-11-21 15:21:23 -08:00
if ( mi_unlikely ( next = = NULL ) ) { next = ( mi_block_t * ) null ; }
2019-12-28 15:17:49 -08:00
block - > next = mi_rotl ( ( uintptr_t ) next ^ key2 , key1 ) + key1 ;
2019-06-19 16:26:12 -07:00
# else
2019-12-27 23:33:50 -08:00
UNUSED ( key1 ) ; UNUSED ( key2 ) ; UNUSED ( null ) ;
2019-06-19 16:26:12 -07:00
block - > next = ( mi_encoded_t ) next ;
# endif
}
2019-10-18 18:11:04 -07:00
static inline mi_block_t * mi_block_next ( const mi_page_t * page , const mi_block_t * block ) {
2019-10-28 15:54:33 -07:00
# ifdef MI_ENCODE_FREELIST
2019-12-27 23:33:50 -08:00
mi_block_t * next = mi_block_nextx ( page , block , page - > key [ 0 ] , page - > key [ 1 ] ) ;
// check for free list corruption: is `next` at least in the same page?
2019-11-21 15:21:23 -08:00
// TODO: check if `next` is `page->block_size` aligned?
2019-12-27 23:33:50 -08:00
if ( mi_unlikely ( next ! = NULL & & ! mi_is_in_same_page ( block , next ) ) ) {
2020-01-17 19:59:55 -08:00
_mi_error_message ( EFAULT , " corrupted free list entry of size %zub at %p: value 0x%zx \n " , mi_page_block_size ( page ) , block , ( uintptr_t ) next ) ;
2019-10-28 15:54:33 -07:00
next = NULL ;
2019-11-12 10:16:59 -08:00
}
2019-10-18 18:11:04 -07:00
return next ;
2019-07-22 20:51:12 -07:00
# else
UNUSED ( page ) ;
2019-12-27 23:33:50 -08:00
return mi_block_nextx ( page , block , 0 , 0 ) ;
2019-07-22 20:51:12 -07:00
# endif
2019-06-19 16:26:12 -07:00
}
2019-10-18 18:11:04 -07:00
static inline void mi_block_set_next ( const mi_page_t * page , mi_block_t * block , const mi_block_t * next ) {
2019-10-28 15:54:33 -07:00
# ifdef MI_ENCODE_FREELIST
2019-12-27 23:33:50 -08:00
mi_block_set_nextx ( page , block , next , page - > key [ 0 ] , page - > key [ 1 ] ) ;
2019-07-22 20:51:12 -07:00
# else
UNUSED ( page ) ;
2019-12-27 23:33:50 -08:00
mi_block_set_nextx ( page , block , next , 0 , 0 ) ;
2019-07-22 20:51:12 -07:00
# endif
2019-06-19 16:26:12 -07:00
}
2019-07-22 20:51:12 -07:00
2019-12-22 17:07:01 -08:00
// -------------------------------------------------------------------
// Fast "random" shuffle
// -------------------------------------------------------------------
static inline uintptr_t _mi_random_shuffle ( uintptr_t x ) {
2019-12-28 15:17:49 -08:00
if ( x = = 0 ) { x = 17 ; } // ensure we don't get stuck in generating zeros
2019-12-22 17:07:01 -08:00
# if (MI_INTPTR_SIZE==8)
// by Sebastiano Vigna, see: <http://xoshiro.di.unimi.it/splitmix64.c>
x ^ = x > > 30 ;
x * = 0xbf58476d1ce4e5b9UL ;
x ^ = x > > 27 ;
x * = 0x94d049bb133111ebUL ;
x ^ = x > > 31 ;
# elif (MI_INTPTR_SIZE==4)
// by Chris Wellons, see: <https://nullprogram.com/blog/2018/07/31/>
x ^ = x > > 16 ;
x * = 0x7feb352dUL ;
x ^ = x > > 15 ;
x * = 0x846ca68bUL ;
x ^ = x > > 16 ;
# endif
return x ;
}
2019-11-12 10:16:59 -08:00
// -------------------------------------------------------------------
// Optimize numa node access for the common case (= one node)
// -------------------------------------------------------------------
2019-11-13 13:35:50 -08:00
int _mi_os_numa_node_get ( mi_os_tld_t * tld ) ;
size_t _mi_os_numa_node_count_get ( void ) ;
2019-11-12 10:16:59 -08:00
2019-11-13 13:35:50 -08:00
extern size_t _mi_numa_node_count ;
2019-11-12 10:16:59 -08:00
static inline int _mi_os_numa_node ( mi_os_tld_t * tld ) {
if ( mi_likely ( _mi_numa_node_count = = 1 ) ) return 0 ;
else return _mi_os_numa_node_get ( tld ) ;
}
2019-11-13 13:35:50 -08:00
static inline size_t _mi_os_numa_node_count ( void ) {
2019-11-12 10:16:59 -08:00
if ( mi_likely ( _mi_numa_node_count > 0 ) ) return _mi_numa_node_count ;
else return _mi_os_numa_node_count_get ( ) ;
}
2019-06-19 16:26:12 -07:00
// -------------------------------------------------------------------
// Getting the thread id should be performant
// as it is called in the fast path of `_mi_free`,
// so we specialize for various platforms.
// -------------------------------------------------------------------
# if defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
2019-06-25 12:16:36 +02:00
static inline uintptr_t _mi_thread_id ( void ) mi_attr_noexcept {
2019-06-19 16:26:12 -07:00
// Windows: works on Intel and ARM in both 32- and 64-bit
2019-06-25 20:06:34 -07:00
return ( uintptr_t ) NtCurrentTeb ( ) ;
2019-06-19 16:26:12 -07:00
}
2019-06-23 16:25:33 +08:00
# elif (defined(__GNUC__) || defined(__clang__)) && \
( defined ( __x86_64__ ) | | defined ( __i386__ ) | | defined ( __arm__ ) | | defined ( __aarch64__ ) )
2019-06-19 16:26:12 -07:00
// TLS register on x86 is in the FS or GS register
// see: https://akkadia.org/drepper/tls.pdf
2019-06-25 12:16:36 +02:00
static inline uintptr_t _mi_thread_id ( void ) mi_attr_noexcept {
2019-06-19 16:26:12 -07:00
uintptr_t tid ;
# if defined(__i386__)
__asm__ ( " movl %%gs:0, %0 " : " =r " ( tid ) : : ) ; // 32-bit always uses GS
# elif defined(__MACH__)
2019-06-23 15:29:41 +08:00
__asm__ ( " movq %%gs:0, %0 " : " =r " ( tid ) : : ) ; // x86_64 macOS uses GS
2019-06-23 16:25:33 +08:00
# elif defined(__x86_64__)
2019-06-19 16:26:12 -07:00
__asm__ ( " movq %%fs:0, %0 " : " =r " ( tid ) : : ) ; // x86_64 Linux, BSD uses FS
2019-06-23 16:25:33 +08:00
# elif defined(__arm__)
asm volatile ( " mrc p15, 0, %0, c13, c0, 3 " : " =r " ( tid ) ) ;
# elif defined(__aarch64__)
asm volatile ( " mrs %0, tpidr_el0 " : " =r " ( tid ) ) ;
2019-06-19 16:26:12 -07:00
# endif
return tid ;
}
# else
// otherwise use standard C
2019-06-25 12:16:36 +02:00
static inline uintptr_t _mi_thread_id ( void ) mi_attr_noexcept {
2019-06-22 11:33:18 -07:00
return ( uintptr_t ) & _mi_heap_default ;
2019-06-19 16:26:12 -07:00
}
# endif
# endif