Skip to content

Commit

Permalink
Merge branch 'feature/custom-tls-for-external-library' into libgit-ne…
Browse files Browse the repository at this point in the history
…xt-1.7.2
  • Loading branch information
zawata committed Feb 7, 2024
2 parents a9d63bf + 0602711 commit efdaa4a
Show file tree
Hide file tree
Showing 9 changed files with 479 additions and 5 deletions.
66 changes: 66 additions & 0 deletions include/git2/sys/custom_tls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_custom_tls_h__
#define INCLUDE_sys_custom_tls_h__

#include "git2/common.h"

GIT_BEGIN_DECL

/**
* Used to retrieve a pointer from a user of the library to pass to a newly
* created internal libgit2 thread. This should allow users of the library to
* establish a context that spans an internally threaded operation. This can
* useful for libraries that leverage callbacks used in an internally threaded
* routine.
*/
typedef void *GIT_CALLBACK(git_retrieve_tls_for_internal_thread_cb)(void);

/**
* This callback will be called when a thread is exiting so that a user
* of the library can clean up their thread local storage.
*/
typedef void GIT_CALLBACK(git_set_tls_on_internal_thread_cb)(void *payload);

/**
* This callback will be called when a thread is exiting so that a user
* of the library can clean up their thread local storage.
*/
typedef void GIT_CALLBACK(git_teardown_tls_on_internal_thread_cb)(void);

/**
* Sets the callbacks for custom thread local storage used by internally
* created libgit2 threads. This allows users of the library an opportunity
* to set thread local storage for internal threads based on the creating
* thread.
*
* @param retrieve_storage_for_internal_thread Used to retrieve a pointer on
* a thread before spawning child
* threads. This pointer will be
* passed to set_storage_on_thread
* in the newly spawned threads.
* @param set_storage_on_thread When a thread is spawned internally in libgit2,
* whatever pointer was retrieved in the calling
* thread by retrieve_storage_for_internal_thread
* will be passed to this callback in the newly
* spawned thread.
* @param teardown_storage_on_thread Before an internally spawned thread exits,
* this method will be called allowing a user
* of the library an opportunity to clean up
* any thread local storage they set up on
* the internal thread.
* @return 0 on success, or an error code. (use git_error_last for information
* about the error)
*/
GIT_EXTERN(int) git_custom_tls_set_callbacks(
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread,
git_set_tls_on_internal_thread_cb set_storage_on_thread,
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread);

GIT_END_DECL

#endif
2 changes: 2 additions & 0 deletions src/libgit2/libgit2.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "buf.h"
#include "cache.h"
#include "common.h"
#include "custom_tls.h"
#include "filter.h"
#include "grafts.h"
#include "hash.h"
Expand Down Expand Up @@ -76,6 +77,7 @@ int git_libgit2_init(void)
git_threadstate_global_init,
git_threads_global_init,
git_rand_global_init,
git_custom_tls__global_init,
git_hash_global_init,
git_sysdir_global_init,
git_filter_global_init,
Expand Down
122 changes: 122 additions & 0 deletions src/util/custom_tls.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/

#include "runtime.h"

#ifdef GIT_THREADS

#ifdef GIT_WIN32
# include "win32/thread.h"
#else
# include "unix/pthread.h"
#endif

struct git_custom_tls_callbacks {
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread;

git_set_tls_on_internal_thread_cb set_storage_on_thread;

git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread;

git_rwlock lock;
};

struct git_custom_tls_callbacks git__custom_tls = { 0, 0, 0 };

static void git_custom_tls_global_shutdown(void)
{
if (git_rwlock_wrlock(&git__custom_tls.lock) < 0)
return;

git__custom_tls.retrieve_storage_for_internal_thread = 0;
git__custom_tls.set_storage_on_thread = 0;
git__custom_tls.teardown_storage_on_thread = 0;

git_rwlock_wrunlock(&git__custom_tls.lock);
git_rwlock_free(&git__custom_tls.lock);
}

int git_custom_tls__global_init(void)
{
if (git_rwlock_init(&git__custom_tls.lock) < 0)
return -1;

return git_runtime_shutdown_register(git_custom_tls_global_shutdown);
}

int git_custom_tls_set_callbacks(
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread,
git_set_tls_on_internal_thread_cb set_storage_on_thread,
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread)
{
/* We want to ensure that all callbacks are set or not set in totality.
* It does not make sense to have a subset of callbacks set.
*/
assert((retrieve_storage_for_internal_thread && set_storage_on_thread &&
teardown_storage_on_thread) || !(retrieve_storage_for_internal_thread &&
set_storage_on_thread && teardown_storage_on_thread));

if (git_rwlock_wrlock(&git__custom_tls.lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to lock custom thread local storage");
return -1;
}

git__custom_tls.retrieve_storage_for_internal_thread =
retrieve_storage_for_internal_thread;
git__custom_tls.set_storage_on_thread =
set_storage_on_thread;
git__custom_tls.teardown_storage_on_thread =
teardown_storage_on_thread;

git_rwlock_wrunlock(&git__custom_tls.lock);
return 0;
}

int git_custom_tls__init(git_custom_tls *tls)
{
if (git_rwlock_rdlock(&git__custom_tls.lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to lock custom thread local storage");
return -1;
}

/* We try to ensure that all 3 callbacks must be set or not set.
* It would not make sense to have a subset of the callbacks set.
*/
if (!git__custom_tls.retrieve_storage_for_internal_thread) {
tls->set_storage_on_thread = NULL;
tls->teardown_storage_on_thread = NULL;
tls->payload = NULL;
} else {
/* We set these on a struct so that if for whatever reason the opts are changed
* at least the opts will remain consistent for any given thread already in
* motion.
*/
tls->set_storage_on_thread = git__custom_tls.set_storage_on_thread;
tls->teardown_storage_on_thread = git__custom_tls.teardown_storage_on_thread;
tls->payload = git__custom_tls.retrieve_storage_for_internal_thread();
}

git_rwlock_rdunlock(&git__custom_tls.lock);
return 0;
}

#else

int git_custom_tls__global_init(void)
{
return 0;
}

int git_custom_tls_set_callbacks(
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread,
git_set_tls_on_internal_thread_cb set_storage_on_thread,
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread)
{
return 0;
}

#endif
32 changes: 32 additions & 0 deletions src/util/custom_tls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_custom_tls_h__
#define INCLUDE_custom_tls_h__

#include "git2/sys/custom_tls.h"

int git_custom_tls__global_init(void);

#ifdef GIT_THREADS

typedef struct {
git_set_tls_on_internal_thread_cb set_storage_on_thread;

git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread;

/**
* payload should be set on the thread that is spawning the child thread.
* This payload will be passed to set_storage_on_thread
*/
void *payload;
} git_custom_tls;

int git_custom_tls__init(git_custom_tls *tls);

#endif

#endif
73 changes: 73 additions & 0 deletions src/util/unix/pthread.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/

#include "pthread.h"
#include "thread.h"
#include "runtime.h"

git_tlsdata_key thread_handle;

static void git_threads_global_shutdown(void) {
git_tlsdata_dispose(thread_handle);
}

int git_threads_global_init(void) {
int error = git_tlsdata_init(&thread_handle, NULL);
if (error != 0) {
return error;
}

return git_runtime_shutdown_register(git_threads_global_shutdown);
}

static void *git_unix__threadproc(void *arg)
{
void *result;
int error;
git_thread *thread = arg;

error = git_tlsdata_set(thread_handle, thread);
if (error != 0) {
return NULL;
}

if (thread->tls.set_storage_on_thread) {
thread->tls.set_storage_on_thread(thread->tls.payload);
}

result = thread->proc(thread->param);

if (thread->tls.teardown_storage_on_thread) {
thread->tls.teardown_storage_on_thread();
}

return result;
}

int git_thread_create(
git_thread *thread,
void *(*start_routine)(void*),
void *arg)
{

thread->proc = start_routine;
thread->param = arg;
if (git_custom_tls__init(&thread->tls) < 0)
return -1;

return pthread_create(&thread->thread, NULL, git_unix__threadproc, thread);
}

void git_thread_exit(void *value)
{
git_thread *thread = git_tlsdata_get(thread_handle);

if (thread && thread->tls.teardown_storage_on_thread)
thread->tls.teardown_storage_on_thread();

return pthread_exit(value);
}
16 changes: 12 additions & 4 deletions src/util/unix/pthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,26 @@
#ifndef INCLUDE_unix_pthread_h__
#define INCLUDE_unix_pthread_h__

#include "git2_util.h"
#include "custom_tls.h"

typedef struct {
pthread_t thread;
void *(*proc)(void *);
void *param;
git_custom_tls tls;
} git_thread;

GIT_INLINE(int) git_threads_global_init(void) { return 0; }
int git_threads_global_init(void);

#define git_thread_create(git_thread_ptr, start_routine, arg) \
pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg)
int git_thread_create(
git_thread *thread,
void *(*start_routine)(void*),
void *arg);
#define git_thread_join(git_thread_ptr, status) \
pthread_join((git_thread_ptr)->thread, status)
#define git_thread_currentid() ((size_t)(pthread_self()))
#define git_thread_exit(retval) pthread_exit(retval)
void git_thread_exit(void *value);

/* Git Mutex */
#define git_mutex pthread_mutex_t
Expand Down
16 changes: 15 additions & 1 deletion src/util/win32/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,16 @@ static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
/* Set the current thread for `git_thread_exit` */
FlsSetValue(fls_index, thread);

if (thread->tls.set_storage_on_thread) {
thread->tls.set_storage_on_thread(thread->tls.payload);
}

thread->result = thread->proc(thread->param);

if (thread->tls.teardown_storage_on_thread) {
thread->tls.teardown_storage_on_thread();
}

return CLEAN_THREAD_EXIT;
}

Expand Down Expand Up @@ -72,6 +80,9 @@ int git_thread_create(
thread->result = NULL;
thread->param = arg;
thread->proc = start_routine;
if (git_custom_tls__init(&thread->tls) < 0)
return -1;

thread->thread = CreateThread(
NULL, 0, git_win32__threadproc, thread, 0, NULL);

Expand Down Expand Up @@ -107,8 +118,11 @@ void git_thread_exit(void *value)
{
git_thread *thread = FlsGetValue(fls_index);

if (thread)
if (thread) {
if (thread->tls.teardown_storage_on_thread)
thread->tls.teardown_storage_on_thread();
thread->result = value;
}

ExitThread(CLEAN_THREAD_EXIT);
}
Expand Down
2 changes: 2 additions & 0 deletions src/util/win32/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#define INCLUDE_win32_thread_h__

#include "git2_util.h"
#include "custom_tls.h"

#if defined (_MSC_VER)
# define GIT_RESTRICT __restrict
Expand All @@ -21,6 +22,7 @@ typedef struct {
void *(*proc)(void *);
void *param;
void *result;
git_custom_tls tls;
} git_thread;

typedef CRITICAL_SECTION git_mutex;
Expand Down
Loading

0 comments on commit efdaa4a

Please sign in to comment.