From a1d34bc000cee6d72c3b5e329faa58424641611f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 30 Dec 2008 21:49:38 -0800 Subject: [PATCH] Support building on Mac OS X by using pthread_getspecific for TLS The Mach-O format does not permit gcc to implement the __thread TLS specification, so we must instead emulate it using a single int cell allocated from memory and stored inside of the thread specific data associated with the current pthread. What makes this tricky is git_errno must be a valid lvalue, so we really need to return a pointer to the caller and deference it as part of the git_errno macro. The GCC-specific __attribute__((constructor)) extension is used to ensure the pthread_key_t is allocated before any Git functions are executed in the library, as this is necessary to access our thread specific storage. Signed-off-by: Shawn O. Pearce --- src/common.h | 3 +++ src/errors.c | 23 +++++++++++++++++++++++ src/git/errors.h | 7 +++++++ src/git/thread-utils.h | 15 ++++++++++++--- tests/t0000-errno.c | 14 ++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 tests/t0000-errno.c diff --git a/src/common.h b/src/common.h index 0e115c5be..d17bf1cee 100644 --- a/src/common.h +++ b/src/common.h @@ -5,6 +5,9 @@ #include "util.h" #include "errors.h" +#ifdef GIT_HAS_PTHREAD +# include +#endif #include #include #include diff --git a/src/errors.c b/src/errors.c index b3e014dd4..75636f477 100644 --- a/src/errors.c +++ b/src/errors.c @@ -1,9 +1,32 @@ #include "common.h" #include "thread-utils.h" /* for GIT_TLS */ +#if defined(GIT_TLS) /* compile-time constant initialization required */ GIT_TLS int git_errno = 0; +#elif defined(GIT_HAS_PTHREAD) + +static pthread_key_t errno_key; + +static void init_errno(void) __attribute__((constructor)); +static void init_errno(void) +{ + pthread_key_create(&errno_key, free); +} + +int *git__errno_storage(void) +{ + int *e = pthread_getspecific(errno_key); + if (!e) { + e = calloc(1, sizeof(*e)); + pthread_setspecific(errno_key, e); + } + return e; +} + +#endif + static struct { int num; const char *str; diff --git a/src/git/errors.h b/src/git/errors.h index 18eb2b8ad..37870a4c5 100644 --- a/src/git/errors.h +++ b/src/git/errors.h @@ -13,8 +13,15 @@ GIT_BEGIN_DECL /** The git errno. */ +#if defined(GIT_TLS) GIT_EXTERN(int) GIT_TLS git_errno; +#elif defined(GIT_HAS_PTHREAD) +# define git_errno (*git__errno_storage()) +GIT_EXTERN(int *) git__errno_storage(void); + +#endif + /** * strerror() for the Git library * @param num The error code to explain diff --git a/src/git/thread-utils.h b/src/git/thread-utils.h index c14aa17cb..8baf75bed 100644 --- a/src/git/thread-utils.h +++ b/src/git/thread-utils.h @@ -9,8 +9,15 @@ #define GIT_HAS_TLS 1 -#if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || \ - defined(__xlc__) || defined(__xlC__) +#if defined(__APPLE__) && defined(__MACH__) +# undef GIT_TLS +# define GIT_HAS_PTHREAD + +#elif defined(__GNUC__) || \ + defined(__SUNPRO_C) || \ + defined(__SUNPRO_CC) || \ + defined(__xlc__) || \ + defined(__xlC__) # define GIT_TLS __thread #elif defined(__INTEL_COMPILER) @@ -20,7 +27,9 @@ # define GIT_TLS __thread # endif -#elif defined(_WIN32) || defined(_WIN32_CE) || defined(__BORLANDC__) +#elif defined(_WIN32) || \ + defined(_WIN32_CE) || \ + defined(__BORLANDC__) # define GIT_TLS __declspec(thread) #else diff --git a/tests/t0000-errno.c b/tests/t0000-errno.c new file mode 100644 index 000000000..dba81bc59 --- /dev/null +++ b/tests/t0000-errno.c @@ -0,0 +1,14 @@ +#include "test_lib.h" +#include "errors.h" +#include + +BEGIN_TEST(errno_zero_on_init) + must_be_true(git_errno == 0); +END_TEST + +BEGIN_TEST(set_ENOTOID) + must_be_true(GIT_ENOTOID != 0); + git_errno = GIT_ENOTOID; + must_be_true(git_errno == GIT_ENOTOID); + must_pass(strcmp(git_strerror(git_errno), "Not a git oid")); +END_TEST