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 <spearce@spearce.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
diff --git a/src/common.h b/src/common.h
index 0e115c5..d17bf1c 100644
--- a/src/common.h
+++ b/src/common.h
@@ -5,6 +5,9 @@
#include "util.h"
#include "errors.h"
+#ifdef GIT_HAS_PTHREAD
+# include <pthread.h>
+#endif
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
diff --git a/src/errors.c b/src/errors.c
index b3e014d..75636f4 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 18eb2b8..37870a4 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 c14aa17..8baf75b 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 0000000..dba81bc
--- /dev/null
+++ b/tests/t0000-errno.c
@@ -0,0 +1,14 @@
+#include "test_lib.h"
+#include "errors.h"
+#include <string.h>
+
+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