Browse Source

handle thread registration and per thread stack scanning

Nicolas Cannasse 7 years ago
parent
commit
2b25836064
6 changed files with 359 additions and 90 deletions
  1. 167 65
      src/alloc.c
  2. 5 2
      src/debugger.c
  3. 22 4
      src/hl.h
  4. 6 2
      src/main.c
  5. 1 1
      src/std/maps.h
  6. 158 16
      src/std/thread.c

+ 167 - 65
src/alloc.c

@@ -134,6 +134,22 @@ static gc_pheader *gc_level1_null[1<<GC_LEVEL1_BITS] = {NULL};
 static gc_pheader **hl_gc_page_map[1<<GC_LEVEL0_BITS] = {NULL};
 static gc_pheader **hl_gc_page_map[1<<GC_LEVEL0_BITS] = {NULL};
 static void (*gc_track_callback)(hl_type *,int,int,void*) = NULL;
 static void (*gc_track_callback)(hl_type *,int,int,void*) = NULL;
 
 
+
+typedef struct {
+	void *stack_top;
+	void *stack_cur;
+	hl_lock *lock;
+	jmp_buf regs;
+	int blocking;
+} gc_thread;
+
+static struct {
+	int count;
+	gc_thread **threads;
+	hl_lock *global_lock;
+	hl_tls *cur_thread;
+} gc_threads;
+
 static struct {
 static struct {
 	int64 total_requested;
 	int64 total_requested;
 	int64 total_allocated;
 	int64 total_allocated;
@@ -168,11 +184,31 @@ static void ***gc_roots = NULL;
 static int gc_roots_count = 0;
 static int gc_roots_count = 0;
 static int gc_roots_max = 0;
 static int gc_roots_max = 0;
 
 
+static gc_thread *gc_get_thread() {
+	return hl_tls_get(gc_threads.cur_thread);
+}
+
+#ifndef HL_THREADS
+#	define gc_global_lock(_)
+#else
+static void gc_global_lock( bool lock ) {
+	gc_thread *t = gc_get_thread();
+	if( lock ) {
+		t->blocking++;
+		hl_lock_acquire(gc_threads.global_lock);
+	} else {
+		t->blocking--;
+		hl_lock_release(gc_threads.global_lock);
+	}
+}
+#endif
+
 HL_PRIM void hl_gc_set_track( void *f ) {
 HL_PRIM void hl_gc_set_track( void *f ) {
 	gc_track_callback = f;
 	gc_track_callback = f;
 }
 }
 
 
 HL_PRIM void hl_add_root( void *r ) {
 HL_PRIM void hl_add_root( void *r ) {
+	gc_global_lock(true);
 	if( gc_roots_count == gc_roots_max ) {
 	if( gc_roots_count == gc_roots_max ) {
 		int nroots = gc_roots_max ? (gc_roots_max << 1) : 16;
 		int nroots = gc_roots_max ? (gc_roots_max << 1) : 16;
 		void ***roots = (void***)malloc(sizeof(void*)*nroots);
 		void ***roots = (void***)malloc(sizeof(void*)*nroots);
@@ -182,20 +218,19 @@ HL_PRIM void hl_add_root( void *r ) {
 		gc_roots_max = nroots;
 		gc_roots_max = nroots;
 	}
 	}
 	gc_roots[gc_roots_count++] = (void**)r;
 	gc_roots[gc_roots_count++] = (void**)r;
-}
-
-HL_PRIM void hl_pop_root() {
-	gc_roots_count--;
+	gc_global_lock(false);
 }
 }
 
 
 HL_PRIM void hl_remove_root( void *v ) {
 HL_PRIM void hl_remove_root( void *v ) {
 	int i;
 	int i;
-	for(i=0;i<gc_roots_count;i++)
+	gc_global_lock(true);
+	for(i=gc_roots_count-1;i>=0;i--)
 		if( gc_roots[i] == (void**)v ) {
 		if( gc_roots[i] == (void**)v ) {
 			gc_roots_count--;
 			gc_roots_count--;
 			memmove(gc_roots + i, gc_roots + (i+1), (gc_roots_count - i) * sizeof(void*));
 			memmove(gc_roots + i, gc_roots + (i+1), (gc_roots_count - i) * sizeof(void*));
 			break;
 			break;
 		}
 		}
+	gc_global_lock(false);
 }
 }
 
 
 HL_PRIM gc_pheader *hl_gc_get_page( void *v ) {
 HL_PRIM gc_pheader *hl_gc_get_page( void *v ) {
@@ -207,6 +242,61 @@ HL_PRIM gc_pheader *hl_gc_get_page( void *v ) {
 	return page;
 	return page;
 }
 }
 
 
+// -------------------------  THREADS ----------------------------------------------------------
+
+HL_API void hl_register_thread( void *stack_top ) {
+	if( gc_get_thread() )
+		hl_error("Thread already registered");
+
+	gc_thread *t = (gc_thread*)malloc(sizeof(gc_thread));
+	t->blocking = 0;
+	t->lock = hl_lock_alloc();
+	t->stack_top = stack_top;
+	hl_tls_set(gc_threads.cur_thread, t);
+
+	gc_global_lock(true);
+	gc_thread **all = (gc_thread**)malloc(sizeof(void*) * (gc_threads.count + 1));
+	memcpy(all,gc_threads.threads,sizeof(void*)*gc_threads.count);
+	gc_threads.threads = all;
+	all[gc_threads.count++] = t;
+	gc_global_lock(false);
+}
+
+HL_API void hl_unregister_thread() {
+	int i;
+	gc_thread *t = gc_get_thread();
+	if( !t )
+		hl_error("Thread not registered");
+	gc_global_lock(true);
+	for(i=0;i<gc_threads.count;i++)
+		if( gc_threads.threads[i] == t ) {
+			memmove(gc_threads.threads + i, gc_threads.threads + i + 1, sizeof(void*) * (gc_threads.count - i - 1));
+			gc_threads.count--;
+			free(t);
+			break;
+		}
+	hl_tls_set(gc_threads.cur_thread, NULL);
+	gc_global_lock(false);
+}
+
+HL_API void *hl_gc_stack_top() {
+	return gc_get_thread()->stack_top;
+}
+
+HL_API void *hl_gc_threads_info() {
+	return &gc_threads;
+}
+
+static void gc_stop_self() {
+	gc_thread *t = gc_get_thread();
+	setjmp(t->regs);
+	t->stack_cur = &t;
+}
+
+static void gc_stop_world( bool b ) {
+	if( b ) gc_stop_self();
+}
+
 // -------------------------  ALLOCATOR ----------------------------------------------------------
 // -------------------------  ALLOCATOR ----------------------------------------------------------
 
 
 static void *gc_alloc_page_memory( int size );
 static void *gc_alloc_page_memory( int size );
@@ -561,28 +651,11 @@ static void *gc_alloc_gen( int size, int flags, int *allocated ) {
 
 
 static void gc_check_mark();
 static void gc_check_mark();
 
 
-//#define HL_BUMP_ALLOC
-
-#ifdef HL_BUMP_ALLOC
-static unsigned char *alloc_all = NULL;
-static unsigned char *alloc_end = NULL;
-#endif
-
 void *hl_gc_alloc_gen( hl_type *t, int size, int flags ) {
 void *hl_gc_alloc_gen( hl_type *t, int size, int flags ) {
 	void *ptr;
 	void *ptr;
 	int time = 0;
 	int time = 0;
 	int allocated = 0;
 	int allocated = 0;
-#ifdef HL_BUMP_ALLOC
-	if( !alloc_all ) {
-		int tot = 3<<29;
-		alloc_all = gc_alloc_page_memory(tot);
-		if( !alloc_all ) hl_fatal("Failed to allocate bump memory");
-		alloc_end = alloc_all + tot;
-	}
-	ptr = alloc_all;
-	alloc_all += size;
-	if( alloc_all > alloc_end ) out_of_memory("bump");
-#else
+	gc_global_lock(true);
 	gc_check_mark();
 	gc_check_mark();
 	if( gc_flags & GC_PROFILE ) time = TIMESTAMP();
 	if( gc_flags & GC_PROFILE ) time = TIMESTAMP();
 	ptr = gc_alloc_gen(size, flags, &allocated);
 	ptr = gc_alloc_gen(size, flags, &allocated);
@@ -590,11 +663,11 @@ void *hl_gc_alloc_gen( hl_type *t, int size, int flags ) {
 #	ifdef GC_DEBUG
 #	ifdef GC_DEBUG
 	memset(ptr,0xCD,allocated);
 	memset(ptr,0xCD,allocated);
 #	endif
 #	endif
-#endif
 	if( flags & MEM_ZERO )
 	if( flags & MEM_ZERO )
 		MZERO(ptr,allocated);
 		MZERO(ptr,allocated);
 	else if( MEM_HAS_PTR(flags) && allocated != size )
 	else if( MEM_HAS_PTR(flags) && allocated != size )
 		MZERO((char*)ptr+size,allocated-size); // erase possible pointers after data
 		MZERO((char*)ptr+size,allocated-size); // erase possible pointers after data
+	gc_global_lock(false);
 	if( (gc_flags & GC_TRACK) && gc_track_callback )
 	if( (gc_flags & GC_TRACK) && gc_track_callback )
 		((void (*)(hl_type *,int,int,void*))gc_track_callback)(t,size,flags,ptr);
 		((void (*)(hl_type *,int,int,void*))gc_track_callback)(t,size,flags,ptr);
 	return ptr;
 	return ptr;
@@ -603,7 +676,6 @@ void *hl_gc_alloc_gen( hl_type *t, int size, int flags ) {
 // -------------------------  MARKING ----------------------------------------------------------
 // -------------------------  MARKING ----------------------------------------------------------
 
 
 static float gc_mark_threshold = 0.2f;
 static float gc_mark_threshold = 0.2f;
-static void *gc_stack_top = NULL;
 static int mark_size = 0;
 static int mark_size = 0;
 static unsigned char *mark_data = NULL;
 static unsigned char *mark_data = NULL;
 static void **cur_mark_stack = NULL;
 static void **cur_mark_stack = NULL;
@@ -758,16 +830,35 @@ static void gc_call_finalizers(){
 	}
 	}
 }
 }
 
 
+static void gc_mark_stack( void *start, void *end ) {
+	void **mark_stack = cur_mark_stack;
+	void **stack_head = (void**)start;
+	while( stack_head < (void**)end ) {
+		void *p = *stack_head++;
+		gc_pheader *page = GC_GET_PAGE(p);
+		int bid;
+		if( !page || (((unsigned char*)p - (unsigned char*)page)%page->block_size) != 0 ) continue;
+#		ifdef HL_64
+		if( !INPAGE(p,page) ) continue;
+#		endif
+		bid = (int)((unsigned char*)p - (unsigned char*)page) / page->block_size;
+		if( page->sizes ) {
+			if( page->sizes[bid] == 0 ) continue;
+		} else if( bid < page->first_block )
+			continue;
+		if( (page->bmp[bid>>3] & (1<<(bid&7))) == 0 ) {
+			page->bmp[bid>>3] |= 1<<(bid&7);
+			GC_PUSH_GEN(p,page,bid);
+		}
+	}
+	cur_mark_stack = mark_stack;
+}
+
 static void gc_mark() {
 static void gc_mark() {
-	jmp_buf regs;
-	void **stack_head;
-	void **stack_top = (void**)gc_stack_top;
 	void **mark_stack = cur_mark_stack;
 	void **mark_stack = cur_mark_stack;
 	int mark_bytes = gc_stats.mark_bytes;
 	int mark_bytes = gc_stats.mark_bytes;
 	int pid, i;
 	int pid, i;
 	unsigned char *mark_cur;
 	unsigned char *mark_cur;
-	// save registers
-	setjmp(regs);
 	// prepare mark bits
 	// prepare mark bits
 	if( mark_bytes > mark_size ) {
 	if( mark_bytes > mark_size ) {
 		gc_free_page_memory(mark_data, mark_size);
 		gc_free_page_memory(mark_data, mark_size);
@@ -806,27 +897,16 @@ static void gc_mark() {
 			GC_PUSH_GEN(p,page,bid);
 			GC_PUSH_GEN(p,page,bid);
 		}
 		}
 	}
 	}
-	// scan stack
-	stack_head = (void**)&stack_head;
-	if( stack_head > (void**)&regs ) stack_head = (void**)&regs; // fix for compilers that might inverse variables
-	while( stack_head <= stack_top ) {
-		void *p = *stack_head++;
-		gc_pheader *page = GC_GET_PAGE(p);
-		int bid;
-		if( !page || (((unsigned char*)p - (unsigned char*)page)%page->block_size) != 0 ) continue;
-#		ifdef HL_64
-		if( !INPAGE(p,page) ) continue;
-#		endif
-		bid = (int)((unsigned char*)p - (unsigned char*)page) / page->block_size;
-		if( page->sizes ) {
-			if( page->sizes[bid] == 0 ) continue;
-		} else if( bid < page->first_block )
-			continue;
-		if( (page->bmp[bid>>3] & (1<<(bid&7))) == 0 ) {
-			page->bmp[bid>>3] |= 1<<(bid&7);
-			GC_PUSH_GEN(p,page,bid);
-		}
+
+	// scan threads stacks & registers
+	for(i=0;i<gc_threads.count;i++) {
+		gc_thread *t = gc_threads.threads[i];
+		cur_mark_stack = mark_stack;
+		gc_mark_stack(t->stack_cur,t->stack_top);
+		gc_mark_stack(&t->regs,(void**)&t->regs + (sizeof(jmp_buf) / sizeof(void*) - 1));
+		mark_stack = cur_mark_stack;
 	}
 	}
+
 	cur_mark_stack = mark_stack;
 	cur_mark_stack = mark_stack;
 	if( mark_stack ) gc_flush_mark();
 	if( mark_stack ) gc_flush_mark();
 	gc_call_finalizers();
 	gc_call_finalizers();
@@ -838,9 +918,12 @@ static void gc_mark() {
 
 
 HL_API void hl_gc_major() {
 HL_API void hl_gc_major() {
 	int time = TIMESTAMP(), dt;
 	int time = TIMESTAMP(), dt;
+	gc_global_lock(true);
 	gc_stats.last_mark = gc_stats.total_allocated;
 	gc_stats.last_mark = gc_stats.total_allocated;
 	gc_stats.last_mark_allocs = gc_stats.allocation_count;
 	gc_stats.last_mark_allocs = gc_stats.allocation_count;
+	gc_stop_world(true);
 	gc_mark();
 	gc_mark();
+	gc_stop_world(false);
 	dt = TIMESTAMP() - time;
 	dt = TIMESTAMP() - time;
 	gc_stats.mark_count++;
 	gc_stats.mark_count++;
 	gc_stats.mark_time += dt;
 	gc_stats.mark_time += dt;
@@ -858,6 +941,7 @@ HL_API void hl_gc_major() {
 		last_profile.alloc_time = gc_stats.alloc_time;
 		last_profile.alloc_time = gc_stats.alloc_time;
 		last_profile.total_allocated = gc_stats.total_allocated;
 		last_profile.total_allocated = gc_stats.total_allocated;
 	}
 	}
+	gc_global_lock(false);
 }
 }
 
 
 HL_API bool hl_is_gc_ptr( void *ptr ) {
 HL_API bool hl_is_gc_ptr( void *ptr ) {
@@ -882,9 +966,8 @@ static void gc_check_mark() {
 		hl_gc_major();
 		hl_gc_major();
 }
 }
 
 
-static void hl_gc_init( void *stack_top ) {
+static void hl_gc_init() {
 	int i;
 	int i;
-	gc_stack_top = stack_top;
 	for(i=0;i<1<<GC_LEVEL0_BITS;i++)
 	for(i=0;i<1<<GC_LEVEL0_BITS;i++)
 		hl_gc_page_map[i] = gc_level1_null;
 		hl_gc_page_map[i] = gc_level1_null;
 	if( TRAILING_ONES(0x080003FF) != 10 || TRAILING_ONES(0) != 0 || TRAILING_ONES(0xFFFFFFFF) != 32 )
 	if( TRAILING_ONES(0x080003FF) != 10 || TRAILING_ONES(0) != 0 || TRAILING_ONES(0xFFFFFFFF) != 32 )
@@ -897,22 +980,37 @@ static void hl_gc_init( void *stack_top ) {
 	if( getenv("HL_DUMP_MEMORY") )
 	if( getenv("HL_DUMP_MEMORY") )
 		gc_flags |= GC_DUMP_MEM;
 		gc_flags |= GC_DUMP_MEM;
 #	endif
 #	endif
+	memset(&gc_threads,0,sizeof(gc_threads));
+	gc_threads.global_lock = hl_lock_alloc();
+	gc_threads.cur_thread = hl_tls_alloc();
 }
 }
 
 
 // ---- UTILITIES ----------------------
 // ---- UTILITIES ----------------------
 
 
-static bool is_blocking = false; // TODO : use TLS for multithread
-
 HL_API bool hl_is_blocking() {
 HL_API bool hl_is_blocking() {
-	return is_blocking;
+	gc_thread *t = gc_get_thread();
+	// when called from a non GC thread, tells if the main thread is blocking
+	if( t == NULL ) {
+		if( gc_threads.count == 0 )
+			return false;
+		t = gc_threads.threads[0];
+	}
+	return t->blocking > 0;
 }
 }
 
 
-HL_API void hl_blocking( bool b) {
-	is_blocking = b;
+HL_API void hl_blocking( bool b ) {
+	gc_thread *t = gc_get_thread();
+	if( !t ) hl_error("Unregistered thread");
+	if( b )
+		t->blocking++; 
+	else if( t->blocking == 0 ) 
+		hl_error("Unblocked thread");
+	else
+		t->blocking--;
 }
 }
 
 
-void hl_global_init( void *stack_top ) {
-	hl_gc_init(stack_top);
+void hl_global_init() {
+	hl_gc_init();
 }
 }
 
 
 void hl_cache_free();
 void hl_cache_free();
@@ -1149,6 +1247,8 @@ HL_API void hl_gc_set_dump_types( hl_types_dump tdump ) {
 
 
 HL_API void hl_gc_dump_memory( const char *filename ) {
 HL_API void hl_gc_dump_memory( const char *filename ) {
 	int i;
 	int i;
+	gc_global_lock(true);
+	gc_stop_world(true);
 	gc_mark();
 	gc_mark();
 	fdump = fopen(filename,"wb");
 	fdump = fopen(filename,"wb");
 	// header
 	// header
@@ -1179,13 +1279,13 @@ HL_API void hl_gc_dump_memory( const char *filename ) {
 	for(i=0;i<gc_roots_count;i++)
 	for(i=0;i<gc_roots_count;i++)
 		fdump_p(*gc_roots[i]);
 		fdump_p(*gc_roots[i]);
 	// stacks
 	// stacks
-	fdump_i(1);
-	fdump_p(gc_stack_top);
-	{
-		void **stack_head = (void**)&stack_head;
-		int size = (int)((void**)gc_stack_top - stack_head);
+	fdump_i(gc_threads.count);
+	for(i=0;i<gc_threads.count;i++) {
+		gc_thread *t = gc_threads.threads[i];
+		fdump_p(t->stack_top);
+		int size = (int)((void**)t->stack_top - (void**)t->stack_cur);
 		fdump_i(size);
 		fdump_i(size);
-		fdump_d(stack_head,size*sizeof(void*));
+		fdump_d(t->stack_cur,size*sizeof(void*));
 	}
 	}
 	// types
 	// types
 #	define fdump_t(t)	fdump_i(t.kind); fdump_p(&t);
 #	define fdump_t(t)	fdump_i(t.kind); fdump_p(&t);
@@ -1201,6 +1301,8 @@ HL_API void hl_gc_dump_memory( const char *filename ) {
 	if( gc_types_dump ) gc_types_dump(fdump_d);
 	if( gc_types_dump ) gc_types_dump(fdump_d);
 	fclose(fdump);
 	fclose(fdump);
 	fdump = NULL;
 	fdump = NULL;
+	gc_stop_world(false);
+	gc_global_lock(false);
 }
 }
 
 
 HL_API vdynamic *hl_debug_call( int mode, vdynamic *v ) {
 HL_API vdynamic *hl_debug_call( int mode, vdynamic *v ) {

+ 5 - 2
src/debugger.c

@@ -35,7 +35,7 @@ HL_API int hl_socket_recv( hl_socket *s, vbyte *buf, int pos, int len );
 HL_API void hl_sys_sleep( double t );
 HL_API void hl_sys_sleep( double t );
 HL_API int hl_thread_id();
 HL_API int hl_thread_id();
 HL_API vdynamic **hl_debug_exc;
 HL_API vdynamic **hl_debug_exc;
-HL_API void *hl_gc_thread_info();
+HL_API void *hl_gc_threads_info();
 
 
 static hl_socket *debug_socket = NULL;
 static hl_socket *debug_socket = NULL;
 static hl_socket *client_socket = NULL;
 static hl_socket *client_socket = NULL;
@@ -48,12 +48,15 @@ static void send( void *ptr, int size ) {
 
 
 static void hl_debug_loop( hl_module *m ) {
 static void hl_debug_loop( hl_module *m ) {
 	void *dbg_addr = &hl_debug_exc;
 	void *dbg_addr = &hl_debug_exc;
-	void *inf_addr = hl_gc_thread_info();
+	void *inf_addr = hl_gc_threads_info();
 	int flags = 0;
 	int flags = 0;
 #	ifdef HL_64
 #	ifdef HL_64
 	flags |= 1;
 	flags |= 1;
 #	endif
 #	endif
 	if( sizeof(bool) == 4 ) flags |= 2;
 	if( sizeof(bool) == 4 ) flags |= 2;
+#	ifdef HL_THREADS
+	flags |= 4;
+#	endif
 	while( true ) {
 	while( true ) {
 		int i;
 		int i;
 		vbyte cmd;
 		vbyte cmd;

+ 22 - 4
src/hl.h

@@ -120,6 +120,10 @@
 #	define HL_DEBUG
 #	define HL_DEBUG
 #endif
 #endif
 
 
+#ifndef HL_NO_THREADS
+#	define HL_THREADS
+#endif
+
 #include <stddef.h>
 #include <stddef.h>
 #ifndef HL_VCC
 #ifndef HL_VCC
 #	include <stdint.h>
 #	include <stdint.h>
@@ -600,13 +604,28 @@ HL_API vdynamic *hl_dyn_call_safe( vclosure *c, vdynamic **args, int nargs, bool
 // ----------------------- THREADS --------------------------------------------------
 // ----------------------- THREADS --------------------------------------------------
 
 
 struct _hl_thread;
 struct _hl_thread;
+struct _hl_lock;
+struct _hl_tls;
 typedef struct _hl_thread hl_thread;
 typedef struct _hl_thread hl_thread;
+typedef struct _hl_lock hl_lock;
+typedef struct _hl_tls hl_tls;
 
 
 HL_API hl_thread *hl_thread_start( void *callback, void *param, bool withGC );
 HL_API hl_thread *hl_thread_start( void *callback, void *param, bool withGC );
 HL_API hl_thread *hl_thread_current( void );
 HL_API hl_thread *hl_thread_current( void );
 HL_API void hl_register_thread( void *stack_top );
 HL_API void hl_register_thread( void *stack_top );
-HL_API void hl_unregister_thread();
-HL_API void *hl_gc_stack_top();
+HL_API void hl_unregister_thread( void );
+HL_API void *hl_gc_stack_top( void );
+
+HL_API hl_lock *hl_lock_alloc( void );
+HL_API void hl_lock_acquire( hl_lock *l );
+HL_API bool hl_lock_try_acquire( hl_lock *l );
+HL_API void hl_lock_release( hl_lock *l );
+HL_API void hl_lock_free( hl_lock *l );
+
+HL_API hl_tls *hl_tls_alloc( void );
+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 );
 
 
 // ----------------------- ALLOC --------------------------------------------------
 // ----------------------- ALLOC --------------------------------------------------
 
 
@@ -620,7 +639,6 @@ HL_API void *hl_gc_stack_top();
 
 
 HL_API void *hl_gc_alloc_gen( hl_type *t, int size, int flags );
 HL_API void *hl_gc_alloc_gen( hl_type *t, int size, int flags );
 HL_API void hl_add_root( void *ptr );
 HL_API void hl_add_root( void *ptr );
-HL_API void hl_pop_root( void );
 HL_API void hl_remove_root( void *ptr );
 HL_API void hl_remove_root( void *ptr );
 HL_API void hl_gc_major( void );
 HL_API void hl_gc_major( void );
 HL_API bool hl_is_gc_ptr( void *ptr );
 HL_API bool hl_is_gc_ptr( void *ptr );
@@ -641,7 +659,7 @@ HL_API void *hl_malloc( hl_alloc *a, int size );
 HL_API void *hl_zalloc( hl_alloc *a, int size );
 HL_API void *hl_zalloc( hl_alloc *a, int size );
 HL_API void hl_free( hl_alloc *a );
 HL_API void hl_free( hl_alloc *a );
 
 
-HL_API void hl_global_init( void *stack_top );
+HL_API void hl_global_init( void );
 HL_API void hl_global_free( void );
 HL_API void hl_global_free( void );
 
 
 HL_API void *hl_alloc_executable_memory( int size );
 HL_API void *hl_alloc_executable_memory( int size );

+ 6 - 2
src/main.c

@@ -83,6 +83,9 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 
 
 #endif
 #endif
 
 
+HL_PRIM double hl_sys_time();
+static int K = 0;
+
 #ifdef HL_WIN
 #ifdef HL_WIN
 int wmain(int argc, pchar *argv[]) {
 int wmain(int argc, pchar *argv[]) {
 #else
 #else
@@ -101,6 +104,7 @@ int main(int argc, pchar *argv[]) {
 	int first_boot_arg = -1;
 	int first_boot_arg = -1;
 	argv++;
 	argv++;
 	argc--;
 	argc--;
+
 	while( argc ) {
 	while( argc ) {
 		pchar *arg = *argv++;
 		pchar *arg = *argv++;
 		argc--;
 		argc--;
@@ -146,9 +150,9 @@ int main(int argc, pchar *argv[]) {
 #	ifdef HL_WIN
 #	ifdef HL_WIN
 	setlocale(LC_CTYPE,""); // printf to current locale
 	setlocale(LC_CTYPE,""); // printf to current locale
 #	endif
 #	endif
-	hl_global_init(&ctx);
+	hl_global_init();
 	hl_sys_init((void**)argv,argc,file);
 	hl_sys_init((void**)argv,argc,file);
-	hl_register_thread(&argc);
+	hl_register_thread(&ctx);
 	setbuf(stdout,NULL); // disable stdout buffering
 	setbuf(stdout,NULL); // disable stdout buffering
 	ctx.code = load_code(file);
 	ctx.code = load_code(file);
 	if( ctx.code == NULL )
 	if( ctx.code == NULL )

+ 1 - 1
src/std/maps.h

@@ -108,7 +108,7 @@ static void _MNAME(resize)( t_map *m ) {
 				c = old.entries[c].next;
 				c = old.entries[c].next;
 			}
 			}
 		}
 		}
-		hl_pop_root();
+		hl_remove_root(&old);
 	}
 	}
 }
 }
 
 

+ 158 - 16
src/std/thread.c

@@ -21,31 +21,169 @@
  */
  */
 #include <hl.h>
 #include <hl.h>
 
 
-#ifndef HL_WIN
+#if !defined(HL_THREADS)
+
+struct _hl_lock {
+	void *_unused;
+};
+
+struct _hl_tls {
+	void *value;
+};
+
+
+#elif defined(HL_WIN)
+
+struct _hl_lock {
+	CRITICAL_SECTION cs;
+};
+
+#else
+
 #	include <pthread.h>
 #	include <pthread.h>
 #	include <unistd.h>
 #	include <unistd.h>
-#	include <sys/syscall.h>
+#	include <sys/syscall.h>
+
+struct _hl_lock {
+	pthread_mutex_t lock;
+};
+
+struct _hl_tls {
+	pthread_key_t key;
+};
 #endif
 #endif
 
 
+// ----------------- ALLOC
+
+HL_PRIM hl_lock *hl_lock_alloc() {
+#	if !defined(HL_THREADS)
+	return (hl_lock*)1;
+#	elif defined(HL_WIN)
+	hl_lock *l = (hl_lock*)malloc(sizeof(hl_lock));
+	InitializeCriticalSection(&l->cs);
+	return l;
+#	else
+	hl_lock *l = (hl_lock*)malloc(sizeof(hl_lock));
+	pthread_mutexattr_t a;
+	pthread_mutexattr_init(&a);
+	pthread_mutexattr_settype(&a,PTHREAD_MUTEX_RECURSIVE);
+	pthread_mutex_init(&l->lock,&a);
+	pthread_mutexattr_destroy(&a);
+	return l;
+#	endif
+}
+
+HL_PRIM void hl_lock_acquire( hl_lock *l ) {
+#	if !defined(HL_THREADS)
+#	elif defined(HL_WIN)
+	EnterCriticalSection(&l->cs);
+#	else
+	pthread_mutex_lock(&l->lock);
+#	endif
+}
+
+HL_PRIM bool hl_lock_try_acquire( hl_lock *l ) {
+#if	!defined(HL_THREADS)
+	return true;
+#	elif defined(HL_WIN)
+	return (bool)TryEnterCriticalSection(&l->cs);
+#	else
+	return pthread_mutex_trylock(&l->lock) == 0;
+#	endif
+}
+
+HL_PRIM void hl_lock_release( hl_lock *l ) {
+#	if !defined(HL_THREADS)
+#	elif defined(HL_WIN)
+	LeaveCriticalSection(&l->cs);
+#	else
+	pthread_mutex_unlock(&l->lock);
+#	endif
+}
+
+HL_PRIM void hl_lock_free( hl_lock *l ) {
+#	if !defined(HL_THREADS)
+#	elif defined(HL_WIN)
+	DeleteCriticalSection(&l->cs);
+	free(l);
+#	else
+	pthread_mutex_destroy(&l->lock);
+	free(l);
+#	endif
+}
+
+// ----------------- THREAD LOCAL
+
+HL_PRIM hl_tls *hl_tls_alloc() {
+#	if !defined(HL_THREADS)
+	hl_tls *l = malloc(sizeof(hl_tls));
+	l->value = NULL;
+	return l;
+#	elif defined(HL_WIN)
+	DWORD t = TlsAlloc();
+	TlsSetValue(t,NULL);
+	return (hl_tls*)(int_val)t;
+#	else
+	hl_tls *l = malloc(sizeof(hl_tls));
+	pthread_key_create(&l->key,NULL);
+	return l;
+#	endif
+}
+
+HL_PRIM void hl_tls_free( hl_tls *l ) {
+#	if !defined(HL_THREADS)
+	free(l);
+#	elif defined(HL_WIN)
+	TlsFree((DWORD)(int_val)l);
+#	else
+	pthread_key_delete(l->key);
+	free(l);
+#	endif
+}
+
+HL_PRIM void hl_tls_set( hl_tls *l, void *v ) {
+#	if !defined(HL_THREADS)
+	l->value = v;
+#	elif defined(HL_WIN)
+	TlsSetValue((DWORD)(int_val)l,v);
+#	else
+	pthread_setspecific(l->key,v);
+#	endif
+}
+
+HL_PRIM void *hl_tls_get( hl_tls *l ) {
+#	if !defined(HL_THREADS)
+	return l->value;
+#	elif defined(HL_WIN)
+	return (void*)TlsGetValue((DWORD)(int_val)l);
+#	else
+	return pthread_getspecific(l->key);
+#	endif
+}
+
+// ----------------- THREAD
+
 HL_PRIM hl_thread *hl_thread_current() {
 HL_PRIM hl_thread *hl_thread_current() {
-#	ifdef HL_WIN
+#if !defined(HL_THREADS)
+	return NULL;
+#elif defined(HL_WIN)
 	return (hl_thread*)(int_val)GetCurrentThreadId();
 	return (hl_thread*)(int_val)GetCurrentThreadId();
-#	else
+#else
 	return (hl_thread*)pthread_self();
 	return (hl_thread*)pthread_self();
-#	endif
+#endif
 }
 }
 
 
 HL_PRIM int hl_thread_id() {
 HL_PRIM int hl_thread_id() {
-#	ifdef HL_WIN
+#if !defined(HL_THREADS)
+	return 0;
+#elif defined(HL_WIN)
 	return (int)GetCurrentThreadId();
 	return (int)GetCurrentThreadId();
-#	else
-#	if defined(SYS_gettid) && !defined(HL_TVOS)
+#elif defined(SYS_gettid) && !defined(HL_TVOS)
 	return syscall(SYS_gettid);
 	return syscall(SYS_gettid);
-#	else
+#else
 	hl_error("hl_thread_id() not available for this platform");
 	hl_error("hl_thread_id() not available for this platform");
 	return -1;
 	return -1;
-#	endif
-#	endif
+#endif
 }
 }
 
 
 typedef struct {
 typedef struct {
@@ -56,10 +194,10 @@ typedef struct {
 static void gc_thread_entry( thread_start *_s ) {
 static void gc_thread_entry( thread_start *_s ) {
 	thread_start s = *_s;
 	thread_start s = *_s;
 	hl_register_thread(&s);
 	hl_register_thread(&s);
-	free(_s);
 	hl_remove_root(&_s->param);
 	hl_remove_root(&_s->param);
+	free(_s);
 	s.callb(s.param);
 	s.callb(s.param);
-	hl_unregister_thread(&s);
+	hl_unregister_thread();
 }
 }
 
 
 HL_PRIM hl_thread *hl_thread_start( void *callback, void *param, bool withGC ) {
 HL_PRIM hl_thread *hl_thread_start( void *callback, void *param, bool withGC ) {
@@ -71,14 +209,18 @@ HL_PRIM hl_thread *hl_thread_start( void *callback, void *param, bool withGC ) {
 		callback = gc_thread_entry;
 		callback = gc_thread_entry;
 		param = s;
 		param = s;
 	}
 	}
-#	ifdef HL_WIN
+#if !defined(HL_THREADS)
+	hl_pop_root();
+	hl_error("Threads support is disabled");
+	return NULL;
+#elif defined(HL_WIN)
 	DWORD tid;
 	DWORD tid;
 	HANDLE h = CreateThread(NULL,0,callback,param,0,&tid);
 	HANDLE h = CreateThread(NULL,0,callback,param,0,&tid);
 	if( h == NULL )
 	if( h == NULL )
 		return NULL;
 		return NULL;
 	CloseHandle(h);
 	CloseHandle(h);
 	return (hl_thread*)(int_val)tid;
 	return (hl_thread*)(int_val)tid;
-#	else
+#else
 	pthread_t t;
 	pthread_t t;
 	pthread_attr_t attr;
 	pthread_attr_t attr;
 	pthread_attr_init(&attr);
 	pthread_attr_init(&attr);
@@ -89,7 +231,7 @@ HL_PRIM hl_thread *hl_thread_start( void *callback, void *param, bool withGC ) {
 	}
 	}
 	pthread_attr_destroy(&attr);
 	pthread_attr_destroy(&attr);
 	return (hl_thread*)t;
 	return (hl_thread*)t;
-#	endif
+#endif
 }
 }
 
 
 static void hl_run_thread( vclosure *c ) {
 static void hl_run_thread( vclosure *c ) {