Add a mutex and atomic counter abstraction and implementations These abstractions can be used to implement an efficient resource reference counter and simple mutual exclusion. On pthreads we use pthread_mutex_t, except when we are also on glibc and can directly use its asm/atomic.h definitions. 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 125 126 127 128 129 130 131 132 133
diff --git a/src/common.h b/src/common.h
index 89b6f61..6195078 100644
--- a/src/common.h
+++ b/src/common.h
@@ -9,6 +9,7 @@
#include <inttypes.h>
#include <assert.h>
#include <errno.h>
+#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
@@ -19,6 +20,7 @@
#include "cc-compat.h"
#include "util.h"
+#include "thread-utils.h"
#include "errors.h"
#define GIT_PATH_MAX 4096
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 199447c..f5a98ad 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -1,7 +1,88 @@
#ifndef INCLUDE_thread_utils_h__
#define INCLUDE_thread_utils_h__
-#include "git/thread-utils.h"
+#if defined(GIT_HAS_PTHREAD)
+typedef pthread_mutex_t git_lck;
+# define GITLCK_INIT PTHREAD_MUTEX_INITIALIZER
+# define gitlck_init(a) pthread_mutex_init(a, NULL)
+# define gitlck_lock(a) pthread_mutex_lock(a)
+# define gitlck_unlock(a) pthread_mutex_unlock(a)
+# define gitlck_free(a) pthread_mutex_destroy(a)
+
+# if defined(__GLIBC__)
+# include <asm/atomic.h>
+typedef atomic_t git_refcnt;
+# define gitrc_init(a) atomic_set(a, 0)
+# define gitrc_inc(a) atomic_inc_return(a)
+# define gitrc_dec(a) atomic_dec_and_test(a)
+# define gitrc_free(a) (void)0
+
+# else
+typedef struct { git_lck lock; int counter; } git_refcnt;
+
+/** Initialize to 0. No memory barrier is issued. */
+GIT_INLINE(void) gitrc_init(git_refcnt *p)
+{
+ gitlck_init(&p->lock);
+ p->counter = 0;
+}
+
+/**
+ * Increment.
+ *
+ * Atomically increments @p by 1. A memory barrier is also
+ * issued before and after the operation.
+ *
+ * @param p pointer of type git_refcnt
+ */
+GIT_INLINE(void) gitrc_inc(git_refcnt *p)
+{
+ gitlck_lock(&p->lock);
+ p->counter++;
+ gitlck_unlock(&p->lock);
+}
+
+/**
+ * Decrement and test.
+ *
+ * Atomically decrements @p by 1 and returns true if the
+ * result is 0, or false for all other cases. A memory
+ * barrier is also issued before and after the operation.
+ *
+ * @param p pointer of type git_refcnt
+ */
+GIT_INLINE(int) gitrc_dec(git_refcnt *p)
+{
+ int c;
+ gitlck_lock(&p->lock);
+ c = --p->counter;
+ gitlck_unlock(&p->lock);
+ return !!c;
+}
+
+/** Free any resources associated with the counter. */
+# define gitrc_free(p) gitlck_free(&(p)->lock)
+
+# endif
+
+#elif defined(GIT_THREADS)
+# error GIT_THREADS but no git_lck implementation
+
+#else
+typedef struct {} git_lck;
+# define GIT_MUTEX_INIT {}
+# define gitlck_init(a) (void)0
+# define gitlck_lock(a) (void)0
+# define gitlck_unlock(a) (void)0
+# define gitlck_free(a) (void)0
+
+typedef struct { int counter; } git_refcnt;
+# define gitrc_init(a) ((a)->counter = 0)
+# define gitrc_inc(a) ((a)->counter++)
+# define gitrc_dec(a) (--(a)->counter == 0)
+# define gitrc_free(a) (void)0
+
+#endif
extern int git_online_cpus(void);
diff --git a/tests/t0002-refcnt.c b/tests/t0002-refcnt.c
new file mode 100644
index 0000000..efa59b7
--- /dev/null
+++ b/tests/t0002-refcnt.c
@@ -0,0 +1,13 @@
+#include "test_lib.h"
+#include "common.h"
+
+BEGIN_TEST(init_inc2_dec2_free)
+ git_refcnt p;
+
+ gitrc_init(&p);
+ gitrc_inc(&p);
+ gitrc_inc(&p);
+ must_be_true(!gitrc_dec(&p));
+ must_be_true(gitrc_dec(&p));
+ gitrc_free(&p);
+END_TEST