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_thread_utils_h__
8 #define INCLUDE_thread_utils_h__
9 
10 #if defined(__GNUC__) && defined(GIT_THREADS)
11 # if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1))
12 #  error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF
13 # endif
14 #endif
15 
16 /* Common operations even if threading has been disabled */
17 typedef struct {
18 #if defined(GIT_WIN32)
19 	volatile long val;
20 #else
21 	volatile int val;
22 #endif
23 } git_atomic;
24 
25 #ifdef GIT_ARCH_64
26 
27 typedef struct {
28 #if defined(GIT_WIN32)
29 	__int64 val;
30 #else
31 	int64_t val;
32 #endif
33 } git_atomic64;
34 
35 typedef git_atomic64 git_atomic_ssize;
36 
37 #define git_atomic_ssize_add git_atomic64_add
38 
39 #else
40 
41 typedef git_atomic git_atomic_ssize;
42 
43 #define git_atomic_ssize_add git_atomic_add
44 
45 #endif
46 
47 #ifdef GIT_THREADS
48 
49 #ifdef GIT_WIN32
50 #   include "win32/thread.h"
51 #else
52 #   include "unix/pthread.h"
53 #endif
54 
git_atomic_set(git_atomic * a,int val)55 GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
56 {
57 #if defined(GIT_WIN32)
58 	InterlockedExchange(&a->val, (LONG)val);
59 #elif defined(__GNUC__)
60 	__sync_lock_test_and_set(&a->val, val);
61 #else
62 #	error "Unsupported architecture for atomic operations"
63 #endif
64 }
65 
git_atomic_inc(git_atomic * a)66 GIT_INLINE(int) git_atomic_inc(git_atomic *a)
67 {
68 #if defined(GIT_WIN32)
69 	return InterlockedIncrement(&a->val);
70 #elif defined(__GNUC__)
71 	return __sync_add_and_fetch(&a->val, 1);
72 #else
73 #	error "Unsupported architecture for atomic operations"
74 #endif
75 }
76 
git_atomic_add(git_atomic * a,int32_t addend)77 GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
78 {
79 #if defined(GIT_WIN32)
80 	return InterlockedExchangeAdd(&a->val, addend);
81 #elif defined(__GNUC__)
82 	return __sync_add_and_fetch(&a->val, addend);
83 #else
84 #	error "Unsupported architecture for atomic operations"
85 #endif
86 }
87 
git_atomic_dec(git_atomic * a)88 GIT_INLINE(int) git_atomic_dec(git_atomic *a)
89 {
90 #if defined(GIT_WIN32)
91 	return InterlockedDecrement(&a->val);
92 #elif defined(__GNUC__)
93 	return __sync_sub_and_fetch(&a->val, 1);
94 #else
95 #	error "Unsupported architecture for atomic operations"
96 #endif
97 }
98 
git___compare_and_swap(void * volatile * ptr,void * oldval,void * newval)99 GIT_INLINE(void *) git___compare_and_swap(
100 	void * volatile *ptr, void *oldval, void *newval)
101 {
102 	volatile void *foundval;
103 #if defined(GIT_WIN32)
104 	foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
105 #elif defined(__GNUC__)
106 	foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
107 #else
108 #	error "Unsupported architecture for atomic operations"
109 #endif
110 	return (foundval == oldval) ? oldval : newval;
111 }
112 
git___swap(void * volatile * ptr,void * newval)113 GIT_INLINE(volatile void *) git___swap(
114 	void * volatile *ptr, void *newval)
115 {
116 #if defined(GIT_WIN32)
117 	return InterlockedExchangePointer(ptr, newval);
118 #else
119 	return __sync_lock_test_and_set(ptr, newval);
120 #endif
121 }
122 
123 #ifdef GIT_ARCH_64
124 
git_atomic64_add(git_atomic64 * a,int64_t addend)125 GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
126 {
127 #if defined(GIT_WIN32)
128 	return InterlockedExchangeAdd64(&a->val, addend);
129 #elif defined(__GNUC__)
130 	return __sync_add_and_fetch(&a->val, addend);
131 #else
132 #	error "Unsupported architecture for atomic operations"
133 #endif
134 }
135 
136 #endif
137 
138 #else
139 
140 #define git_thread unsigned int
141 #define git_thread_create(thread, start_routine, arg) 0
142 #define git_thread_join(id, status) (void)0
143 
144 /* Pthreads Mutex */
145 #define git_mutex unsigned int
git_mutex_init(git_mutex * mutex)146 GIT_INLINE(int) git_mutex_init(git_mutex *mutex) \
147 	{ GIT_UNUSED(mutex); return 0; }
git_mutex_lock(git_mutex * mutex)148 GIT_INLINE(int) git_mutex_lock(git_mutex *mutex) \
149 	{ GIT_UNUSED(mutex); return 0; }
150 #define git_mutex_unlock(a) (void)0
151 #define git_mutex_free(a) (void)0
152 
153 /* Pthreads condition vars */
154 #define git_cond unsigned int
155 #define git_cond_init(c, a)	(void)0
156 #define git_cond_free(c) (void)0
157 #define git_cond_wait(c, l)	(void)0
158 #define git_cond_signal(c) (void)0
159 #define git_cond_broadcast(c) (void)0
160 
161 /* Pthreads rwlock */
162 #define git_rwlock unsigned int
163 #define git_rwlock_init(a)		0
164 #define git_rwlock_rdlock(a)	0
165 #define git_rwlock_rdunlock(a)	(void)0
166 #define git_rwlock_wrlock(a)	0
167 #define git_rwlock_wrunlock(a)	(void)0
168 #define git_rwlock_free(a)		(void)0
169 #define GIT_RWLOCK_STATIC_INIT	0
170 
171 
git_atomic_set(git_atomic * a,int val)172 GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
173 {
174 	a->val = val;
175 }
176 
git_atomic_inc(git_atomic * a)177 GIT_INLINE(int) git_atomic_inc(git_atomic *a)
178 {
179 	return ++a->val;
180 }
181 
git_atomic_add(git_atomic * a,int32_t addend)182 GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
183 {
184 	a->val += addend;
185 	return a->val;
186 }
187 
git_atomic_dec(git_atomic * a)188 GIT_INLINE(int) git_atomic_dec(git_atomic *a)
189 {
190 	return --a->val;
191 }
192 
git___compare_and_swap(void * volatile * ptr,void * oldval,void * newval)193 GIT_INLINE(void *) git___compare_and_swap(
194 	void * volatile *ptr, void *oldval, void *newval)
195 {
196 	if (*ptr == oldval)
197 		*ptr = newval;
198 	else
199 		oldval = newval;
200 	return oldval;
201 }
202 
git___swap(void * volatile * ptr,void * newval)203 GIT_INLINE(volatile void *) git___swap(
204 	void * volatile *ptr, void *newval)
205 {
206 	volatile void *old = *ptr;
207 	*ptr = newval;
208 	return old;
209 }
210 
211 #ifdef GIT_ARCH_64
212 
git_atomic64_add(git_atomic64 * a,int64_t addend)213 GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
214 {
215 	a->val += addend;
216 	return a->val;
217 }
218 
219 #endif
220 
221 #endif
222 
git_atomic_get(git_atomic * a)223 GIT_INLINE(int) git_atomic_get(git_atomic *a)
224 {
225 	return (int)a->val;
226 }
227 
228 /* Atomically replace oldval with newval
229  * @return oldval if it was replaced or newval if it was not
230  */
231 #define git__compare_and_swap(P,O,N) \
232 	git___compare_and_swap((void * volatile *)P, O, N)
233 
234 #define git__swap(ptr, val) (void *)git___swap((void * volatile *)&ptr, val)
235 
236 extern int git_online_cpus(void);
237 
238 #if defined(GIT_THREADS) && defined(_MSC_VER)
239 # define GIT_MEMORY_BARRIER MemoryBarrier()
240 #elif defined(GIT_THREADS)
241 # define GIT_MEMORY_BARRIER __sync_synchronize()
242 #else
243 # define GIT_MEMORY_BARRIER /* noop */
244 #endif
245 
246 #endif
247