Skip to content

Commit de0cb7e

Browse files
committed
use Tls expansion slots on windows if direct slots are exhausted (see also PR #1202)
1 parent 2357662 commit de0cb7e

4 files changed

Lines changed: 98 additions & 24 deletions

File tree

CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ option(MI_SEE_ASM "Generate assembly files" OFF)
1818
option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON)
1919
option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON)
2020
option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" ON)
21-
option(MI_WIN_USE_FIXED_TLS "Use a fixed TLS slot on Windows to avoid extra tests in the malloc fast path" OFF)
21+
option(MI_WIN_DIRECT_TLS "Use only direct TLS slots on Windows to avoid extra tests in the malloc fast path (only works if the program uses less than 64 TlsAlloc'd slots in total)" OFF)
2222
option(MI_LOCAL_DYNAMIC_TLS "Use local-dynamic-tls, a slightly slower but dlopen-compatible thread local storage mechanism (Unix)" OFF)
2323
option(MI_LIBC_MUSL "Enable this when linking with musl libc" OFF)
2424

@@ -394,9 +394,9 @@ if(MI_WIN_USE_FLS)
394394
list(APPEND mi_defines MI_WIN_USE_FLS=1)
395395
endif()
396396

397-
if(MI_WIN_USE_FIXED_TLS)
398-
message(STATUS "Use fixed TLS slot on Windows to avoid extra tests in the malloc fast path (MI_WIN_USE_FIXED_TLS=ON)")
399-
list(APPEND mi_defines MI_WIN_USE_FIXED_TLS=1)
397+
if(MI_WIN_DIRECT_TLS)
398+
message(STATUS "Use only direct TLS slots on Windows to avoid extra tests in the malloc fast path -- this only works if the program uses less than 64 TlsAlloc'd slots in total! (MI_WIN_USE_ONLY_DIRECT_TLS=ON)")
399+
list(APPEND mi_defines MI_WIN_DIRECT_TLS=1)
400400
endif()
401401

402402
# Check /proc/cpuinfo for an SV39 MMU and limit the virtual address bits.

include/mimalloc/prim.h

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,15 +408,38 @@ static inline mi_theap_t* _mi_theap_cached(void) {
408408
// Dynamic TLS slot (windows)
409409
#define MI_THEAP_INITASNULL 1
410410

411+
// We try to use direct slots, but can also use the expansion slots (upto 1024 available)
412+
#if MI_SIZE_SIZE==4
413+
#define MI_TLS_EXPANSION_SLOT (0x0F94 / MI_SIZE_SIZE)
414+
#else
415+
#define MI_TLS_EXPANSION_SLOT (0x1780 / MI_SIZE_SIZE)
416+
#endif
417+
411418
extern mi_decl_hidden size_t _mi_theap_default_slot;
412419
extern mi_decl_hidden size_t _mi_theap_cached_slot;
420+
extern mi_decl_hidden size_t _mi_theap_default_expansion_slot;
421+
extern mi_decl_hidden size_t _mi_theap_cached_expansion_slot;
413422

414423
static inline mi_theap_t* _mi_theap_default(void) {
415-
return (mi_theap_t*)mi_prim_tls_slot(_mi_theap_default_slot); // valid initial "last user slot" so it returns NULL at first leading to slot initialization
424+
const size_t slot = _mi_theap_default_slot;
425+
mi_theap_t* theap = (mi_theap_t*)mi_prim_tls_slot(slot);
426+
#if !MI_WIN_DIRECT_TLS
427+
if (slot==MI_TLS_EXPANSION_SLOT && theap!=NULL) { // in initialized TlsExpansionSlots ?
428+
theap = ((mi_theap_t**)theap)[_mi_theap_default_expansion_slot];
429+
}
430+
#endif
431+
return theap;
416432
}
417433

418434
static inline mi_theap_t* _mi_theap_cached(void) {
419-
return (mi_theap_t*)mi_prim_tls_slot(_mi_theap_cached_slot);
435+
const size_t slot = _mi_theap_cached_slot;
436+
mi_theap_t* theap = (mi_theap_t*)mi_prim_tls_slot(slot);
437+
#if !MI_WIN_DIRECT_TLS
438+
if (slot==MI_TLS_EXPANSION_SLOT && theap!=NULL) { // in initialized TlsExpansionSlots ?
439+
theap = ((mi_theap_t**)theap)[_mi_theap_cached_expansion_slot];
440+
}
441+
#endif
442+
return theap;
420443
}
421444

422445
#elif MI_TLS_MODEL_DYNAMIC_PTHREADS
@@ -479,7 +502,7 @@ static inline mi_theap_t* _mi_heap_theap_peek(const mi_heap_t* heap) {
479502
static inline mi_theap_t* _mi_page_associated_theap_peek(mi_page_t* page) {
480503
mi_heap_t* const heap = page->heap;
481504
mi_theap_t* theap;
482-
if mi_likely(heap==NULL) { theap = __mi_theap_main; } // note: on macOS accessing the thread_local can cause allocation during thread shutdown (and reinitialize the thread)!
505+
if mi_likely(heap==NULL) { theap = __mi_theap_main; } // note: on macOS accessing the thread_local can cause allocation during thread shutdown (and reinitialize the thread)!
483506
else { theap = _mi_heap_theap_peek(heap); }
484507
mi_assert_internal(theap==NULL || _mi_thread_id()==theap->tld->thread_id);
485508
return theap;

src/alloc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ mi_decl_nodiscard extern inline mi_decl_restrict void* mi_theap_malloc(mi_theap_
252252
}
253253

254254
mi_decl_nodiscard mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept {
255-
return mi_theap_malloc(_mi_theap_default(), size);
255+
return mi_theap_malloc(_mi_theap_default(), size);
256256
}
257257

258258
mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept {

src/init.c

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -682,32 +682,83 @@ mi_decl_cold mi_decl_noinline mi_theap_t* _mi_theap_empty_get(void) {
682682
}
683683

684684
#if MI_TLS_MODEL_DYNAMIC_WIN32
685+
#include <winternl.h>
685686

686-
// only for win32 for now
687+
// If we can, we use one of the 64 direct TLS slots (but fall back to expansion slots if needed)
687688
#if MI_SIZE_SIZE==4
688-
#define MI_TLS_USER_BASE (0x0E10 / MI_SIZE_SIZE)
689+
#define MI_TLS_DIRECT_FIRST (0x0E10 / MI_SIZE_SIZE)
689690
#else
690-
#define MI_TLS_USER_BASE (0x1480 / MI_SIZE_SIZE)
691+
#define MI_TLS_DIRECT_FIRST (0x1480 / MI_SIZE_SIZE)
691692
#endif
692-
#define MI_TLS_USER_LAST_SLOT (MI_TLS_USER_BASE + 63)
693+
#define MI_TLS_DIRECT_SLOTS (64)
694+
#define MI_TLS_EXPANSION_SLOTS (1024)
693695

694-
// we initially use the last user slot so NULL is returned
695-
// when allocating a slot, we check we get a slot before the last one (so it wasn't used yet)
696-
mi_decl_hidden size_t _mi_theap_default_slot = MI_TLS_USER_LAST_SLOT;
697-
mi_decl_hidden size_t _mi_theap_cached_slot = MI_TLS_USER_LAST_SLOT;
696+
#if !MI_WIN_DIRECT_TLS
697+
#define MI_TLS_INITIAL_SLOT MI_TLS_EXPANSION_SLOT
698+
#define MI_TLS_INITIAL_EXPANSION_SLOT (MI_TLS_EXPANSION_SLOTS-1)
699+
#else
700+
// with only direct entries, use the "arbitrary user data" field
701+
// and assume it is NULL (see also <http://www.nynaeve.net/?p=98>)
702+
#define MI_TLS_INITIAL_EXPANSION_SLOT (0)
703+
#if MI_SIZE_SIZE==4
704+
#define MI_TLS_INITIAL_SLOT (0x14 / MI_SIZE_SIZE)
705+
#else
706+
#define MI_TLS_INITIAL_SLOT (0x28 / MI_SIZE_SIZE)
707+
#endif
708+
#endif
709+
710+
// we initially use the last of the expansion slots as the default NULL.
711+
// note: this will fail if the program allocates exactly 1024+64 slots with TlsAlloc (which is quite unlikely)
712+
mi_decl_hidden mi_decl_cache_align size_t _mi_theap_default_slot = MI_TLS_INITIAL_SLOT;
713+
mi_decl_hidden size_t _mi_theap_default_expansion_slot = MI_TLS_INITIAL_EXPANSION_SLOT;
714+
mi_decl_hidden size_t _mi_theap_cached_slot = MI_TLS_INITIAL_SLOT;
715+
mi_decl_hidden size_t _mi_theap_cached_expansion_slot = MI_TLS_INITIAL_EXPANSION_SLOT;
716+
717+
static size_t mi_win_tls_slot_alloc(size_t* extended) {
718+
const DWORD slot = TlsAlloc();
719+
if (slot==TLS_OUT_OF_INDEXES || slot >= MI_TLS_DIRECT_SLOTS + MI_TLS_EXPANSION_SLOTS - 1) {
720+
// note: we also fail if the program already allocated the maximum number of expansion slots (as we use the last one as the default)
721+
*extended = 0;
722+
return 0;
723+
}
724+
else if (slot<MI_TLS_DIRECT_SLOTS) {
725+
*extended = 0;
726+
return (slot + MI_TLS_DIRECT_FIRST);
727+
}
728+
else {
729+
#if MI_WIN_DIRECT_TLS
730+
*extended = 0;
731+
return 0;
732+
#else
733+
*extended = (slot - MI_TLS_DIRECT_SLOTS);
734+
return MI_TLS_EXPANSION_SLOT;
735+
#endif
736+
}
737+
}
698738

699-
mi_decl_cold mi_theap_t* _mi_tls_slots_init(void) {
739+
mi_decl_cold mi_theap_t* _mi_win_tls_slots_init(void) {
700740
static mi_atomic_once_t tls_slots_init;
701741
if (mi_atomic_once(&tls_slots_init)) {
702-
_mi_theap_default_slot = TlsAlloc() + MI_TLS_USER_BASE;
703-
_mi_theap_cached_slot = TlsAlloc() + MI_TLS_USER_BASE;
704-
if (_mi_theap_cached_slot >= MI_TLS_USER_LAST_SLOT) {
742+
_mi_theap_default_slot = mi_win_tls_slot_alloc(&_mi_theap_default_expansion_slot);
743+
_mi_theap_cached_slot = mi_win_tls_slot_alloc(&_mi_theap_cached_expansion_slot);
744+
if (_mi_theap_cached_slot==0) {
705745
_mi_error_message(EFAULT, "unable to allocate fast TLS user slot (0x%zx)\n", _mi_theap_cached_slot);
706746
}
707747
}
708748
return (mi_theap_t*)&_mi_theap_empty;
709749
}
710750

751+
static void mi_win_tls_slot_set(size_t slot, size_t extended_slot, void* value) {
752+
mi_assert_internal((slot >= MI_TLS_DIRECT_FIRST && slot < MI_TLS_DIRECT_FIRST + MI_TLS_DIRECT_SLOTS) || slot == MI_TLS_EXPANSION_SLOT);
753+
if (slot < MI_TLS_DIRECT_FIRST + MI_TLS_DIRECT_SLOTS) {
754+
mi_prim_tls_slot_set(slot, value);
755+
}
756+
else {
757+
mi_assert_internal(extended_slot < MI_TLS_EXPANSION_SLOTS);
758+
TlsSetValue((DWORD)(extended_slot + MI_TLS_DIRECT_SLOTS), value); // use TlsSetValue to initialize the TlsExpansion array if needed
759+
}
760+
}
761+
711762
#elif MI_TLS_MODEL_DYNAMIC_PTHREADS
712763

713764
// only for pthreads for now
@@ -731,8 +782,8 @@ void _mi_theap_cached_set(mi_theap_t* theap) {
731782
#elif MI_TLS_MODEL_FIXED_SLOT
732783
mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_SLOT_CACHED, theap);
733784
#elif MI_TLS_MODEL_DYNAMIC_WIN32
734-
_mi_tls_slots_init();
735-
mi_prim_tls_slot_set(_mi_theap_cached_slot, theap);
785+
_mi_win_tls_slots_init();
786+
mi_win_tls_slot_set(_mi_theap_cached_slot, _mi_theap_cached_expansion_slot, theap);
736787
#elif MI_TLS_MODEL_DYNAMIC_PTHREADS
737788
_mi_tls_keys_init();
738789
if (_mi_theap_cached_key!=0) pthread_setspecific(_mi_theap_cached_key, theap);
@@ -747,8 +798,8 @@ void _mi_theap_default_set(mi_theap_t* theap) {
747798
#elif MI_TLS_MODEL_FIXED_SLOT
748799
mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_SLOT_DEFAULT, theap);
749800
#elif MI_TLS_MODEL_DYNAMIC_WIN32
750-
_mi_tls_slots_init();
751-
mi_prim_tls_slot_set(_mi_theap_default_slot, theap);
801+
_mi_win_tls_slots_init();
802+
mi_win_tls_slot_set(_mi_theap_default_slot, _mi_theap_default_expansion_slot, theap);
752803
#elif MI_TLS_MODEL_DYNAMIC_PTHREADS
753804
_mi_tls_keys_init();
754805
if (_mi_theap_default_key!=0) pthread_setspecific(_mi_theap_default_key, theap);

0 commit comments

Comments
 (0)