| 
									
										
										
										
											2023-10-30 08:43:11 -07:00
										 |  |  | /* ----------------------------------------------------------------------------
 | 
					
						
							|  |  |  | Copyright (c) 2018-2023, 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 | 
					
						
							|  |  |  | "LICENSE" at the root of this distribution. | 
					
						
							|  |  |  | -----------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This file is included in `src/prim/prim.c`
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "mimalloc.h"
 | 
					
						
							|  |  |  | #include "mimalloc/internal.h"
 | 
					
						
							|  |  |  | #include "mimalloc/atomic.h"
 | 
					
						
							|  |  |  | #include "mimalloc/prim.h"
 | 
					
						
							| 
									
										
										
										
											2023-10-30 23:10:22 +01:00
										 |  |  | #include <unistd.h>               // sbrk()
 | 
					
						
							| 
									
										
										
										
											2023-10-30 08:43:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | // Initialize
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void _mi_prim_mem_init( mi_os_mem_config_t* config ) { | 
					
						
							|  |  |  |   config->page_size = 64*MI_KiB; // WebAssembly has a fixed page size: 64KiB
 | 
					
						
							|  |  |  |   config->alloc_granularity = 16; | 
					
						
							|  |  |  |   config->has_overcommit = false; | 
					
						
							|  |  |  |   config->must_free_whole = true; | 
					
						
							|  |  |  |   config->has_virtual_reserve = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | // Free
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int _mi_prim_free(void* addr, size_t size ) { | 
					
						
							|  |  |  |   MI_UNUSED(addr); MI_UNUSED(size); | 
					
						
							|  |  |  |   // wasi heap cannot be shrunk
 | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | // Allocation: sbrk or memory_grow
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(MI_USE_SBRK)
 | 
					
						
							|  |  |  |   static void* mi_memory_grow( size_t size ) { | 
					
						
							|  |  |  |     void* p = sbrk(size); | 
					
						
							|  |  |  |     if (p == (void*)(-1)) return NULL; | 
					
						
							|  |  |  |     #if !defined(__wasi__) // on wasi this is always zero initialized already (?)
 | 
					
						
							|  |  |  |     memset(p,0,size); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  |     return p; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | #elif defined(__wasi__)
 | 
					
						
							|  |  |  |   static void* mi_memory_grow( size_t size ) { | 
					
						
							|  |  |  |     size_t base = (size > 0 ? __builtin_wasm_memory_grow(0,_mi_divide_up(size, _mi_os_page_size())) | 
					
						
							|  |  |  |                             : __builtin_wasm_memory_size(0)); | 
					
						
							|  |  |  |     if (base == SIZE_MAX) return NULL; | 
					
						
							|  |  |  |     return (void*)(base * _mi_os_page_size()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(MI_USE_PTHREADS)
 | 
					
						
							|  |  |  | static pthread_mutex_t mi_heap_grow_mutex = PTHREAD_MUTEX_INITIALIZER; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void* mi_prim_mem_grow(size_t size, size_t try_alignment) { | 
					
						
							|  |  |  |   void* p = NULL; | 
					
						
							|  |  |  |   if (try_alignment <= 1) { | 
					
						
							|  |  |  |     // `sbrk` is not thread safe in general so try to protect it (we could skip this on WASM but leave it in for now)
 | 
					
						
							|  |  |  |     #if defined(MI_USE_PTHREADS)
 | 
					
						
							|  |  |  |     pthread_mutex_lock(&mi_heap_grow_mutex); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  |     p = mi_memory_grow(size); | 
					
						
							|  |  |  |     #if defined(MI_USE_PTHREADS)
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&mi_heap_grow_mutex); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     void* base = NULL; | 
					
						
							|  |  |  |     size_t alloc_size = 0; | 
					
						
							|  |  |  |     // to allocate aligned use a lock to try to avoid thread interaction
 | 
					
						
							|  |  |  |     // between getting the current size and actual allocation
 | 
					
						
							|  |  |  |     // (also, `sbrk` is not thread safe in general)
 | 
					
						
							|  |  |  |     #if defined(MI_USE_PTHREADS)
 | 
					
						
							|  |  |  |     pthread_mutex_lock(&mi_heap_grow_mutex); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       void* current = mi_memory_grow(0);  // get current size
 | 
					
						
							|  |  |  |       if (current != NULL) { | 
					
						
							|  |  |  |         void* aligned_current = mi_align_up_ptr(current, try_alignment);  // and align from there to minimize wasted space
 | 
					
						
							|  |  |  |         alloc_size = _mi_align_up( ((uint8_t*)aligned_current - (uint8_t*)current) + size, _mi_os_page_size()); | 
					
						
							|  |  |  |         base = mi_memory_grow(alloc_size); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     #if defined(MI_USE_PTHREADS)
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&mi_heap_grow_mutex); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  |     if (base != NULL) { | 
					
						
							|  |  |  |       p = mi_align_up_ptr(base, try_alignment); | 
					
						
							|  |  |  |       if ((uint8_t*)p + size > (uint8_t*)base + alloc_size) { | 
					
						
							|  |  |  |         // another thread used wasm_memory_grow/sbrk in-between and we do not have enough
 | 
					
						
							|  |  |  |         // space after alignment. Give up (and waste the space as we cannot shrink :-( )
 | 
					
						
							|  |  |  |         // (in `mi_os_mem_alloc_aligned` this will fall back to overallocation to align)
 | 
					
						
							|  |  |  |         p = NULL; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /*
 | 
					
						
							|  |  |  |   if (p == NULL) { | 
					
						
							|  |  |  |     _mi_warning_message("unable to allocate sbrk/wasm_memory_grow OS memory (%zu bytes, %zu alignment)\n", size, try_alignment); | 
					
						
							|  |  |  |     errno = ENOMEM; | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   */ | 
					
						
							|  |  |  |   mi_assert_internal( p == NULL || try_alignment == 0 || (uintptr_t)p % try_alignment == 0 ); | 
					
						
							|  |  |  |   return p; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned.
 | 
					
						
							|  |  |  | int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { | 
					
						
							|  |  |  |   MI_UNUSED(allow_large); MI_UNUSED(commit); | 
					
						
							|  |  |  |   *is_large = false; | 
					
						
							|  |  |  |   *is_zero = false; | 
					
						
							|  |  |  |   *addr = mi_prim_mem_grow(size, try_alignment); | 
					
						
							|  |  |  |   return (*addr != NULL ? 0 : ENOMEM); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | // Commit/Reset/Protect
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int _mi_prim_commit(void* addr, size_t size, bool* is_zero) { | 
					
						
							|  |  |  |   MI_UNUSED(addr); MI_UNUSED(size); | 
					
						
							|  |  |  |   *is_zero = false; | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit) { | 
					
						
							|  |  |  |   MI_UNUSED(addr); MI_UNUSED(size); | 
					
						
							|  |  |  |   *needs_recommit = false; | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int _mi_prim_reset(void* addr, size_t size) { | 
					
						
							|  |  |  |   MI_UNUSED(addr); MI_UNUSED(size); | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int _mi_prim_protect(void* addr, size_t size, bool protect) { | 
					
						
							|  |  |  |   MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect); | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | // Huge pages and NUMA nodes
 | 
					
						
							|  |  |  | //---------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr) { | 
					
						
							|  |  |  |   MI_UNUSED(hint_addr); MI_UNUSED(size); MI_UNUSED(numa_node); | 
					
						
							|  |  |  |   *is_zero = true; | 
					
						
							|  |  |  |   *addr = NULL; | 
					
						
							|  |  |  |   return ENOSYS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | size_t _mi_prim_numa_node(void) { | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | size_t _mi_prim_numa_node_count(void) { | 
					
						
							|  |  |  |   return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | // Clock
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <time.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mi_msecs_t _mi_prim_clock_now(void) { | 
					
						
							|  |  |  |   struct timespec t; | 
					
						
							|  |  |  |   #ifdef CLOCK_MONOTONIC
 | 
					
						
							|  |  |  |   clock_gettime(CLOCK_MONOTONIC, &t); | 
					
						
							|  |  |  |   #else
 | 
					
						
							|  |  |  |   clock_gettime(CLOCK_REALTIME, &t); | 
					
						
							|  |  |  |   #endif
 | 
					
						
							|  |  |  |   return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // low resolution timer
 | 
					
						
							|  |  |  | mi_msecs_t _mi_prim_clock_now(void) { | 
					
						
							|  |  |  |   #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0)
 | 
					
						
							|  |  |  |   return (mi_msecs_t)clock(); | 
					
						
							|  |  |  |   #elif (CLOCKS_PER_SEC < 1000)
 | 
					
						
							|  |  |  |   return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); | 
					
						
							|  |  |  |   #else
 | 
					
						
							|  |  |  |   return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); | 
					
						
							|  |  |  |   #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | // Process info
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void _mi_prim_process_info(mi_process_info_t* pinfo) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // use defaults
 | 
					
						
							|  |  |  |   MI_UNUSED(pinfo); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | // Output
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void _mi_prim_out_stderr( const char* msg ) { | 
					
						
							|  |  |  |   fputs(msg,stderr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | // Environment
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { | 
					
						
							|  |  |  |   // cannot call getenv() when still initializing the C runtime.
 | 
					
						
							|  |  |  |   if (_mi_preloading()) return false; | 
					
						
							|  |  |  |   const char* s = getenv(name); | 
					
						
							|  |  |  |   if (s == NULL) { | 
					
						
							|  |  |  |     // we check the upper case name too.
 | 
					
						
							|  |  |  |     char buf[64+1]; | 
					
						
							|  |  |  |     size_t len = _mi_strnlen(name,sizeof(buf)-1); | 
					
						
							|  |  |  |     for (size_t i = 0; i < len; i++) { | 
					
						
							|  |  |  |       buf[i] = _mi_toupper(name[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     buf[len] = 0; | 
					
						
							|  |  |  |     s = getenv(buf); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (s == NULL || _mi_strnlen(s,result_size) >= result_size)  return false; | 
					
						
							|  |  |  |   _mi_strlcpy(result, s, result_size); | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | // Random
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool _mi_prim_random_buf(void* buf, size_t buf_len) { | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | // Thread init/done
 | 
					
						
							|  |  |  | //----------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void _mi_prim_thread_init_auto_done(void) { | 
					
						
							|  |  |  |   // nothing
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void _mi_prim_thread_done_auto_done(void) { | 
					
						
							|  |  |  |   // nothing
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { | 
					
						
							|  |  |  |   MI_UNUSED(heap); | 
					
						
							|  |  |  | } |