about summary refs log tree commit diff stats
path: root/src/libtools/threads.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libtools/threads.c')
-rw-r--r--src/libtools/threads.c220
1 files changed, 101 insertions, 119 deletions
diff --git a/src/libtools/threads.c b/src/libtools/threads.c
index c9d788e5..ed0d2b0b 100644
--- a/src/libtools/threads.c
+++ b/src/libtools/threads.c
@@ -24,6 +24,7 @@
 #include "x64trace.h"
 #include "dynarec.h"
 #include "bridge.h"
+#include "myalign.h"
 #ifdef DYNAREC
 #include "dynablock.h"
 #include "dynarec/native_lock.h"
@@ -53,21 +54,6 @@ typedef struct threadstack_s {
 	size_t 	stacksize;
 } threadstack_t;
 
-// longjmp / setjmp
-typedef struct jump_buff_x64_s {
-	uint64_t save_reg[8];
-} jump_buff_x64_t;
-
-typedef struct __jmp_buf_tag_s {
-    jump_buff_x64_t  __jmpbuf;
-    int              __mask_was_saved;
-	#ifdef ANDROID
-	sigset_t       	 __saved_mask;
-	#else
-    __sigset_t       __saved_mask;
-	#endif
-} __jmp_buf_tag_t;
-
 typedef struct x64_unwind_buff_s {
 	struct {
 		jump_buff_x64_t		__cancel_jmp_buf;	
@@ -76,6 +62,15 @@ typedef struct x64_unwind_buff_s {
 	void *__pad[4];
 } x64_unwind_buff_t __attribute__((__aligned__));
 
+typedef struct my_tls_keys_s {
+	pthread_key_t	key;
+	uintptr_t		f;
+} my_tls_keys_t;
+
+static int keys_cap = 0;
+static int keys_size = 0;
+static my_tls_keys_t *keys = NULL;
+
 typedef void(*vFv_t)();
 
 KHASH_MAP_INIT_INT64(threadstack, threadstack_t*)
@@ -139,52 +134,61 @@ int GetStackSize(x64emu_t* emu, uintptr_t attr, void** stack, size_t* stacksize)
 	return 0;
 }
 
-static void InitCancelThread()
-{
-}
-
-static void FreeCancelThread(box64context_t* context)
-{
-	if(!context)
-		return;
-}
-
-#ifndef ANDROID
-static __pthread_unwind_buf_t* AddCancelThread(x64_unwind_buff_t* buff)
-{
-	__pthread_unwind_buf_t* r = (__pthread_unwind_buf_t*)box_calloc(1, sizeof(__pthread_unwind_buf_t));
-	buff->__pad[3] = r;
-	return r;
-}
-
-static __pthread_unwind_buf_t* GetCancelThread(x64_unwind_buff_t* buff)
-{
-	return (__pthread_unwind_buf_t*)buff->__pad[3];
-}
-
-static void DelCancelThread(x64_unwind_buff_t* buff)
-{
-	__pthread_unwind_buf_t* r = (__pthread_unwind_buf_t*)buff->__pad[3];
-	box_free(r);
-	buff->__pad[3] = NULL;
-}
-#endif
+void my_longjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val);
 
 typedef struct emuthread_s {
 	uintptr_t 	fnc;
 	void*		arg;
 	x64emu_t*	emu;
+	int			cancel_cap, cancel_size;
+	x64_unwind_buff_t **cancels;
 } emuthread_t;
 
 static void emuthread_destroy(void* p)
 {
 	emuthread_t *et = (emuthread_t*)p;
+	if(!et)
+		return;
+	void* ptr;
+	// check all tls keys
+	int end = 4;
+	while(end) {
+		int still = 0;
+		for(int i=0; i<keys_size; ++i) {
+			ptr = pthread_getspecific(keys[i].key);
+			if(ptr) {
+				still = 1;
+				RunFunctionWithEmu(et->emu, 0, keys[i].f, 1, ptr);
+			}
+		}
+		/*if(still) --end; else*/ end=0;
+	}
+	// check tlsdata
+	if (my_context && (ptr = pthread_getspecific(my_context->tlskey)) != NULL)
+        free_tlsdatasize(ptr);
+	// free x64emu
 	if(et) {
 		FreeX64Emu(&et->emu);
 		box_free(et);
 	}
 }
 
+static void emuthread_cancel(void* p)
+{
+	emuthread_t *et = (emuthread_t*)p;
+	if(!et)
+		return;
+	// check cancels threads
+	for(int i=et->cancel_size-1; i>=0; --i) {
+		et->emu->flags.quitonlongjmp = 0;
+		my_longjmp(et->emu, et->cancels[i]->__cancel_jmp_buf, 1);
+		DynaRun(et->emu);	// will return after a __pthread_unwind_next()
+	}
+	box_free(et->cancels);
+	et->cancels=NULL;
+	et->cancel_size = et->cancel_cap = 0;
+}
+
 static pthread_key_t thread_key;
 
 void thread_set_emu(x64emu_t* emu)
@@ -257,7 +261,9 @@ static void* pthread_routine(void* p)
 	PushExit(emu);
 	R_RIP = et->fnc;
 	R_RDI = (uintptr_t)et->arg;
+	pthread_cleanup_push(emuthread_cancel, p);
 	DynaRun(emu);
+	pthread_cleanup_pop(0);
 	void* ret = (void*)R_RAX;
 	//void* ret = (void*)RunFunctionWithEmu(et->emu, 0, et->fnc, 1, et->arg);
 	return ret;
@@ -537,62 +543,32 @@ void* my_prepare_thread(x64emu_t *emu, void* f, void* arg, int ssize, void** pet
 	return pthread_routine;
 }
 
-void my_longjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val);
-
-#ifndef ANDROID
-#define CANCEL_MAX 8
-static __thread x64emu_t* cancel_emu[CANCEL_MAX] = {0};
-static __thread x64_unwind_buff_t* cancel_buff[CANCEL_MAX] = {0};
-static __thread int cancel_deep = 0;
-EXPORT void my___pthread_register_cancel(void* E, void* B)
-{
-	// get a stack local copy of the args, as may be live in some register depending the architecture (like ARM)
-	if(cancel_deep<0) {
-		printf_log(LOG_NONE/*LOG_INFO*/, "BOX64: Warning, inconsistent value in __pthread_register_cancel (%d)\n", cancel_deep);
-		cancel_deep = 0;
-	}
-	if(cancel_deep!=CANCEL_MAX-1) 
-		++cancel_deep;
-	else
-		{printf_log(LOG_NONE/*LOG_INFO*/, "BOX64: Warning, calling __pthread_register_cancel(...) too many time\n");}
-		
-	cancel_emu[cancel_deep] = (x64emu_t*)E;
-
-	x64_unwind_buff_t* buff = cancel_buff[cancel_deep] = (x64_unwind_buff_t*)B;
-	__pthread_unwind_buf_t * pbuff = AddCancelThread(buff);
-	if(__sigsetjmp((struct __jmp_buf_tag*)(void*)pbuff->__cancel_jmp_buf, 0)) {
-		//DelCancelThread(cancel_buff);	// no del here, it will be delete by unwind_next...
-		int i = cancel_deep--;
-		x64emu_t* emu = cancel_emu[i];
-		my_longjmp(emu, cancel_buff[i]->__cancel_jmp_buf, 1);
-		DynaRun(emu);	// resume execution // TODO: Use ejb instead?
-		return;
+EXPORT void my___pthread_register_cancel(x64emu_t* emu, x64_unwind_buff_t* buff)
+{
+	emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key);
+	if(et->cancel_cap == et->cancel_size) {
+		et->cancel_cap+=8;
+		et->cancels = box_realloc(et->cancels, sizeof(x64_unwind_buff_t*)*et->cancel_cap);
 	}
-
-	__pthread_register_cancel(pbuff);
+	et->cancels[et->cancel_size++] = buff;
 }
 
 EXPORT void my___pthread_unregister_cancel(x64emu_t* emu, x64_unwind_buff_t* buff)
 {
-	(void)emu;
-	__pthread_unwind_buf_t * pbuff = GetCancelThread(buff);
-	__pthread_unregister_cancel(pbuff);
-
-	--cancel_deep;
-	DelCancelThread(buff);
+	emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key);
+	for (int i=et->cancel_size-1; i>=0; --i) {
+		if(et->cancels[i] == buff) {
+			if(i!=et->cancel_size-1)
+				memmove(et->cancels+i, et->cancels+i+1, sizeof(x64_unwind_buff_t*)*(et->cancel_size-i-1));
+			et->cancel_size--;
+		}
+	}
 }
 
 EXPORT void my___pthread_unwind_next(x64emu_t* emu, x64_unwind_buff_t* buff)
 {
-	(void)emu;
-	__pthread_unwind_buf_t pbuff = *GetCancelThread(buff);
-	DelCancelThread(buff);
-	// function is noreturn, putting stuff on the stack to have it auto-free (is that correct?)
-	__pthread_unwind_next(&pbuff);
-	// just in case it does return
 	emu->quit = 1;
 }
-#endif
 
 KHASH_MAP_INIT_INT(once, int)
 
@@ -628,28 +604,6 @@ GO(27)			\
 GO(28)			\
 GO(29)			
 
-// key_destructor
-#define GO(A)   \
-static uintptr_t my_key_destructor_fct_##A = 0;  \
-static void my_key_destructor_##A(void* a)    			\
-{                                       		\
-    RunFunction(my_key_destructor_fct_##A, 1, a);\
-}
-SUPER()
-#undef GO
-static void* findkey_destructorFct(void* fct)
-{
-    if(!fct) return fct;
-    if(GetNativeFnc((uintptr_t)fct))  return GetNativeFnc((uintptr_t)fct);
-    #define GO(A) if(my_key_destructor_fct_##A == (uintptr_t)fct) return my_key_destructor_##A;
-    SUPER()
-    #undef GO
-    #define GO(A) if(my_key_destructor_fct_##A == 0) {my_key_destructor_fct_##A = (uintptr_t)fct; return my_key_destructor_##A; }
-    SUPER()
-    #undef GO
-    printf_log(LOG_NONE, "Warning, no more slot for pthread key_destructor callback\n");
-    return NULL;
-}
 // cleanup_routine
 #define GO(A)   \
 static uintptr_t my_cleanup_routine_fct_##A = 0;  \
@@ -698,12 +652,35 @@ int EXPORT my_pthread_once(x64emu_t* emu, int* once, void* cb)
 }
 EXPORT int my___pthread_once(x64emu_t* emu, void* once, void* cb) __attribute__((alias("my_pthread_once")));
 
-EXPORT int my_pthread_key_create(x64emu_t* emu, void* key, void* dtor)
+EXPORT int my_pthread_key_create(x64emu_t* emu, pthread_key_t* key, void* dtor)
 {
 	(void)emu;
-	return pthread_key_create(key, findkey_destructorFct(dtor));
+	int ret = pthread_key_create(key, NULL/*findkey_destructorFct(dtor)*/);
+	if(!ret && dtor) {
+		if(keys_cap==keys_size) {
+			keys_cap += 8;
+			keys = box_realloc(keys, sizeof(my_tls_keys_t)*keys_cap);
+		}
+		keys[keys_size].f = (uintptr_t)dtor;
+		keys[keys_size++].key = *key;
+	}
+	return ret;
+}
+EXPORT int my___pthread_key_create(x64emu_t* emu, pthread_key_t* key, void* dtor) __attribute__((alias("my_pthread_key_create")));
+
+EXPORT int my_pthread_key_delete(x64emu_t* emu, pthread_key_t key)
+{
+	int ret = pthread_key_delete(key);
+	if(ret) {
+		for(int i=keys_size-1; i>=0; --i)
+			if(keys[i].key == key) {
+				if(i!=keys_size-1)
+					memmove(keys+i, keys+i+1, sizeof(my_tls_keys_t)*(keys_size-(i+1)));
+				--keys_size;
+			}
+	}
+	return ret;
 }
-EXPORT int my___pthread_key_create(x64emu_t* emu, void* key, void* dtor) __attribute__((alias("my_pthread_key_create")));
 
 pthread_cond_t* alignCond(pthread_cond_t* pc)
 {
@@ -1106,15 +1083,12 @@ void init_pthread_helper()
 		real_phtread_kill_old = (iFli_t)pthread_kill;
 	}
 
-	InitCancelThread();
 	pthread_key_create(&thread_key, emuthread_destroy);
 	pthread_setspecific(thread_key, NULL);
 }
 
-void fini_pthread_helper(box64context_t* context)
+void clean_current_emuthread()
 {
-	FreeCancelThread(context);
-	CleanStackSize(context);
 	emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key);
 	if(et) {
 		pthread_setspecific(thread_key, NULL);
@@ -1122,6 +1096,14 @@ void fini_pthread_helper(box64context_t* context)
 	}
 }
 
+void fini_pthread_helper(box64context_t* context)
+{
+	box_free(keys);
+	keys_size = keys_cap = 0;
+	CleanStackSize(context);
+	clean_current_emuthread();
+}
+
 int checkUnlockMutex(void* m)
 {
 	pthread_mutex_t* mutex = (pthread_mutex_t*)m;