microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
allocator/mimalloc-sys/mimalloc/src/prim/wasi/prim.c
275lines · modecode
| 1 | /* ---------------------------------------------------------------------------- |
| 2 | Copyright (c) 2018-2023, Microsoft Research, Daan Leijen |
| 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 | |
| 8 | // This file is included in `src/prim/prim.c` |
| 9 | |
| 10 | #include "mimalloc.h" |
| 11 | #include "mimalloc/internal.h" |
| 12 | #include "mimalloc/atomic.h" |
| 13 | #include "mimalloc/prim.h" |
| 14 | |
| 15 | //--------------------------------------------- |
| 16 | // Initialize |
| 17 | //--------------------------------------------- |
| 18 | |
| 19 | void _mi_prim_mem_init( mi_os_mem_config_t* config ) { |
| 20 | config->page_size = 64*MI_KiB; // WebAssembly has a fixed page size: 64KiB |
| 21 | config->alloc_granularity = 16; |
| 22 | config->has_overcommit = false; |
| 23 | config->must_free_whole = true; |
| 24 | config->has_virtual_reserve = false; |
| 25 | } |
| 26 | |
| 27 | //--------------------------------------------- |
| 28 | // Free |
| 29 | //--------------------------------------------- |
| 30 | |
| 31 | int _mi_prim_free(void* addr, size_t size ) { |
| 32 | MI_UNUSED(addr); MI_UNUSED(size); |
| 33 | // wasi heap cannot be shrunk |
| 34 | return 0; |
| 35 | } |
| 36 | |
| 37 | |
| 38 | //--------------------------------------------- |
| 39 | // Allocation: sbrk or memory_grow |
| 40 | //--------------------------------------------- |
| 41 | |
| 42 | #if defined(MI_USE_SBRK) |
| 43 | static void* mi_memory_grow( size_t size ) { |
| 44 | void* p = sbrk(size); |
| 45 | if (p == (void*)(-1)) return NULL; |
| 46 | #if !defined(__wasi__) // on wasi this is always zero initialized already (?) |
| 47 | memset(p,0,size); |
| 48 | #endif |
| 49 | return p; |
| 50 | } |
| 51 | #elif defined(__wasi__) |
| 52 | static void* mi_memory_grow( size_t size ) { |
| 53 | size_t base = (size > 0 ? __builtin_wasm_memory_grow(0,_mi_divide_up(size, _mi_os_page_size())) |
| 54 | : __builtin_wasm_memory_size(0)); |
| 55 | if (base == SIZE_MAX) return NULL; |
| 56 | return (void*)(base * _mi_os_page_size()); |
| 57 | } |
| 58 | #endif |
| 59 | |
| 60 | #if defined(MI_USE_PTHREADS) |
| 61 | static pthread_mutex_t mi_heap_grow_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 62 | #endif |
| 63 | |
| 64 | static void* mi_prim_mem_grow(size_t size, size_t try_alignment) { |
| 65 | void* p = NULL; |
| 66 | if (try_alignment <= 1) { |
| 67 | // `sbrk` is not thread safe in general so try to protect it (we could skip this on WASM but leave it in for now) |
| 68 | #if defined(MI_USE_PTHREADS) |
| 69 | pthread_mutex_lock(&mi_heap_grow_mutex); |
| 70 | #endif |
| 71 | p = mi_memory_grow(size); |
| 72 | #if defined(MI_USE_PTHREADS) |
| 73 | pthread_mutex_unlock(&mi_heap_grow_mutex); |
| 74 | #endif |
| 75 | } |
| 76 | else { |
| 77 | void* base = NULL; |
| 78 | size_t alloc_size = 0; |
| 79 | // to allocate aligned use a lock to try to avoid thread interaction |
| 80 | // between getting the current size and actual allocation |
| 81 | // (also, `sbrk` is not thread safe in general) |
| 82 | #if defined(MI_USE_PTHREADS) |
| 83 | pthread_mutex_lock(&mi_heap_grow_mutex); |
| 84 | #endif |
| 85 | { |
| 86 | void* current = mi_memory_grow(0); // get current size |
| 87 | if (current != NULL) { |
| 88 | void* aligned_current = mi_align_up_ptr(current, try_alignment); // and align from there to minimize wasted space |
| 89 | alloc_size = _mi_align_up( ((uint8_t*)aligned_current - (uint8_t*)current) + size, _mi_os_page_size()); |
| 90 | base = mi_memory_grow(alloc_size); |
| 91 | } |
| 92 | } |
| 93 | #if defined(MI_USE_PTHREADS) |
| 94 | pthread_mutex_unlock(&mi_heap_grow_mutex); |
| 95 | #endif |
| 96 | if (base != NULL) { |
| 97 | p = mi_align_up_ptr(base, try_alignment); |
| 98 | if ((uint8_t*)p + size > (uint8_t*)base + alloc_size) { |
| 99 | // another thread used wasm_memory_grow/sbrk in-between and we do not have enough |
| 100 | // space after alignment. Give up (and waste the space as we cannot shrink :-( ) |
| 101 | // (in `mi_os_mem_alloc_aligned` this will fall back to overallocation to align) |
| 102 | p = NULL; |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | /* |
| 107 | if (p == NULL) { |
| 108 | _mi_warning_message("unable to allocate sbrk/wasm_memory_grow OS memory (%zu bytes, %zu alignment)\n", size, try_alignment); |
| 109 | errno = ENOMEM; |
| 110 | return NULL; |
| 111 | } |
| 112 | */ |
| 113 | mi_assert_internal( p == NULL || try_alignment == 0 || (uintptr_t)p % try_alignment == 0 ); |
| 114 | return p; |
| 115 | } |
| 116 | |
| 117 | // Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. |
| 118 | int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { |
| 119 | MI_UNUSED(allow_large); MI_UNUSED(commit); |
| 120 | *is_large = false; |
| 121 | *is_zero = false; |
| 122 | *addr = mi_prim_mem_grow(size, try_alignment); |
| 123 | return (*addr != NULL ? 0 : ENOMEM); |
| 124 | } |
| 125 | |
| 126 | |
| 127 | //--------------------------------------------- |
| 128 | // Commit/Reset/Protect |
| 129 | //--------------------------------------------- |
| 130 | |
| 131 | int _mi_prim_commit(void* addr, size_t size, bool* is_zero) { |
| 132 | MI_UNUSED(addr); MI_UNUSED(size); |
| 133 | *is_zero = false; |
| 134 | return 0; |
| 135 | } |
| 136 | |
| 137 | int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit) { |
| 138 | MI_UNUSED(addr); MI_UNUSED(size); |
| 139 | *needs_recommit = false; |
| 140 | return 0; |
| 141 | } |
| 142 | |
| 143 | int _mi_prim_reset(void* addr, size_t size) { |
| 144 | MI_UNUSED(addr); MI_UNUSED(size); |
| 145 | return 0; |
| 146 | } |
| 147 | |
| 148 | int _mi_prim_protect(void* addr, size_t size, bool protect) { |
| 149 | MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect); |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | |
| 154 | //--------------------------------------------- |
| 155 | // Huge pages and NUMA nodes |
| 156 | //--------------------------------------------- |
| 157 | |
| 158 | int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr) { |
| 159 | MI_UNUSED(hint_addr); MI_UNUSED(size); MI_UNUSED(numa_node); |
| 160 | *is_zero = true; |
| 161 | *addr = NULL; |
| 162 | return ENOSYS; |
| 163 | } |
| 164 | |
| 165 | size_t _mi_prim_numa_node(void) { |
| 166 | return 0; |
| 167 | } |
| 168 | |
| 169 | size_t _mi_prim_numa_node_count(void) { |
| 170 | return 1; |
| 171 | } |
| 172 | |
| 173 | |
| 174 | //---------------------------------------------------------------- |
| 175 | // Clock |
| 176 | //---------------------------------------------------------------- |
| 177 | |
| 178 | #include <time.h> |
| 179 | |
| 180 | #if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) |
| 181 | |
| 182 | mi_msecs_t _mi_prim_clock_now(void) { |
| 183 | struct timespec t; |
| 184 | #ifdef CLOCK_MONOTONIC |
| 185 | clock_gettime(CLOCK_MONOTONIC, &t); |
| 186 | #else |
| 187 | clock_gettime(CLOCK_REALTIME, &t); |
| 188 | #endif |
| 189 | return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); |
| 190 | } |
| 191 | |
| 192 | #else |
| 193 | |
| 194 | // low resolution timer |
| 195 | mi_msecs_t _mi_prim_clock_now(void) { |
| 196 | #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) |
| 197 | return (mi_msecs_t)clock(); |
| 198 | #elif (CLOCKS_PER_SEC < 1000) |
| 199 | return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); |
| 200 | #else |
| 201 | return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); |
| 202 | #endif |
| 203 | } |
| 204 | |
| 205 | #endif |
| 206 | |
| 207 | |
| 208 | //---------------------------------------------------------------- |
| 209 | // Process info |
| 210 | //---------------------------------------------------------------- |
| 211 | |
| 212 | void _mi_prim_process_info(mi_process_info_t* pinfo) |
| 213 | { |
| 214 | // use defaults |
| 215 | MI_UNUSED(pinfo); |
| 216 | } |
| 217 | |
| 218 | |
| 219 | //---------------------------------------------------------------- |
| 220 | // Output |
| 221 | //---------------------------------------------------------------- |
| 222 | |
| 223 | void _mi_prim_out_stderr( const char* msg ) { |
| 224 | fputs(msg,stderr); |
| 225 | } |
| 226 | |
| 227 | |
| 228 | //---------------------------------------------------------------- |
| 229 | // Environment |
| 230 | //---------------------------------------------------------------- |
| 231 | |
| 232 | bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { |
| 233 | // cannot call getenv() when still initializing the C runtime. |
| 234 | if (_mi_preloading()) return false; |
| 235 | const char* s = getenv(name); |
| 236 | if (s == NULL) { |
| 237 | // we check the upper case name too. |
| 238 | char buf[64+1]; |
| 239 | size_t len = _mi_strnlen(name,sizeof(buf)-1); |
| 240 | for (size_t i = 0; i < len; i++) { |
| 241 | buf[i] = _mi_toupper(name[i]); |
| 242 | } |
| 243 | buf[len] = 0; |
| 244 | s = getenv(buf); |
| 245 | } |
| 246 | if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false; |
| 247 | _mi_strlcpy(result, s, result_size); |
| 248 | return true; |
| 249 | } |
| 250 | |
| 251 | |
| 252 | //---------------------------------------------------------------- |
| 253 | // Random |
| 254 | //---------------------------------------------------------------- |
| 255 | |
| 256 | bool _mi_prim_random_buf(void* buf, size_t buf_len) { |
| 257 | return false; |
| 258 | } |
| 259 | |
| 260 | |
| 261 | //---------------------------------------------------------------- |
| 262 | // Thread init/done |
| 263 | //---------------------------------------------------------------- |
| 264 | |
| 265 | void _mi_prim_thread_init_auto_done(void) { |
| 266 | // nothing |
| 267 | } |
| 268 | |
| 269 | void _mi_prim_thread_done_auto_done(void) { |
| 270 | // nothing |
| 271 | } |
| 272 | |
| 273 | void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { |
| 274 | MI_UNUSED(heap); |
| 275 | } |
| 276 | |