Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <pthread.h>
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");
Expand All @@ -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));
Expand All @@ -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<gc_threads.count;i++)
if( gc_threads.threads[i] == t ) {
Expand Down
3 changes: 2 additions & 1 deletion src/hl.h
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,6 @@ HL_API void hl_condition_free(hl_condition *cond);
HL_API hl_tls *hl_tls_alloc( bool gc_value );
HL_API void hl_tls_set( hl_tls *l, void *value );
HL_API void *hl_tls_get( hl_tls *l );
HL_API void hl_tls_free( hl_tls *l );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an abi breakage, maybe it's better to deprecate it and keep a dummy implementation


// ----------------------- ALLOC --------------------------------------------------

Expand Down Expand Up @@ -984,6 +983,8 @@ typedef struct {
thread_t mach_thread_id;
pthread_t pthread_id;
#endif
int tls_arr_size;
void **tls_arr;
} hl_thread_info;

typedef struct {
Expand Down
120 changes: 28 additions & 92 deletions src/std/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,6 @@ struct _hl_condition {
CRITICAL_SECTION cs;
CONDITION_VARIABLE cond;
};

struct _hl_tls {
void (*free)( hl_tls * );
DWORD tid;
bool gc;
};

#else

# include <pthread.h>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Loading