Commit 028ef0de72da1ad4d1b2ee4a125ede81e3f2ebed

Shawn O. Pearce 2008-12-31T13:20:21

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>

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