Commit fd2d4759 by Edward Thomson

threads: git_tls_data to git_tlsdata

Use a no-allocation approach to the TLS data abstraction.
parent 42d5f110
...@@ -7,205 +7,132 @@ ...@@ -7,205 +7,132 @@
#include "common.h" #include "common.h"
#ifndef GIT_THREADS #if !defined(GIT_THREADS)
struct git_tls_data { #define TLSDATA_MAX 16
void GIT_CALLBACK(free_fn)(void *payload);
void *storage;
};
int git_tls_data__init(git_tls_data **out, typedef struct {
void GIT_CALLBACK(free_fn)(void *storage)) void *value;
void (GIT_SYSTEM_CALL *destroy_fn)(void *);
} tlsdata_value;
static tlsdata_value tlsdata_values[TLSDATA_MAX];
static int tlsdata_cnt = 0;
int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
{ {
struct git_tls_data *tls = git__malloc(sizeof(struct git_tls_data)); if (tlsdata_cnt >= TLSDATA_MAX)
GIT_ERROR_CHECK_ALLOC(tls); return -1;
tls->storage = NULL; tlsdata_values[tlsdata_cnt].value = NULL;
tls->free_fn = free_fn; tlsdata_values[tlsdata_cnt].destroy_fn = destroy_fn;
*out = tls;
return 0; *key = tlsdata_cnt;
} tlsdata_cnt++;
int git_tls_data__set(git_tls_data *tls, void *payload)
{
tls->storage = payload;
return 0; return 0;
} }
void *git_tls_data__get(git_tls_data *tls) int git_tlsdata_set(git_tlsdata_key key, void *value)
{ {
return tls->storage; if (key < 0 || key > tlsdata_cnt)
} return -1;
void git_tls_data__free(git_tls_data *tls) tlsdata_values[key].value = value;
{ return 0;
tls->free_fn(tls->storage);
git__free(tls);
} }
#elif defined(GIT_WIN32) void *git_tlsdata_get(git_tlsdata_key key)
struct git_tls_data {
void GIT_CALLBACK(free_fn)(void *payload);
DWORD fls_index;
};
struct git_tls_cell {
void GIT_CALLBACK(free_fn)(void *storage);
void *storage;
};
static void WINAPI git_tls_cell__free(void *sc)
{ {
struct git_tls_cell *storage_cell = sc; if (key < 0 || key > tlsdata_cnt)
if (storage_cell == NULL) { return NULL;
return;
}
storage_cell->free_fn(storage_cell->storage); return tlsdata_values[key].value;
git__free(storage_cell);
} }
int git_tls_data__init(git_tls_data **out, int git_tlsdata_dispose(git_tlsdata_key key)
void GIT_CALLBACK(free_fn)(void *payload))
{ {
struct git_tls_data *tls = git__malloc(sizeof(struct git_tls_data)); void *value;
GIT_ERROR_CHECK_ALLOC(tls); void (*destroy_fn)(void *) = NULL;
if ((tls->fls_index = FlsAlloc(git_tls_cell__free)) == FLS_OUT_OF_INDEXES) { if (key < 0 || key > tlsdata_cnt)
git__free(tls);
return -1; return -1;
}
tls->free_fn = free_fn;
*out = tls;
return 0;
}
int git_tls_data__set(git_tls_data *tls, void *payload) value = tlsdata_values[key].value;
{ destroy_fn = tlsdata_values[key].destroy_fn;
struct git_tls_cell *storage_cell;
if (payload == NULL) { tlsdata_values[key].value = NULL;
if ((storage_cell = FlsGetValue(tls->fls_index)) != NULL) tlsdata_values[key].destroy_fn = NULL;
git_tls_cell__free(storage_cell);
if (FlsSetValue(tls->fls_index, NULL) == 0) if (value && destroy_fn)
return -1; destroy_fn(value);
return 0; return 0;
} }
storage_cell = git__malloc(sizeof(struct git_tls_cell)); #elif defined(GIT_WIN32)
GIT_ERROR_CHECK_ALLOC(storage_cell);
storage_cell->free_fn = tls->free_fn; int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
storage_cell->storage = payload; {
DWORD fls_index = FlsAlloc(destroy_fn);
if (FlsSetValue(tls->fls_index, storage_cell) == 0) { if (fls_index == FLS_OUT_OF_INDEXES)
git__free(storage_cell);
return -1; return -1;
}
*key = fls_index;
return 0; return 0;
} }
void *git_tls_data__get(git_tls_data *tls) int git_tlsdata_set(git_tlsdata_key key, void *value)
{ {
struct git_tls_cell *storage_cell = FlsGetValue(tls->fls_index); if (!FlsSetValue(key, value))
if (storage_cell == NULL) return -1;
return NULL;
return storage_cell->storage;
}
void git_tls_data__free(git_tls_data *tls) return 0;
{
FlsFree(tls->fls_index);
tls->free_fn = NULL;
git__free(tls);
} }
#elif defined(_POSIX_THREADS) void *git_tlsdata_get(git_tlsdata_key key)
struct git_tls_data {
void GIT_CALLBACK(free_fn)(void *payload);
pthread_key_t tls_key;
};
struct git_tls_cell {
void GIT_CALLBACK(free_fn)(void *storage);
void *storage;
};
static void git_tls_cell__free(void *sc)
{ {
struct git_tls_cell *storage_cell = sc; return FlsGetValue(key);
storage_cell->free_fn(storage_cell->storage);
git__free(storage_cell);
} }
int git_tls_data__init(git_tls_data **out, int git_tlsdata_dispose(git_tlsdata_key key)
void GIT_CALLBACK(free_fn)(void *payload))
{ {
struct git_tls_data *tls = git__malloc(sizeof(struct git_tls_data)); if (!FlsFree(key))
GIT_ERROR_CHECK_ALLOC(tls);
if (pthread_key_create(&tls->tls_key, git_tls_cell__free) != 0) {
git__free(tls);
return -1; return -1;
}
tls->free_fn = free_fn;
*out = tls;
return 0; return 0;
} }
int git_tls_data__set(git_tls_data *tls, void *payload) #elif defined(_POSIX_THREADS)
{
struct git_tls_cell *storage_cell;
if (payload == NULL) {
if ((storage_cell = pthread_getspecific(tls->tls_key)) != NULL)
git_tls_cell__free(storage_cell);
if (pthread_setspecific(tls->tls_key, NULL) != 0) int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
{
if (pthread_key_create(key, destroy_fn) != 0)
return -1; return -1;
return 0; return 0;
} }
storage_cell = git__malloc(sizeof(struct git_tls_cell));
GIT_ERROR_CHECK_ALLOC(storage_cell);
storage_cell->free_fn = tls->free_fn;
storage_cell->storage = payload;
if (pthread_setspecific(tls->tls_key, storage_cell) != 0) { int git_tlsdata_set(git_tlsdata_key key, void *value)
git__free(storage_cell); {
if (pthread_setspecific(key, value) != 0)
return -1; return -1;
}
return 0; return 0;
} }
void *git_tls_data__get(git_tls_data *tls) void *git_tlsdata_get(git_tlsdata_key key)
{ {
struct git_tls_cell *storage_cell = pthread_getspecific(tls->tls_key); return pthread_getspecific(key);
if (storage_cell == NULL)
return NULL;
return storage_cell->storage;
} }
void git_tls_data__free(git_tls_data *tls) int git_tlsdata_dispose(git_tlsdata_key key)
{ {
git_tls_data__set(tls, NULL); if (pthread_key_delete(key) != 0)
pthread_key_delete(tls->tls_key); return -1;
git__free(tls);
return 0;
} }
#else #else
......
...@@ -367,52 +367,56 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) ...@@ -367,52 +367,56 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
#endif #endif
/** /* Thread-local data */
* An opaque structure for managing TLS in the library
*/ #if !defined(GIT_THREADS)
typedef struct git_tls_data git_tls_data; # define git_tlsdata_key int
#elif defined(GIT_WIN32)
# define git_tlsdata_key DWORD
#elif defined(_POSIX_THREADS)
# define git_tlsdata_key pthread_key_t
#else
# error unknown threading model
#endif
/** /**
* Initializes a thread local storage container. * Create a thread-local data key. The destroy function will be
* This has an implementation even without GIT_THREADS * called upon thread exit. On some platforms, it may be called
* which just serves to encourage use of this where TLS * when all threads have deleted their keys.
* is necessary.
* *
* Do not call this before the allocator has been initialized. * Note that the tlsdata functions do not set an error message on
* failure; this is because the error handling in libgit2 is itself
* handled by thread-local data storage.
* *
* @param out a pointer to store the TLS container in * @param key the tlsdata key
* @param free_fn the method that should be called when * @param destroy_fn function pointer called upon thread exit
* deleting something in the TLS. Will be
* registered as the clean up callback for
* the OS specific TLS construct.
* @return 0 on success, non-zero on failure * @return 0 on success, non-zero on failure
*/ */
int git_tls_data__init(git_tls_data **out, int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *));
void GIT_CALLBACK(free_fn)(void *payload));
/** /**
* Will set a thread specific value on the TLS. Passing NULL will free the * Set a the thread-local value for the given key.
* currently held thread specific value.
* *
* @param tls the TLS instance to store data on * @param key the tlsdata key to store data on
* @param payload the pointer to store * @param value the pointer to store
* @return 0 on success, non-zero on failure * @return 0 on success, non-zero on failure
*/ */
int git_tls_data__set(git_tls_data *tls, void *payload); int git_tlsdata_set(git_tlsdata_key key, void *value);
/** /**
* Will get the thread specific value stored in TLS. * Get the thread-local value for the given key.
* *
* @param tls the TLS instance to retrieve data from * @param key the tlsdata key to retrieve the value of
* @return the pointer stored with git_tlsdata_set
*/ */
void *git_tls_data__get(git_tls_data *tls); void *git_tlsdata_get(git_tlsdata_key key);
/** /**
* Must call this to clean up the TLS when no longer in use. * Delete the given thread-local key.
* The TLS pointer is unusable after a call to this.
* *
* @param tls the TLS to free * @param key the tlsdata key to dispose
* @return 0 on success, non-zero on failure
*/ */
void git_tls_data__free(git_tls_data *tls); int git_tlsdata_dispose(git_tlsdata_key key);
#endif #endif
#include "clar_libgit2.h"
#include "thread_helpers.h"
void test_threads_tlsdata__can_set_and_get(void)
{
git_tlsdata_key key_one, key_two, key_three;
cl_git_pass(git_tlsdata_init(&key_one, NULL));
cl_git_pass(git_tlsdata_init(&key_two, NULL));
cl_git_pass(git_tlsdata_init(&key_three, NULL));
cl_git_pass(git_tlsdata_set(key_one, (void *)(size_t)42424242));
cl_git_pass(git_tlsdata_set(key_two, (void *)(size_t)0xdeadbeef));
cl_git_pass(git_tlsdata_set(key_three, (void *)(size_t)98761234));
cl_assert_equal_sz((size_t)42424242, git_tlsdata_get(key_one));
cl_assert_equal_sz((size_t)0xdeadbeef, git_tlsdata_get(key_two));
cl_assert_equal_sz((size_t)98761234, git_tlsdata_get(key_three));
cl_git_pass(git_tlsdata_dispose(key_one));
cl_git_pass(git_tlsdata_dispose(key_two));
cl_git_pass(git_tlsdata_dispose(key_three));
}
#ifdef GIT_THREADS
static void *set_and_get(void *param)
{
git_tlsdata_key *tlsdata_key = (git_tlsdata_key *)param;
int val;
if (git_tlsdata_set(*tlsdata_key, &val) != 0 ||
git_tlsdata_get(*tlsdata_key) != &val)
return (void *)0;
return (void *)1;
}
#endif
#define THREAD_COUNT 10
void test_threads_tlsdata__threads(void)
{
#ifdef GIT_THREADS
git_thread thread[THREAD_COUNT];
git_tlsdata_key tlsdata;
int i;
cl_git_pass(git_tlsdata_init(&tlsdata, NULL));
for (i = 0; i < THREAD_COUNT; i++)
cl_git_pass(git_thread_create(&thread[i], set_and_get, &tlsdata));
for (i = 0; i < THREAD_COUNT; i++) {
void *result;
cl_git_pass(git_thread_join(&thread[i], &result));
cl_assert_equal_sz(1, (size_t)result);
}
cl_git_pass(git_tlsdata_dispose(tlsdata));
#endif
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment