diff --git a/src/lib/eina/eina_inline_lock_posix.x b/src/lib/eina/eina_inline_lock_posix.x index 5270f467fb..b9136fcef5 100644 --- a/src/lib/eina/eina_inline_lock_posix.x +++ b/src/lib/eina/eina_inline_lock_posix.x @@ -495,14 +495,20 @@ eina_rwlock_release(Eina_RWLock *mutex) return EINA_LOCK_SUCCEED; } -static inline Eina_Bool -eina_tls_new(Eina_TLS *key) +static inline Eina_Bool +eina_tls_cb_new(Eina_TLS *key, Eina_TLS_Delete_Cb delete_cb) { - if (pthread_key_create(key, NULL) != 0) + if (pthread_key_create(key, delete_cb) != 0) return EINA_FALSE; return EINA_TRUE; } +static inline Eina_Bool +eina_tls_new(Eina_TLS *key) +{ + return eina_tls_cb_new(key, NULL); +} + static inline void eina_tls_free(Eina_TLS key) { diff --git a/src/lib/eina/eina_inline_lock_win32.x b/src/lib/eina/eina_inline_lock_win32.x index 6feb60d649..3123ad6d9b 100644 --- a/src/lib/eina/eina_inline_lock_win32.x +++ b/src/lib/eina/eina_inline_lock_win32.x @@ -71,6 +71,9 @@ struct _Eina_RWLock EAPI extern Eina_Bool _eina_threads_activated; +EAPI extern Eina_Bool _eina_thread_tls_cb_register(Eina_TLS key, Eina_TLS_Delete_Cb cb); +EAPI extern Eina_Bool _eina_thread_tls_cb_unregister(Eina_TLS key); +EAPI extern Eina_Bool _eina_thread_tls_key_add(Eina_TLS key); static inline Eina_Bool @@ -481,16 +484,31 @@ eina_rwlock_release(Eina_RWLock *mutex) } static inline Eina_Bool -eina_tls_new(Eina_TLS *key) +eina_tls_cb_new(Eina_TLS *key, Eina_TLS_Delete_Cb delete_cb) { if ((*key = TlsAlloc()) == TLS_OUT_OF_INDEXES) return EINA_FALSE; + if (delete_cb) + { + if (!_eina_thread_tls_cb_register(*key, delete_cb)) + { + TlsFree(key); + return EINA_FALSE; + } + } return EINA_TRUE; } +static inline Eina_Bool +eina_tls_new(Eina_TLS *key) +{ + return eina_tls_cb_new(key, NULL); +} + static inline void eina_tls_free(Eina_TLS key) { + _eina_thread_tls_cb_unregister(key); TlsFree(key); } @@ -505,6 +523,7 @@ eina_tls_set(Eina_TLS key, const void *data) { if (TlsSetValue(key, (LPVOID)data) == 0) return EINA_FALSE; + _eina_thread_tls_key_add(key); return EINA_TRUE; } diff --git a/src/lib/eina/eina_inline_lock_wince.x b/src/lib/eina/eina_inline_lock_wince.x index 3b732568da..3d96a84533 100644 --- a/src/lib/eina/eina_inline_lock_wince.x +++ b/src/lib/eina/eina_inline_lock_wince.x @@ -26,6 +26,9 @@ #undef WIN32_LEAN_AND_MEAN EAPI extern Eina_Bool _threads_activated; +EAPI extern Eina_Bool _eina_thread_tls_cb_register(Eina_TLS key, Eina_TLS_Delete_Cb cb); +EAPI extern Eina_Bool _eina_thread_tls_cb_unregister(Eina_TLS key); +EAPI extern Eina_Bool _eina_thread_tls_key_add(Eina_TLS key); typedef HANDLE Eina_Lock; typedef Eina_Lock Eina_Spinlock; @@ -151,17 +154,32 @@ eina_rwlock_release(Eina_RWLock *mutex) return eina_lock_release(mutex); } -static inline Eina_Bool +static inline Eina_Bool +eina_tls_cb_new(Eina_TLS *key, Eina_TLS_Delete_Cb delete_cb) +{ + if ((*key = TlsAlloc()) == TLS_OUT_OF_INDEXES) + return EINA_FALSE; + if (delete_cb) + { + if (!_eina_thread_tls_cb_register(*key, delete_cb)) + { + TlsFree(key); + return EINA_FALSE; + } + } + return EINA_TRUE; +} + +static inline Eina_Bool eina_tls_new(Eina_TLS *key) { - if (TlsAlloc() == TLS_OUT_OF_INDEXES) - return EINA_FALSE; - return EINA_TRUE; + return eina_tls_cb_new(key, NULL); } static inline void eina_tls_free(Eina_TLS key) { + _eina_thread_tls_cb_unregister(key); TlsFree(key); } @@ -176,6 +194,7 @@ eina_tls_set(Eina_TLS key, const void *data) { if (TlsSetValue(key, (LPVOID)data) == 0) return EINA_FALSE; + _eina_thread_tls_key_add(key); return EINA_TRUE; } diff --git a/src/lib/eina/eina_lock.h b/src/lib/eina/eina_lock.h index b0afa4193f..ac0c8b1bb3 100644 --- a/src/lib/eina/eina_lock.h +++ b/src/lib/eina/eina_lock.h @@ -42,6 +42,8 @@ typedef enum EINA_LOCK_DEADLOCK } Eina_Lock_Result; +typedef void (*Eina_TLS_Delete_Cb)(void *ptr); + #ifdef _WIN32_WCE # include "eina_inline_lock_wince.x" #elif defined(_WIN32) @@ -91,6 +93,8 @@ static inline Eina_Lock_Result eina_rwlock_release(Eina_RWLock *mutex); /** @relates static Eina_Bool eina_tls_new(pthread_key_t *key) */ static inline Eina_Bool eina_tls_new(Eina_TLS *key); +/** @relates static Eina_Bool eina_tls_cb_new(pthread_key_t *key, Eina_TLS_Delete_Cb delete_cb) */ +static inline Eina_Bool eina_tls_cb_new(Eina_TLS *key, Eina_TLS_Delete_Cb delete_cb); /** @relates static void eina_tls_free(pthread_key_t key) */ static inline void eina_tls_free(Eina_TLS key); /** @relates static void eina_tls_get(pthread_key_t key) */ diff --git a/src/lib/eina/eina_thread.c b/src/lib/eina/eina_thread.c index b4e07e7c37..5ba6c57e69 100644 --- a/src/lib/eina/eina_thread.c +++ b/src/lib/eina/eina_thread.c @@ -27,6 +27,7 @@ #include "eina_sched.h" #ifdef _WIN32 # include "eina_list.h" +# include "eina_lock.h" #endif /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ @@ -38,6 +39,12 @@ # include # undef WIN32_LEAN_AND_MEAN +typedef struct _Eina_TLS_Cbs_Win32 Eina_TLS_Cbs_Win32; +struct _Eina_TLS_Cbs_Win32 +{ + Eina_TLS key; + Eina_TLS_Delete_Cb cb; +}; typedef struct _Eina_Thread_Win32 Eina_Thread_Win32; struct _Eina_Thread_Win32 { @@ -45,6 +52,7 @@ struct _Eina_Thread_Win32 void *(*func)(void *data); void *data; void *ret; + Eina_List *tls_keys; Eina_Thread index; }; @@ -54,6 +62,81 @@ struct _Eina_Thread_Win32 static unsigned long int _current_index = 1; /* start from one as the main loop == 0 */ static Eina_List *_thread_pool = NULL; static Eina_List *_thread_running = NULL; +static Eina_List *_tls_keys_cbs = NULL; + +static inline Eina_TLS_Cbs_Win32 * +_eina_thread_tls_cb_find(Eina_TLS key) +{ + Eina_TLS_Cbs_Win32 *cb; + Eina_List *l; + + EINA_LIST_FOREACH(_tls_keys_cbs, l, cb) + if (cb->key == key) + return cb; + + return NULL; +} + +static inline void +_eina_thread_tls_keys_clean(Eina_Thread_Win32 *tw) +{ + void *data; + Eina_TLS_Cbs_Win32 *cb; + + EINA_LIST_FREE(tw->tls_keys, data) + { + Eina_TLS key = data; + cb = _eina_thread_tls_cb_find(key); + if (cb) + cb->cb(eina_tls_get(key)); + } + tw->tls_keys = NULL; +} + +EAPI Eina_Bool +_eina_thread_tls_cb_register(Eina_TLS key, Eina_TLS_Delete_Cb cb) +{ + Eina_TLS_Cbs_Win32 *tls_cb = malloc(sizeof(Eina_TLS_Cbs_Win32)); + if (!cb) return EINA_FALSE; + + tls_cb->key = key; + tls_cb->cb = cb; + _tls_keys_cbs = eina_list_append(_tls_keys_cbs, tls_cb); + + return EINA_TRUE; +} + +EAPI Eina_Bool +_eina_thread_tls_cb_unregister(Eina_TLS key) +{ + Eina_TLS_Cbs_Win32 *cb = _eina_thread_tls_cb_find(key); + if (!cb) return EINA_FALSE; + + _tls_keys_cbs = eina_list_remove(_tls_keys_cbs, cb); + free(cb); + + return EINA_TRUE; +} + +EAPI Eina_Bool +_eina_thread_tls_key_add(Eina_TLS key) +{ + HANDLE t; + Eina_Thread_Win32 *tw; + Eina_List *l; + + t = GetCurrentThread(); + EINA_LIST_FOREACH(_thread_running, l, tw) + if (tw->thread == t) + { + void *data = key; + if (!eina_list_data_find(tw->tls_keys, data)) + tw->tls_keys = eina_list_append(tw->tls_keys, data); + return EINA_TRUE; + } + + return EINA_FALSE; +} static Eina_Thread_Win32 * _eina_thread_win32_find(Eina_Thread index) @@ -123,6 +206,7 @@ _eina_thread_create(Eina_Thread *t, tw->func = func; tw->data = (void *)data; + tw->tls_keys = NULL; tw->thread = CreateThread(NULL, 0, _eina_thread_win32_cb, tw, 0, NULL); if (!tw->thread) goto on_error; @@ -159,6 +243,7 @@ _eina_thread_join(Eina_Thread t) tw->thread = NULL; tw->func = NULL; tw->data = NULL; + _eina_thread_tls_keys_clean(tw); _thread_running = eina_list_remove(_thread_running, tw); _thread_pool = eina_list_append(_thread_pool, _thread_pool); diff --git a/src/tests/eina/eina_test_lock.c b/src/tests/eina/eina_test_lock.c index 9bac3041b8..7c6626eaa7 100644 --- a/src/tests/eina/eina_test_lock.c +++ b/src/tests/eina/eina_test_lock.c @@ -77,33 +77,57 @@ START_TEST(eina_test_spinlock) END_TEST static Eina_TLS key; +static int _eina_tls_free_count = 0; + +static void * +_eina_test_tls_alloc(int v) +{ + int *ptr = malloc(sizeof(int)); + *ptr = v; + + return ptr; +} + +static void +_eina_test_tls_free(void *ptr) +{ + _eina_tls_free_count++; + free(ptr); +} static void * _eina_test_tls_thread(void *data EINA_UNUSED, Eina_Thread t EINA_UNUSED) { - unsigned int mystack = 21; + int *ptr; - fail_if(!eina_tls_set(key, &mystack)); - fail_if(eina_tls_get(key) != &mystack); + ptr = eina_tls_get(key); + fail_if(eina_tls_get(key) != NULL); + + fail_if(!eina_tls_set(key, _eina_test_tls_alloc(24))); + + ptr = eina_tls_get(key); + fail_if(eina_tls_get(key) == NULL); + fail_if(*ptr != 24); return NULL; } START_TEST(eina_test_tls) { - unsigned int ft = 42; - fail_if(!eina_init()); - fail_if(!eina_tls_new(&key)); + fail_if(!eina_tls_cb_new(&key, _eina_test_tls_free)); - fail_if(!eina_tls_set(key, &ft)); + fail_if(!eina_tls_set(key, _eina_test_tls_alloc(42))); fail_if(!eina_thread_create(&thread, EINA_THREAD_NORMAL, 0, _eina_test_tls_thread, NULL)); eina_thread_join(thread); + fail_if(_eina_tls_free_count != 1); - fail_if(eina_tls_get(key) != &ft); + int *ptr = eina_tls_get(key); + fail_if(eina_tls_get(key) == NULL); + fail_if(*ptr != 42); eina_tls_free(key);