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