mirror of https://github.com/python/cpython
277 lines
8.0 KiB
C
277 lines
8.0 KiB
C
/* ----------------------------------------------------------------------------
|
|
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"
|
|
#include <unistd.h> // sbrk()
|
|
|
|
//---------------------------------------------
|
|
// 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);
|
|
}
|