Skip to content

Commit efdaa4a

Browse files
committed
Merge branch 'feature/custom-tls-for-external-library' into libgit-next-1.7.2
2 parents a9d63bf + 0602711 commit efdaa4a

File tree

9 files changed

+479
-5
lines changed

9 files changed

+479
-5
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/libgit2/libgit2.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "buf.h"
1313
#include "cache.h"
1414
#include "common.h"
15+
#include "custom_tls.h"
1516
#include "filter.h"
1617
#include "grafts.h"
1718
#include "hash.h"
@@ -76,6 +77,7 @@ int git_libgit2_init(void)
7677
git_threadstate_global_init,
7778
git_threads_global_init,
7879
git_rand_global_init,
80+
git_custom_tls__global_init,
7981
git_hash_global_init,
8082
git_sysdir_global_init,
8183
git_filter_global_init,

src/util/custom_tls.c

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

src/util/custom_tls.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 "git2/sys/custom_tls.h"
11+
12+
int git_custom_tls__global_init(void);
13+
14+
#ifdef GIT_THREADS
15+
16+
typedef struct {
17+
git_set_tls_on_internal_thread_cb set_storage_on_thread;
18+
19+
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread;
20+
21+
/**
22+
* payload should be set on the thread that is spawning the child thread.
23+
* This payload will be passed to set_storage_on_thread
24+
*/
25+
void *payload;
26+
} git_custom_tls;
27+
28+
int git_custom_tls__init(git_custom_tls *tls);
29+
30+
#endif
31+
32+
#endif

src/util/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/util/unix/pthread.h

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

11+
#include "git2_util.h"
12+
#include "custom_tls.h"
13+
1114
typedef struct {
1215
pthread_t thread;
16+
void *(*proc)(void *);
17+
void *param;
18+
git_custom_tls tls;
1319
} git_thread;
1420

15-
GIT_INLINE(int) git_threads_global_init(void) { return 0; }
21+
int git_threads_global_init(void);
1622

17-
#define git_thread_create(git_thread_ptr, start_routine, arg) \
18-
pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg)
23+
int git_thread_create(
24+
git_thread *thread,
25+
void *(*start_routine)(void*),
26+
void *arg);
1927
#define git_thread_join(git_thread_ptr, status) \
2028
pthread_join((git_thread_ptr)->thread, status)
2129
#define git_thread_currentid() ((size_t)(pthread_self()))
22-
#define git_thread_exit(retval) pthread_exit(retval)
30+
void git_thread_exit(void *value);
2331

2432
/* Git Mutex */
2533
#define git_mutex pthread_mutex_t

src/util/win32/thread.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,16 @@ static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
3131
/* Set the current thread for `git_thread_exit` */
3232
FlsSetValue(fls_index, thread);
3333

34+
if (thread->tls.set_storage_on_thread) {
35+
thread->tls.set_storage_on_thread(thread->tls.payload);
36+
}
37+
3438
thread->result = thread->proc(thread->param);
3539

40+
if (thread->tls.teardown_storage_on_thread) {
41+
thread->tls.teardown_storage_on_thread();
42+
}
43+
3644
return CLEAN_THREAD_EXIT;
3745
}
3846

@@ -72,6 +80,9 @@ int git_thread_create(
7280
thread->result = NULL;
7381
thread->param = arg;
7482
thread->proc = start_routine;
83+
if (git_custom_tls__init(&thread->tls) < 0)
84+
return -1;
85+
7586
thread->thread = CreateThread(
7687
NULL, 0, git_win32__threadproc, thread, 0, NULL);
7788

@@ -107,8 +118,11 @@ void git_thread_exit(void *value)
107118
{
108119
git_thread *thread = FlsGetValue(fls_index);
109120

110-
if (thread)
121+
if (thread) {
122+
if (thread->tls.teardown_storage_on_thread)
123+
thread->tls.teardown_storage_on_thread();
111124
thread->result = value;
125+
}
112126

113127
ExitThread(CLEAN_THREAD_EXIT);
114128
}

src/util/win32/thread.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#define INCLUDE_win32_thread_h__
1010

1111
#include "git2_util.h"
12+
#include "custom_tls.h"
1213

1314
#if defined (_MSC_VER)
1415
# define GIT_RESTRICT __restrict
@@ -21,6 +22,7 @@ typedef struct {
2122
void *(*proc)(void *);
2223
void *param;
2324
void *result;
25+
git_custom_tls tls;
2426
} git_thread;
2527

2628
typedef CRITICAL_SECTION git_mutex;

0 commit comments

Comments
 (0)