Skip to content

Commit 6da6a10

Browse files
committed
Merge remote-tracking branch 'zawata/feature/custom-tls-for-external-library' into libgit-next
2 parents 1f5e7f9 + b45219f commit 6da6a10

File tree

9 files changed

+481
-6
lines changed

9 files changed

+481
-6
lines changed

include/git2/sys/custom_tls.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
#ifndef INCLUDE_sys_custom_tls_h__
8+
#define INCLUDE_sys_custom_tls_h__
9+
10+
#include "git2/common.h"
11+
12+
GIT_BEGIN_DECL
13+
14+
/**
15+
* Used to retrieve a pointer from a user of the library to pass to a newly
16+
* created internal libgit2 thread. This should allow users of the library to
17+
* establish a context that spans an internally threaded operation. This can
18+
* useful for libraries that leverage callbacks used in an internally threaded
19+
* routine.
20+
*/
21+
typedef void *GIT_CALLBACK(git_retrieve_tls_for_internal_thread_cb)(void);
22+
23+
/**
24+
* This callback will be called when a thread is exiting so that a user
25+
* of the library can clean up their thread local storage.
26+
*/
27+
typedef void GIT_CALLBACK(git_set_tls_on_internal_thread_cb)(void *payload);
28+
29+
/**
30+
* This callback will be called when a thread is exiting so that a user
31+
* of the library can clean up their thread local storage.
32+
*/
33+
typedef void GIT_CALLBACK(git_teardown_tls_on_internal_thread_cb)(void);
34+
35+
/**
36+
* Sets the callbacks for custom thread local storage used by internally
37+
* created libgit2 threads. This allows users of the library an opportunity
38+
* to set thread local storage for internal threads based on the creating
39+
* thread.
40+
*
41+
* @param retrieve_storage_for_internal_thread Used to retrieve a pointer on
42+
* a thread before spawning child
43+
* threads. This pointer will be
44+
* passed to set_storage_on_thread
45+
* in the newly spawned threads.
46+
* @param set_storage_on_thread When a thread is spawned internally in libgit2,
47+
* whatever pointer was retrieved in the calling
48+
* thread by retrieve_storage_for_internal_thread
49+
* will be passed to this callback in the newly
50+
* spawned thread.
51+
* @param teardown_storage_on_thread Before an internally spawned thread exits,
52+
* this method will be called allowing a user
53+
* of the library an opportunity to clean up
54+
* any thread local storage they set up on
55+
* the internal thread.
56+
* @return 0 on success, or an error code. (use git_error_last for information
57+
* about the error)
58+
*/
59+
GIT_EXTERN(int) git_custom_tls_set_callbacks(
60+
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread,
61+
git_set_tls_on_internal_thread_cb set_storage_on_thread,
62+
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread);
63+
64+
GIT_END_DECL
65+
66+
#endif

src/custom_tls.c

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#include "common.h"
9+
#include "custom_tls.h"
10+
#include "runtime.h"
11+
12+
#ifdef GIT_THREADS
13+
14+
#ifdef GIT_WIN32
15+
# include "win32/thread.h"
16+
#else
17+
# include "unix/pthread.h"
18+
#endif
19+
20+
struct git_custom_tls_callbacks {
21+
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread;
22+
23+
git_set_tls_on_internal_thread_cb set_storage_on_thread;
24+
25+
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread;
26+
27+
git_rwlock lock;
28+
};
29+
30+
struct git_custom_tls_callbacks git__custom_tls = { 0, 0, 0 };
31+
32+
static void git_custom_tls_global_shutdown(void)
33+
{
34+
if (git_rwlock_wrlock(&git__custom_tls.lock) < 0)
35+
return;
36+
37+
git__custom_tls.retrieve_storage_for_internal_thread = 0;
38+
git__custom_tls.set_storage_on_thread = 0;
39+
git__custom_tls.teardown_storage_on_thread = 0;
40+
41+
git_rwlock_wrunlock(&git__custom_tls.lock);
42+
git_rwlock_free(&git__custom_tls.lock);
43+
}
44+
45+
int git_custom_tls__global_init(void)
46+
{
47+
if (git_rwlock_init(&git__custom_tls.lock) < 0)
48+
return -1;
49+
50+
return git_runtime_shutdown_register(git_custom_tls_global_shutdown);
51+
}
52+
53+
int git_custom_tls_set_callbacks(
54+
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread,
55+
git_set_tls_on_internal_thread_cb set_storage_on_thread,
56+
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread)
57+
{
58+
/* We want to ensure that all callbacks are set or not set in totality.
59+
* It does not make sense to have a subset of callbacks set.
60+
*/
61+
assert((retrieve_storage_for_internal_thread && set_storage_on_thread &&
62+
teardown_storage_on_thread) || !(retrieve_storage_for_internal_thread &&
63+
set_storage_on_thread && teardown_storage_on_thread));
64+
65+
if (git_rwlock_wrlock(&git__custom_tls.lock) < 0) {
66+
git_error_set(GIT_ERROR_OS, "failed to lock custom thread local storage");
67+
return -1;
68+
}
69+
70+
git__custom_tls.retrieve_storage_for_internal_thread =
71+
retrieve_storage_for_internal_thread;
72+
git__custom_tls.set_storage_on_thread =
73+
set_storage_on_thread;
74+
git__custom_tls.teardown_storage_on_thread =
75+
teardown_storage_on_thread;
76+
77+
git_rwlock_wrunlock(&git__custom_tls.lock);
78+
return 0;
79+
}
80+
81+
int git_custom_tls__init(git_custom_tls *tls)
82+
{
83+
if (git_rwlock_rdlock(&git__custom_tls.lock) < 0) {
84+
git_error_set(GIT_ERROR_OS, "failed to lock custom thread local storage");
85+
return -1;
86+
}
87+
88+
/* We try to ensure that all 3 callbacks must be set or not set.
89+
* It would not make sense to have a subset of the callbacks set.
90+
*/
91+
if (!git__custom_tls.retrieve_storage_for_internal_thread) {
92+
tls->set_storage_on_thread = NULL;
93+
tls->teardown_storage_on_thread = NULL;
94+
tls->payload = NULL;
95+
} else {
96+
/* We set these on a struct so that if for whatever reason the opts are changed
97+
* at least the opts will remain consistent for any given thread already in
98+
* motion.
99+
*/
100+
tls->set_storage_on_thread = git__custom_tls.set_storage_on_thread;
101+
tls->teardown_storage_on_thread = git__custom_tls.teardown_storage_on_thread;
102+
tls->payload = git__custom_tls.retrieve_storage_for_internal_thread();
103+
}
104+
105+
git_rwlock_rdunlock(&git__custom_tls.lock);
106+
return 0;
107+
}
108+
109+
#else
110+
111+
int git_custom_tls__global_init(void)
112+
{
113+
return 0;
114+
}
115+
116+
int git_custom_tls_set_callbacks(
117+
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread,
118+
git_set_tls_on_internal_thread_cb set_storage_on_thread,
119+
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread)
120+
{
121+
return 0;
122+
}
123+
124+
#endif

src/custom_tls.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
#ifndef INCLUDE_custom_tls_h__
8+
#define INCLUDE_custom_tls_h__
9+
10+
#include "common.h"
11+
#include "git2/sys/custom_tls.h"
12+
13+
int git_custom_tls__global_init(void);
14+
15+
#ifdef GIT_THREADS
16+
17+
typedef struct {
18+
git_set_tls_on_internal_thread_cb set_storage_on_thread;
19+
20+
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread;
21+
22+
/**
23+
* payload should be set on the thread that is spawning the child thread.
24+
* This payload will be passed to set_storage_on_thread
25+
*/
26+
void *payload;
27+
} git_custom_tls;
28+
29+
int git_custom_tls__init(git_custom_tls *tls);
30+
31+
#endif
32+
33+
#endif

src/libgit2.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "transports/smart.h"
3232
#include "transports/http.h"
3333
#include "transports/ssh.h"
34+
#include "custom_tls.h"
3435

3536
#ifdef GIT_WIN32
3637
# include "win32/w32_leakcheck.h"
@@ -69,6 +70,7 @@ int git_libgit2_init(void)
6970
git_allocator_global_init,
7071
git_threadstate_global_init,
7172
git_threads_global_init,
73+
git_custom_tls__global_init,
7274
git_hash_global_init,
7375
git_sysdir_global_init,
7476
git_filter_global_init,

src/unix/pthread.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#include "pthread.h"
9+
#include "thread.h"
10+
#include "runtime.h"
11+
12+
git_tlsdata_key thread_handle;
13+
14+
static void git_threads_global_shutdown(void) {
15+
git_tlsdata_dispose(thread_handle);
16+
}
17+
18+
int git_threads_global_init(void) {
19+
int error = git_tlsdata_init(&thread_handle, NULL);
20+
if (error != 0) {
21+
return error;
22+
}
23+
24+
return git_runtime_shutdown_register(git_threads_global_shutdown);
25+
}
26+
27+
static void *git_unix__threadproc(void *arg)
28+
{
29+
void *result;
30+
int error;
31+
git_thread *thread = arg;
32+
33+
error = git_tlsdata_set(thread_handle, thread);
34+
if (error != 0) {
35+
return NULL;
36+
}
37+
38+
if (thread->tls.set_storage_on_thread) {
39+
thread->tls.set_storage_on_thread(thread->tls.payload);
40+
}
41+
42+
result = thread->proc(thread->param);
43+
44+
if (thread->tls.teardown_storage_on_thread) {
45+
thread->tls.teardown_storage_on_thread();
46+
}
47+
48+
return result;
49+
}
50+
51+
int git_thread_create(
52+
git_thread *thread,
53+
void *(*start_routine)(void*),
54+
void *arg)
55+
{
56+
57+
thread->proc = start_routine;
58+
thread->param = arg;
59+
if (git_custom_tls__init(&thread->tls) < 0)
60+
return -1;
61+
62+
return pthread_create(&thread->thread, NULL, git_unix__threadproc, thread);
63+
}
64+
65+
void git_thread_exit(void *value)
66+
{
67+
git_thread *thread = git_tlsdata_get(thread_handle);
68+
69+
if (thread && thread->tls.teardown_storage_on_thread)
70+
thread->tls.teardown_storage_on_thread();
71+
72+
return pthread_exit(value);
73+
}

src/unix/pthread.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,25 @@
88
#ifndef INCLUDE_unix_pthread_h__
99
#define INCLUDE_unix_pthread_h__
1010

11+
#include "../custom_tls.h"
12+
1113
typedef struct {
1214
pthread_t thread;
15+
void *(*proc)(void *);
16+
void *param;
17+
git_custom_tls tls;
1318
} git_thread;
1419

15-
GIT_INLINE(int) git_threads_global_init(void) { return 0; }
20+
int git_threads_global_init(void);
1621

17-
#define git_thread_create(git_thread_ptr, start_routine, arg) \
18-
pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg)
22+
int git_thread_create(
23+
git_thread *thread,
24+
void *(*start_routine)(void*),
25+
void *arg);
1926
#define git_thread_join(git_thread_ptr, status) \
2027
pthread_join((git_thread_ptr)->thread, status)
2128
#define git_thread_currentid() ((size_t)(pthread_self()))
22-
#define git_thread_exit(retval) pthread_exit(retval)
29+
void git_thread_exit(void *value);
2330

2431
/* Git Mutex */
2532
#define git_mutex pthread_mutex_t

src/win32/thread.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,19 @@ static DWORD fls_index;
2727
static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
2828
{
2929
git_thread *thread = lpParameter;
30-
3130
/* Set the current thread for `git_thread_exit` */
3231
FlsSetValue(fls_index, thread);
3332

33+
if (thread->tls.set_storage_on_thread) {
34+
thread->tls.set_storage_on_thread(thread->tls.payload);
35+
}
36+
3437
thread->result = thread->proc(thread->param);
3538

39+
if (thread->tls.teardown_storage_on_thread) {
40+
thread->tls.teardown_storage_on_thread();
41+
}
42+
3643
return CLEAN_THREAD_EXIT;
3744
}
3845

@@ -72,6 +79,9 @@ int git_thread_create(
7279
thread->result = NULL;
7380
thread->param = arg;
7481
thread->proc = start_routine;
82+
if (git_custom_tls__init(&thread->tls) < 0)
83+
return -1;
84+
7585
thread->thread = CreateThread(
7686
NULL, 0, git_win32__threadproc, thread, 0, NULL);
7787

@@ -107,8 +117,11 @@ void git_thread_exit(void *value)
107117
{
108118
git_thread *thread = FlsGetValue(fls_index);
109119

110-
if (thread)
120+
if (thread) {
121+
if (thread->tls.teardown_storage_on_thread)
122+
thread->tls.teardown_storage_on_thread();
111123
thread->result = value;
124+
}
112125

113126
ExitThread(CLEAN_THREAD_EXIT);
114127
}

0 commit comments

Comments
 (0)