From 5d2434bf71e47802841bad218d521fa254d1ca2d Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 25 Aug 2013 03:12:41 +0200 Subject: [PATCH] unix, windows: add thread-local storage API Uses the pthread_key_{create,delete} and pthread_{get,set}specific functions on UNIX platforms, Tls{Alloc,Free} and Tls{Get,Set}Value on Windows. Fixes #904. --- include/uv-unix.h | 2 +- include/uv-win.h | 4 ++++ include/uv.h | 12 ++++++++++++ src/unix/thread.c | 21 +++++++++++++++++++++ src/win/thread.c | 33 +++++++++++++++++++++++++++++++++ test/test-list.h | 2 ++ test/test-thread.c | 26 ++++++++++++++++++++++++++ 7 files changed, 99 insertions(+), 1 deletion(-) diff --git a/include/uv-unix.h b/include/uv-unix.h index d0b84185..408b44c6 100644 --- a/include/uv-unix.h +++ b/include/uv-unix.h @@ -131,7 +131,7 @@ typedef pthread_mutex_t uv_mutex_t; typedef pthread_rwlock_t uv_rwlock_t; typedef UV_PLATFORM_SEM_T uv_sem_t; typedef pthread_cond_t uv_cond_t; - +typedef pthread_key_t uv_key_t; #if defined(__APPLE__) && defined(__MACH__) diff --git a/include/uv-win.h b/include/uv-win.h index 8e60b354..512d7f89 100644 --- a/include/uv-win.h +++ b/include/uv-win.h @@ -250,6 +250,10 @@ typedef struct { uv_sem_t turnstile2; } uv_barrier_t; +typedef struct { + DWORD tls_index; +} uv_key_t; + #define UV_ONCE_INIT { 0, NULL } typedef struct uv_once_s { diff --git a/include/uv.h b/include/uv.h index cb1388e5..5700f941 100644 --- a/include/uv.h +++ b/include/uv.h @@ -2012,6 +2012,18 @@ UV_EXTERN void uv_barrier_wait(uv_barrier_t* barrier); */ UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)); +/* Thread-local storage. These functions largely follow the semantics of + * pthread_key_create(), pthread_key_delete(), pthread_getspecific() and + * pthread_setspecific(). + * + * Note that the total thread-local storage size may be limited. + * That is, it may not be possible to create many TLS keys. + */ +UV_EXTERN int uv_key_create(uv_key_t* key); +UV_EXTERN void uv_key_delete(uv_key_t* key); +UV_EXTERN void* uv_key_get(uv_key_t* key); +UV_EXTERN void uv_key_set(uv_key_t* key, void* value); + UV_EXTERN int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg); UV_EXTERN unsigned long uv_thread_self(void); diff --git a/src/unix/thread.c b/src/unix/thread.c index e0238aef..8c38c7f1 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -441,3 +441,24 @@ void uv_barrier_wait(uv_barrier_t* barrier) { } #endif /* defined(__APPLE__) && defined(__MACH__) */ + +int uv_key_create(uv_key_t* key) { + return -pthread_key_create(key, NULL); +} + + +void uv_key_delete(uv_key_t* key) { + if (pthread_key_delete(*key)) + abort(); +} + + +void* uv_key_get(uv_key_t* key) { + return pthread_getspecific(*key); +} + + +void uv_key_set(uv_key_t* key, void* value) { + if (pthread_setspecific(*key, value)) + abort(); +} diff --git a/src/win/thread.c b/src/win/thread.c index e5eb7018..0631da5a 100644 --- a/src/win/thread.c +++ b/src/win/thread.c @@ -680,3 +680,36 @@ void uv_barrier_wait(uv_barrier_t* barrier) { uv_sem_wait(&barrier->turnstile2); uv_sem_post(&barrier->turnstile2); } + + +int uv_key_create(uv_key_t* key) { + key->tls_index = TlsAlloc(); + if (key->tls_index == TLS_OUT_OF_INDEXES) + return UV_ENOMEM; + return 0; +} + + +void uv_key_delete(uv_key_t* key) { + if (TlsFree(key->tls_index) == FALSE) + abort(); + key->tls_index = TLS_OUT_OF_INDEXES; +} + + +void* uv_key_get(uv_key_t* key) { + void* value; + + value = TlsGetValue(key->tls_index); + if (value == NULL) + if (GetLastError() != ERROR_SUCCESS) + abort(); + + return value; +} + + +void uv_key_set(uv_key_t* key, void* value) { + if (TlsSetValue(key->tls_index, value) == FALSE) + abort(); +} diff --git a/test/test-list.h b/test/test-list.h index feca4670..334b4832 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -203,6 +203,7 @@ TEST_DECLARE (threadpool_cancel_getaddrinfo) TEST_DECLARE (threadpool_cancel_work) TEST_DECLARE (threadpool_cancel_fs) TEST_DECLARE (threadpool_cancel_single) +TEST_DECLARE (thread_local_storage) TEST_DECLARE (thread_mutex) TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_create) @@ -496,6 +497,7 @@ TASK_LIST_START TEST_ENTRY (threadpool_cancel_work) TEST_ENTRY (threadpool_cancel_fs) TEST_ENTRY (threadpool_cancel_single) + TEST_ENTRY (thread_local_storage) TEST_ENTRY (thread_mutex) TEST_ENTRY (thread_rwlock) TEST_ENTRY (thread_create) diff --git a/test/test-thread.c b/test/test-thread.c index 4bec8428..c396baa1 100644 --- a/test/test-thread.c +++ b/test/test-thread.c @@ -55,6 +55,7 @@ static void fs_do(struct fs_req* req); static void fs_cb(uv_fs_t* handle); static volatile int thread_called; +static uv_key_t tls_key; static void getaddrinfo_do(struct getaddrinfo_req* req) { @@ -181,3 +182,28 @@ TEST_IMPL(threadpool_multiple_event_loops) { return 0; } + + +static void tls_thread(void* arg) { + ASSERT(NULL == uv_key_get(&tls_key)); + uv_key_set(&tls_key, arg); + ASSERT(arg == uv_key_get(&tls_key)); + uv_key_set(&tls_key, NULL); + ASSERT(NULL == uv_key_get(&tls_key)); +} + + +TEST_IMPL(thread_local_storage) { + char name[] = "main"; + uv_thread_t threads[2]; + ASSERT(0 == uv_key_create(&tls_key)); + ASSERT(NULL == uv_key_get(&tls_key)); + uv_key_set(&tls_key, name); + ASSERT(name == uv_key_get(&tls_key)); + ASSERT(0 == uv_thread_create(threads + 0, tls_thread, threads + 0)); + ASSERT(0 == uv_thread_create(threads + 1, tls_thread, threads + 1)); + ASSERT(0 == uv_thread_join(threads + 0)); + ASSERT(0 == uv_thread_join(threads + 1)); + uv_key_delete(&tls_key); + return 0; +}