From 0d700e91c7cf3c1cefa557f6d5d81085eba7019b Mon Sep 17 00:00:00 2001 From: Apprentice-Alchemist <53486764+Apprentice-Alchemist@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:40:51 +0100 Subject: [PATCH] Refactor thread local storage --- src/gc.c | 56 ++++++++++++++++++++++ src/hl.h | 3 +- src/std/thread.c | 120 +++++++++++------------------------------------ 3 files changed, 86 insertions(+), 93 deletions(-) diff --git a/src/gc.c b/src/gc.c index e1b6d3093..84880d18d 100644 --- a/src/gc.c +++ b/src/gc.c @@ -308,6 +308,58 @@ HL_PRIM gc_pheader *hl_gc_get_page( void *v ) { HL_API int hl_thread_id(); +HL_PRIM int hl_atomic_add32(int *a, int b); + +#ifdef HL_WIN +static SRWLOCK free_list_mutex = SRWLOCK_INIT; +static inline void lock_free_list() { AcquireSRWLockExclusive(&free_list_mutex); } +static inline void unlock_free_list() { ReleaseSRWLockExclusive(&free_list_mutex); } +#else +#include +static pthread_mutex_t free_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static inline void lock_free_list() { pthread_mutex_lock(&free_list_mutex); } +static inline void unlock_free_list() { pthread_mutex_unlock(&free_list_mutex); } +#endif + +static int tls_counter = 0; + +struct tls_free_list { + int key; + struct tls_free_list *next; +}; + +static struct tls_free_list * free_list; + +int get_tls_slot() { + lock_free_list(); + if (free_list) { + struct tls_free_list *l = free_list; + int key = l->key; + free_list = l->next; + free(l); + unlock_free_list(); + return key; + } + unlock_free_list(); + return hl_atomic_add32(&tls_counter, 1); +} + +// this function should only run in the finalizer, which is called when all threads +// are stopped +void free_tls_slot(int key) { + for (int i = 0; i < gc_threads.count; i++) { + if (key < gc_threads.threads[i]->tls_arr_size) { + gc_threads.threads[i]->tls_arr[key] = NULL; + } + } + struct tls_free_list *l = malloc(sizeof(struct tls_free_list)); + l->key = key; + lock_free_list(); + l->next = free_list; + free_list = l; + unlock_free_list(); +} + HL_API void hl_register_thread( void *stack_top ) { if( hl_get_thread() ) hl_fatal("Thread already registered"); @@ -324,6 +376,9 @@ HL_API void hl_register_thread( void *stack_top ) { current_thread = t; hl_add_root(&t->exc_value); hl_add_root(&t->exc_handler); + hl_add_root(&t->tls_arr); + t->tls_arr_size = 0; + t->tls_arr = NULL; gc_global_lock(true); hl_thread_info **all = (hl_thread_info**)malloc(sizeof(void*) * (gc_threads.count + 1)); @@ -340,6 +395,7 @@ HL_API void hl_unregister_thread() { hl_fatal("Thread not registered"); hl_remove_root(&t->exc_value); hl_remove_root(&t->exc_handler); + hl_remove_root(&t->tls_arr); gc_global_lock(true); for(i=0;i @@ -104,13 +97,6 @@ struct _hl_condition { pthread_mutex_t mutex; pthread_cond_t cond; }; - -struct _hl_tls { - void (*free)( hl_tls * ); - pthread_key_t key; - bool gc; -}; - #endif // ----------------- ALLOC @@ -463,95 +449,45 @@ DEFINE_PRIM(_VOID, condition_broadcast, _CONDITION) // ----------------- THREAD LOCAL -#if defined(HL_THREADS) -static void **_tls_get( hl_tls *t ) { -# ifdef HL_WIN - return (void**)TlsGetValue(t->tid); -# else - return (void**)pthread_getspecific(t->key); -# endif -} -static void _tls_set( hl_tls *t, void *store ) { -# ifdef HL_WIN - TlsSetValue(t->tid, store); -# else - pthread_setspecific(t->key, store); -# endif -} -#endif +int get_tls_slot(); +void free_tls_slot(int id); + +struct _hl_tls { + void (*free)( hl_tls * ); + int key; + bool gc; +}; + +static void tls_free(hl_tls *l) { free_tls_slot(l->key); } HL_PRIM hl_tls *hl_tls_alloc( bool gc_value ) { -# if !defined(HL_THREADS) hl_tls *l = (hl_tls*)hl_gc_alloc_finalizer(sizeof(hl_tls)); - l->free = hl_tls_free; - l->value = NULL; - return l; -# elif defined(HL_WIN) - hl_tls *l = (hl_tls*)hl_gc_alloc_finalizer(sizeof(hl_tls)); - l->free = hl_tls_free; - l->tid = TlsAlloc(); - l->gc = gc_value; - TlsSetValue(l->tid,NULL); - return l; -# else - hl_tls *l = (hl_tls*)hl_gc_alloc_finalizer(sizeof(hl_tls)); - l->free = hl_tls_free; + l->free = tls_free; l->gc = gc_value; - pthread_key_create(&l->key,NULL); + l->key = get_tls_slot(); return l; -# endif -} - -HL_PRIM void hl_tls_free( hl_tls *l ) { -# if !defined(HL_THREADS) - free(l); -# elif defined(HL_WIN) - if( l->free ) { - TlsFree(l->tid); - l->free = NULL; - } -# else - if( l->free ) { - pthread_key_delete(l->key); - l->free = NULL; - } -# endif } HL_PRIM void hl_tls_set( hl_tls *l, void *v ) { -# if !defined(HL_THREADS) - l->value = v; -# else - if( l->gc ) { - void **store = _tls_get(l); - if( !store) { - if( !v ) - return; - store = (void**)malloc(sizeof(void*)); - hl_add_root(store); - _tls_set(l, store); - } else { - if( !v ) { - free(store); - hl_remove_root(store); - _tls_set(l, NULL); - return; - } - } - *store = v; - } else - _tls_set(l, v); -# endif + hl_thread_info *info = hl_get_thread(); + if (l->key >= info->tls_arr_size) { + int new_max = info->tls_arr_size > 0 ? info->tls_arr_size * 2 : 16; + new_max = l->key > new_max ? l->key : new_max; + void **new_arr = hl_gc_alloc_raw(sizeof(void*) * new_max); + memcpy(new_arr, info->tls_arr, info->tls_arr_size * sizeof(void*)); + info->tls_arr = new_arr; + info->tls_arr_size = new_max; + } + info->tls_arr[l->key] = v; } HL_PRIM void *hl_tls_get( hl_tls *l ) { -# if !defined(HL_THREADS) - return l->value; -# else - void **store = _tls_get(l); - if( !l->gc ) return store; - return store ? *store : NULL; -# endif + hl_thread_info *info = hl_get_thread(); + if (l->key < info->tls_arr_size) { + return info->tls_arr[l->key]; + } else { + return NULL; + } } #define _TLS _ABSTRACT(hl_tls)