#include "mimalloc.h"
#include "mimalloc/internal.h"
#include "mimalloc/prim.h"
#include "mimalloc/prim-tls.h"
void _mi_tls_slots_init(void);
void _mi_tls_slots_done(void);
void _mi_theap_default_set(mi_theap_t* theap);
void _mi_theap_cached_set(mi_theap_t* theap);
#if MI_TLS_MODEL_LOCAL
mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_default = (mi_theap_t*)&_mi_theap_empty;
mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_cached = (mi_theap_t*)&_mi_theap_empty;
#endif
mi_decl_hidden mi_decl_thread void* __mi_thread_id_helper = NULL;
mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {
const mi_threadid_t tid = _mi_prim_thread_id();
mi_assert_internal( (tid & MI_PAGE_FLAG_MASK) == 0 ); return tid;
}
#if MI_TLS_MODEL_WIN32
#if MI_SIZE_SIZE==4
#define MI_TLS_DIRECT_FIRST (0x0E10 / MI_INTPTR_SIZE)
#else
#define MI_TLS_DIRECT_FIRST (0x1480 / MI_INTPTR_SIZE)
#endif
#define MI_TLS_DIRECT_SLOTS (64)
#define MI_TLS_EXPANSION_SLOTS (1024)
#if !MI_WIN_DIRECT_TLS
#define MI_TLS_INITIAL_SLOT MI_TLS_EXPANSION_SLOT
#define MI_TLS_INITIAL_EXPANSION_SLOT (MI_TLS_EXPANSION_SLOTS-1)
#else
#define MI_TLS_INITIAL_SLOT (5)
#define MI_TLS_INITIAL_EXPANSION_SLOT (MI_TLS_EXPANSION_SLOTS-1)
#endif
#define MI_TLS_ERROR_SLOT (5)
#define MI_TLS_ERROR_EXPANSION_SLOT (7)
mi_decl_hidden mi_decl_cache_align _Atomic(size_t) _mi_theap_default_slot = MI_ATOMIC_VAR_INIT(MI_TLS_INITIAL_SLOT);
mi_decl_hidden _Atomic(size_t) _mi_theap_default_expansion_slot = MI_ATOMIC_VAR_INIT(MI_TLS_INITIAL_EXPANSION_SLOT);
mi_decl_hidden _Atomic(size_t) _mi_theap_cached_slot = MI_ATOMIC_VAR_INIT(MI_TLS_INITIAL_SLOT);
mi_decl_hidden _Atomic(size_t) _mi_theap_cached_expansion_slot = MI_ATOMIC_VAR_INIT(MI_TLS_INITIAL_EXPANSION_SLOT);
static DWORD mi_tls_raw_index_default = TLS_OUT_OF_INDEXES;
static DWORD mi_tls_raw_index_cached = TLS_OUT_OF_INDEXES;
static bool mi_win_tls_slot_alloc(_Atomic(size_t)* slot, _Atomic(size_t)* extended, DWORD* raw_index) {
const DWORD index = TlsAlloc();
*raw_index = index;
if (index==TLS_OUT_OF_INDEXES) {
mi_atomic_store_release(slot,MI_TLS_ERROR_SLOT);
mi_atomic_store_release(extended,MI_TLS_ERROR_EXPANSION_SLOT);
return false;
}
else if (index<MI_TLS_DIRECT_SLOTS) {
mi_atomic_store_release(slot,index + MI_TLS_DIRECT_FIRST);
mi_atomic_store_release(extended,0);
return true;
}
#if !MI_WIN_DIRECT_TLS
else if (index < MI_TLS_DIRECT_SLOTS + MI_TLS_EXPANSION_SLOTS - 1) { mi_atomic_store_release(slot, MI_TLS_EXPANSION_SLOT);
mi_atomic_store_release(extended,index - MI_TLS_DIRECT_SLOTS);
return true;
}
#endif
else {
_mi_error_message(EFAULT, "returned TLS index was too high (%u)\n", index);
TlsFree(index);
*raw_index = TLS_OUT_OF_INDEXES;
mi_atomic_store_release(slot, MI_TLS_ERROR_SLOT);
mi_atomic_store_release(extended,MI_TLS_ERROR_EXPANSION_SLOT);
return false;
}
}
static void mi_win_tls_slot_free(_Atomic(size_t)*slot, _Atomic(size_t)*extended, DWORD* raw_index) {
if (*raw_index != TLS_OUT_OF_INDEXES) {
mi_atomic_store_release(slot, MI_TLS_ERROR_SLOT);
mi_atomic_store_release(extended, MI_TLS_ERROR_EXPANSION_SLOT);
TlsFree(*raw_index);
*raw_index = TLS_OUT_OF_INDEXES;
}
}
void _mi_tls_slots_init(void) {
mi_atomic_do_once {
bool ok = mi_win_tls_slot_alloc(&_mi_theap_default_slot, &_mi_theap_default_expansion_slot, &mi_tls_raw_index_default);
if (ok) {
ok = mi_win_tls_slot_alloc(&_mi_theap_cached_slot, &_mi_theap_cached_expansion_slot, &mi_tls_raw_index_cached);
}
if (!ok) {
_mi_error_message(EFAULT, "unable to allocate a fast TLS user slot.\n");
}
}
}
void _mi_tls_slots_done(void) {
mi_win_tls_slot_free(&_mi_theap_default_slot, &_mi_theap_default_expansion_slot, &mi_tls_raw_index_default);
mi_win_tls_slot_free(&_mi_theap_cached_slot, &_mi_theap_cached_expansion_slot, &mi_tls_raw_index_cached );
}
static void mi_win_tls_slot_set(size_t slot, size_t extended_slot, void* value) {
mi_assert_internal((slot >= MI_TLS_DIRECT_FIRST && slot < MI_TLS_DIRECT_FIRST + MI_TLS_DIRECT_SLOTS) || slot == MI_TLS_EXPANSION_SLOT);
if (slot < MI_TLS_DIRECT_FIRST + MI_TLS_DIRECT_SLOTS) {
mi_prim_tls_slot_set(slot, value);
}
else {
mi_assert_internal(extended_slot < MI_TLS_EXPANSION_SLOTS);
TlsSetValue((DWORD)(extended_slot + MI_TLS_DIRECT_SLOTS), value); }
}
#elif MI_TLS_MODEL_PTHREADS
mi_decl_hidden _Atomic(pthread_key_t) _mi_theap_default_key = MI_ATOMIC_VAR_INIT(MI_PTHREAD_KEY_INVALID);
mi_decl_hidden _Atomic(pthread_key_t) _mi_theap_cached_key = MI_ATOMIC_VAR_INIT(MI_PTHREAD_KEY_INVALID);
static void mi_theap_cached_key_destroy(void* theapv) {
mi_theap_t* theap = (mi_theap_t*)theapv;
if (theap!=NULL) {
_mi_theap_decref(theap);
}
}
void _mi_tls_slots_init(void) {
mi_atomic_do_once {
pthread_key_t key;
if (_mi_pthread_key_create(&key,NULL,NULL)) { mi_atomic_store_release(&_mi_theap_default_key,key); }
if (_mi_pthread_key_create(&key,&mi_theap_cached_key_destroy,NULL)) { mi_atomic_store_release(&_mi_theap_cached_key,key); }
}
}
void _mi_tls_slots_done(void) {
pthread_key_t key = mi_atomic_exchange_relaxed(&_mi_theap_default_key,MI_PTHREAD_KEY_INVALID);
if (key!=MI_PTHREAD_KEY_INVALID) { pthread_key_delete(key); }
key = mi_atomic_exchange_relaxed(&_mi_theap_cached_key,MI_PTHREAD_KEY_INVALID);
if (key!=MI_PTHREAD_KEY_INVALID) { pthread_key_delete(key); }
}
#elif MI_TLS_MODEL_FIXED
void _mi_tls_slots_init(void) {
mi_atomic_do_once {
mi_theap_t* theap = _mi_theap_default();
if (theap!=NULL) {
_mi_error_message(EINVAL,"fixed TLS slot is already in use (slot %d = %p)", MI_TLS_MODEL_FIXED_DEFAULT, theap);
}
theap = _mi_theap_cached();
if (theap!=NULL) {
_mi_error_message(EINVAL,"fixed TLS slot is already in use (slot %d = %p)", MI_TLS_MODEL_FIXED_CACHED, theap);
}
}
}
void _mi_tls_slots_done(void) {
}
#else
void _mi_tls_slots_init(void) {
}
void _mi_tls_slots_done(void) {
}
#endif
void _mi_theap_cached_set(mi_theap_t* theap) {
mi_theap_t* prev = _mi_theap_cached();
if (prev==theap) return;
_mi_tls_slots_init();
#if MI_TLS_MODEL_LOCAL
__mi_theap_cached = theap;
#elif MI_TLS_MODEL_FIXED
mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_CACHED, theap);
#elif MI_TLS_MODEL_WIN32
mi_win_tls_slot_set(mi_atomic_load_relaxed(&_mi_theap_cached_slot), mi_atomic_load_relaxed(&_mi_theap_cached_expansion_slot), theap);
#elif MI_TLS_MODEL_PTHREADS
pthread_key_t key = mi_atomic_load_relaxed(&_mi_theap_cached_key);
if (key!=MI_PTHREAD_KEY_INVALID) { pthread_setspecific(key, theap); }
#endif
_mi_theap_incref(theap);
_mi_theap_decref(prev);
}
void _mi_theap_default_set(mi_theap_t* theap) {
mi_assert_internal(theap != NULL);
mi_assert_internal(theap->tld != NULL);
mi_assert_internal(mi_theap_matches_thread(theap));
_mi_tls_slots_init();
#if MI_TLS_MODEL_LOCAL
__mi_theap_default = theap;
#elif MI_TLS_MODEL_FIXED
mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_DEFAULT, theap);
#elif MI_TLS_MODEL_WIN32
mi_win_tls_slot_set(mi_atomic_load_relaxed(&_mi_theap_default_slot), mi_atomic_load_relaxed(&_mi_theap_default_expansion_slot), theap);
#elif MI_TLS_MODEL_PTHREADS
pthread_key_t key = mi_atomic_load_relaxed(&_mi_theap_default_key);
if (key!=MI_PTHREAD_KEY_INVALID) { pthread_setspecific(key, theap); }
#endif
if (mi_theap_is_initialized(theap)) {
_mi_prim_thread_associate_default_theap(theap);
}
}