• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <fcntl.h>
6 #include <time.h>
7 #include <errno.h>
8 #include <pthread.h>
9 #include <sys/mman.h>
10 #include <assert.h>
11 
12 #include "fio.h"
13 #include "log.h"
14 #include "mutex.h"
15 #include "arch/arch.h"
16 #include "os/os.h"
17 #include "helpers.h"
18 #include "fio_time.h"
19 #include "gettime.h"
20 
__fio_mutex_remove(struct fio_mutex * mutex)21 void __fio_mutex_remove(struct fio_mutex *mutex)
22 {
23 	assert(mutex->magic == FIO_MUTEX_MAGIC);
24 	pthread_cond_destroy(&mutex->cond);
25 
26 	/*
27 	 * Ensure any subsequent attempt to grab this mutex will fail
28 	 * with an assert, instead of just silently hanging.
29 	 */
30 	memset(mutex, 0, sizeof(*mutex));
31 }
32 
fio_mutex_remove(struct fio_mutex * mutex)33 void fio_mutex_remove(struct fio_mutex *mutex)
34 {
35 	__fio_mutex_remove(mutex);
36 	munmap((void *) mutex, sizeof(*mutex));
37 }
38 
cond_init_pshared(pthread_cond_t * cond)39 int cond_init_pshared(pthread_cond_t *cond)
40 {
41 	pthread_condattr_t cattr;
42 	int ret;
43 
44 	ret = pthread_condattr_init(&cattr);
45 	if (ret) {
46 		log_err("pthread_condattr_init: %s\n", strerror(ret));
47 		return ret;
48 	}
49 
50 #ifdef CONFIG_PSHARED
51 	ret = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
52 	if (ret) {
53 		log_err("pthread_condattr_setpshared: %s\n", strerror(ret));
54 		return ret;
55 	}
56 #endif
57 	ret = pthread_cond_init(cond, &cattr);
58 	if (ret) {
59 		log_err("pthread_cond_init: %s\n", strerror(ret));
60 		return ret;
61 	}
62 
63 	return 0;
64 }
65 
mutex_init_pshared(pthread_mutex_t * mutex)66 int mutex_init_pshared(pthread_mutex_t *mutex)
67 {
68 	pthread_mutexattr_t mattr;
69 	int ret;
70 
71 	ret = pthread_mutexattr_init(&mattr);
72 	if (ret) {
73 		log_err("pthread_mutexattr_init: %s\n", strerror(ret));
74 		return ret;
75 	}
76 
77 	/*
78 	 * Not all platforms support process shared mutexes (FreeBSD)
79 	 */
80 #ifdef CONFIG_PSHARED
81 	ret = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
82 	if (ret) {
83 		log_err("pthread_mutexattr_setpshared: %s\n", strerror(ret));
84 		return ret;
85 	}
86 #endif
87 	ret = pthread_mutex_init(mutex, &mattr);
88 	if (ret) {
89 		log_err("pthread_mutex_init: %s\n", strerror(ret));
90 		return ret;
91 	}
92 
93 	return 0;
94 }
95 
mutex_cond_init_pshared(pthread_mutex_t * mutex,pthread_cond_t * cond)96 int mutex_cond_init_pshared(pthread_mutex_t *mutex, pthread_cond_t *cond)
97 {
98 	int ret;
99 
100 	ret = mutex_init_pshared(mutex);
101 	if (ret)
102 		return ret;
103 
104 	ret = cond_init_pshared(cond);
105 	if (ret)
106 		return ret;
107 
108 	return 0;
109 }
110 
__fio_mutex_init(struct fio_mutex * mutex,int value)111 int __fio_mutex_init(struct fio_mutex *mutex, int value)
112 {
113 	int ret;
114 
115 	mutex->value = value;
116 	mutex->magic = FIO_MUTEX_MAGIC;
117 
118 	ret = mutex_cond_init_pshared(&mutex->lock, &mutex->cond);
119 	if (ret)
120 		return ret;
121 
122 	return 0;
123 }
124 
fio_mutex_init(int value)125 struct fio_mutex *fio_mutex_init(int value)
126 {
127 	struct fio_mutex *mutex = NULL;
128 
129 	mutex = (void *) mmap(NULL, sizeof(struct fio_mutex),
130 				PROT_READ | PROT_WRITE,
131 				OS_MAP_ANON | MAP_SHARED, -1, 0);
132 	if (mutex == MAP_FAILED) {
133 		perror("mmap mutex");
134 		return NULL;
135 	}
136 
137 	if (!__fio_mutex_init(mutex, value))
138 		return mutex;
139 
140 	fio_mutex_remove(mutex);
141 	return NULL;
142 }
143 
mutex_timed_out(struct timeval * t,unsigned int msecs)144 static bool mutex_timed_out(struct timeval *t, unsigned int msecs)
145 {
146 	struct timeval now;
147 
148 	gettimeofday(&now, NULL);
149 	return mtime_since(t, &now) >= msecs;
150 }
151 
fio_mutex_down_timeout(struct fio_mutex * mutex,unsigned int msecs)152 int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int msecs)
153 {
154 	struct timeval tv_s;
155 	struct timespec t;
156 	int ret = 0;
157 
158 	assert(mutex->magic == FIO_MUTEX_MAGIC);
159 
160 	gettimeofday(&tv_s, NULL);
161 	t.tv_sec = tv_s.tv_sec;
162 	t.tv_nsec = tv_s.tv_usec * 1000;
163 
164 	t.tv_sec += msecs / 1000;
165 	t.tv_nsec += ((msecs * 1000000ULL) % 1000000000);
166 	if (t.tv_nsec >= 1000000000) {
167 		t.tv_nsec -= 1000000000;
168 		t.tv_sec++;
169 	}
170 
171 	pthread_mutex_lock(&mutex->lock);
172 
173 	mutex->waiters++;
174 	while (!mutex->value && !ret) {
175 		/*
176 		 * Some platforms (FreeBSD 9?) seems to return timed out
177 		 * way too early, double check.
178 		 */
179 		ret = pthread_cond_timedwait(&mutex->cond, &mutex->lock, &t);
180 		if (ret == ETIMEDOUT && !mutex_timed_out(&tv_s, msecs))
181 			ret = 0;
182 	}
183 	mutex->waiters--;
184 
185 	if (!ret) {
186 		mutex->value--;
187 		pthread_mutex_unlock(&mutex->lock);
188 		return 0;
189 	}
190 
191 	pthread_mutex_unlock(&mutex->lock);
192 	return ret;
193 }
194 
fio_mutex_down_trylock(struct fio_mutex * mutex)195 bool fio_mutex_down_trylock(struct fio_mutex *mutex)
196 {
197 	bool ret = true;
198 
199 	assert(mutex->magic == FIO_MUTEX_MAGIC);
200 
201 	pthread_mutex_lock(&mutex->lock);
202 	if (mutex->value) {
203 		mutex->value--;
204 		ret = false;
205 	}
206 	pthread_mutex_unlock(&mutex->lock);
207 
208 	return ret;
209 }
210 
fio_mutex_down(struct fio_mutex * mutex)211 void fio_mutex_down(struct fio_mutex *mutex)
212 {
213 	assert(mutex->magic == FIO_MUTEX_MAGIC);
214 
215 	pthread_mutex_lock(&mutex->lock);
216 
217 	while (!mutex->value) {
218 		mutex->waiters++;
219 		pthread_cond_wait(&mutex->cond, &mutex->lock);
220 		mutex->waiters--;
221 	}
222 
223 	mutex->value--;
224 	pthread_mutex_unlock(&mutex->lock);
225 }
226 
fio_mutex_up(struct fio_mutex * mutex)227 void fio_mutex_up(struct fio_mutex *mutex)
228 {
229 	int do_wake = 0;
230 
231 	assert(mutex->magic == FIO_MUTEX_MAGIC);
232 
233 	pthread_mutex_lock(&mutex->lock);
234 	read_barrier();
235 	if (!mutex->value && mutex->waiters)
236 		do_wake = 1;
237 	mutex->value++;
238 	pthread_mutex_unlock(&mutex->lock);
239 
240 	if (do_wake)
241 		pthread_cond_signal(&mutex->cond);
242 }
243 
fio_rwlock_write(struct fio_rwlock * lock)244 void fio_rwlock_write(struct fio_rwlock *lock)
245 {
246 	assert(lock->magic == FIO_RWLOCK_MAGIC);
247 	pthread_rwlock_wrlock(&lock->lock);
248 }
249 
fio_rwlock_read(struct fio_rwlock * lock)250 void fio_rwlock_read(struct fio_rwlock *lock)
251 {
252 	assert(lock->magic == FIO_RWLOCK_MAGIC);
253 	pthread_rwlock_rdlock(&lock->lock);
254 }
255 
fio_rwlock_unlock(struct fio_rwlock * lock)256 void fio_rwlock_unlock(struct fio_rwlock *lock)
257 {
258 	assert(lock->magic == FIO_RWLOCK_MAGIC);
259 	pthread_rwlock_unlock(&lock->lock);
260 }
261 
fio_rwlock_remove(struct fio_rwlock * lock)262 void fio_rwlock_remove(struct fio_rwlock *lock)
263 {
264 	assert(lock->magic == FIO_RWLOCK_MAGIC);
265 	munmap((void *) lock, sizeof(*lock));
266 }
267 
fio_rwlock_init(void)268 struct fio_rwlock *fio_rwlock_init(void)
269 {
270 	struct fio_rwlock *lock;
271 	pthread_rwlockattr_t attr;
272 	int ret;
273 
274 	lock = (void *) mmap(NULL, sizeof(struct fio_rwlock),
275 				PROT_READ | PROT_WRITE,
276 				OS_MAP_ANON | MAP_SHARED, -1, 0);
277 	if (lock == MAP_FAILED) {
278 		perror("mmap rwlock");
279 		lock = NULL;
280 		goto err;
281 	}
282 
283 	lock->magic = FIO_RWLOCK_MAGIC;
284 
285 	ret = pthread_rwlockattr_init(&attr);
286 	if (ret) {
287 		log_err("pthread_rwlockattr_init: %s\n", strerror(ret));
288 		goto err;
289 	}
290 #ifdef CONFIG_PSHARED
291 	ret = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
292 	if (ret) {
293 		log_err("pthread_rwlockattr_setpshared: %s\n", strerror(ret));
294 		goto destroy_attr;
295 	}
296 
297 	ret = pthread_rwlock_init(&lock->lock, &attr);
298 #else
299 	ret = pthread_rwlock_init(&lock->lock, NULL);
300 #endif
301 
302 	if (ret) {
303 		log_err("pthread_rwlock_init: %s\n", strerror(ret));
304 		goto destroy_attr;
305 	}
306 
307 	pthread_rwlockattr_destroy(&attr);
308 
309 	return lock;
310 destroy_attr:
311 	pthread_rwlockattr_destroy(&attr);
312 err:
313 	if (lock)
314 		fio_rwlock_remove(lock);
315 	return NULL;
316 }
317