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.
This commit is contained in:
Ben Noordhuis 2013-08-25 03:12:41 +02:00
parent d5ab1c1a3b
commit 5d2434bf71
7 changed files with 99 additions and 1 deletions

View File

@ -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__)

View File

@ -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 {

View File

@ -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);

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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)

View File

@ -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;
}