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 
fio_mutex_remove(struct fio_mutex * mutex)27 void fio_mutex_remove(struct fio_mutex *mutex)
28 {
29 	__fio_mutex_remove(mutex);
30 	munmap((void *) mutex, sizeof(*mutex));
31 }
32 
__fio_mutex_init(struct fio_mutex * mutex,int value)33 int __fio_mutex_init(struct fio_mutex *mutex, int value)
34 {
35 	pthread_mutexattr_t attr;
36 	pthread_condattr_t cond;
37 	int ret;
38 
39 	mutex->value = value;
40 	mutex->magic = FIO_MUTEX_MAGIC;
41 
42 	ret = pthread_mutexattr_init(&attr);
43 	if (ret) {
44 		log_err("pthread_mutexattr_init: %s\n", strerror(ret));
45 		return ret;
46 	}
47 
48 	/*
49 	 * Not all platforms support process shared mutexes (FreeBSD)
50 	 */
51 #ifdef FIO_HAVE_PSHARED_MUTEX
52 	ret = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
53 	if (ret) {
54 		log_err("pthread_mutexattr_setpshared: %s\n", strerror(ret));
55 		return ret;
56 	}
57 #endif
58 
59 	pthread_condattr_init(&cond);
60 #ifdef FIO_HAVE_PSHARED_MUTEX
61 	pthread_condattr_setpshared(&cond, PTHREAD_PROCESS_SHARED);
62 #endif
63 	pthread_cond_init(&mutex->cond, &cond);
64 
65 	ret = pthread_mutex_init(&mutex->lock, &attr);
66 	if (ret) {
67 		log_err("pthread_mutex_init: %s\n", strerror(ret));
68 		return ret;
69 	}
70 
71 	pthread_condattr_destroy(&cond);
72 	pthread_mutexattr_destroy(&attr);
73 	return 0;
74 }
75 
fio_mutex_init(int value)76 struct fio_mutex *fio_mutex_init(int value)
77 {
78 	struct fio_mutex *mutex = NULL;
79 
80 	mutex = (void *) mmap(NULL, sizeof(struct fio_mutex),
81 				PROT_READ | PROT_WRITE,
82 				OS_MAP_ANON | MAP_SHARED, -1, 0);
83 	if (mutex == MAP_FAILED) {
84 		perror("mmap mutex");
85 		return NULL;
86 	}
87 
88 	if (!__fio_mutex_init(mutex, value))
89 		return mutex;
90 
91 	fio_mutex_remove(mutex);
92 	return NULL;
93 }
94 
mutex_timed_out(struct timeval * t,unsigned int seconds)95 static int mutex_timed_out(struct timeval *t, unsigned int seconds)
96 {
97 	return mtime_since_now(t) >= seconds * 1000;
98 }
99 
fio_mutex_down_timeout(struct fio_mutex * mutex,unsigned int seconds)100 int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int seconds)
101 {
102 	struct timeval tv_s;
103 	struct timespec t;
104 	int ret = 0;
105 
106 	assert(mutex->magic == FIO_MUTEX_MAGIC);
107 
108 	gettimeofday(&tv_s, NULL);
109 	t.tv_sec = tv_s.tv_sec + seconds;
110 	t.tv_nsec = tv_s.tv_usec * 1000;
111 
112 	pthread_mutex_lock(&mutex->lock);
113 
114 	while (!mutex->value && !ret) {
115 		mutex->waiters++;
116 
117 		/*
118 		 * Some platforms (FreeBSD 9?) seems to return timed out
119 		 * way too early, double check.
120 		 */
121 		ret = pthread_cond_timedwait(&mutex->cond, &mutex->lock, &t);
122 		if (ret == ETIMEDOUT && !mutex_timed_out(&tv_s, seconds))
123 			ret = 0;
124 
125 		mutex->waiters--;
126 	}
127 
128 	if (!ret) {
129 		mutex->value--;
130 		pthread_mutex_unlock(&mutex->lock);
131 	}
132 
133 	return ret;
134 }
135 
fio_mutex_down_trylock(struct fio_mutex * mutex)136 int fio_mutex_down_trylock(struct fio_mutex *mutex)
137 {
138 	int ret = 1;
139 
140 	assert(mutex->magic == FIO_MUTEX_MAGIC);
141 
142 	pthread_mutex_lock(&mutex->lock);
143 	if (mutex->value) {
144 		mutex->value--;
145 		ret = 0;
146 	}
147 	pthread_mutex_unlock(&mutex->lock);
148 
149 	return ret;
150 }
151 
fio_mutex_down(struct fio_mutex * mutex)152 void fio_mutex_down(struct fio_mutex *mutex)
153 {
154 	assert(mutex->magic == FIO_MUTEX_MAGIC);
155 
156 	pthread_mutex_lock(&mutex->lock);
157 
158 	while (!mutex->value) {
159 		mutex->waiters++;
160 		pthread_cond_wait(&mutex->cond, &mutex->lock);
161 		mutex->waiters--;
162 	}
163 
164 	mutex->value--;
165 	pthread_mutex_unlock(&mutex->lock);
166 }
167 
fio_mutex_up(struct fio_mutex * mutex)168 void fio_mutex_up(struct fio_mutex *mutex)
169 {
170 	int do_wake = 0;
171 
172 	assert(mutex->magic == FIO_MUTEX_MAGIC);
173 
174 	pthread_mutex_lock(&mutex->lock);
175 	read_barrier();
176 	if (!mutex->value && mutex->waiters)
177 		do_wake = 1;
178 	mutex->value++;
179 	pthread_mutex_unlock(&mutex->lock);
180 
181 	if (do_wake)
182 		pthread_cond_signal(&mutex->cond);
183 }
184 
fio_rwlock_write(struct fio_rwlock * lock)185 void fio_rwlock_write(struct fio_rwlock *lock)
186 {
187 	assert(lock->magic == FIO_RWLOCK_MAGIC);
188 	pthread_rwlock_wrlock(&lock->lock);
189 }
190 
fio_rwlock_read(struct fio_rwlock * lock)191 void fio_rwlock_read(struct fio_rwlock *lock)
192 {
193 	assert(lock->magic == FIO_RWLOCK_MAGIC);
194 	pthread_rwlock_rdlock(&lock->lock);
195 }
196 
fio_rwlock_unlock(struct fio_rwlock * lock)197 void fio_rwlock_unlock(struct fio_rwlock *lock)
198 {
199 	assert(lock->magic == FIO_RWLOCK_MAGIC);
200 	pthread_rwlock_unlock(&lock->lock);
201 }
202 
fio_rwlock_remove(struct fio_rwlock * lock)203 void fio_rwlock_remove(struct fio_rwlock *lock)
204 {
205 	assert(lock->magic == FIO_RWLOCK_MAGIC);
206 	munmap((void *) lock, sizeof(*lock));
207 }
208 
fio_rwlock_init(void)209 struct fio_rwlock *fio_rwlock_init(void)
210 {
211 	struct fio_rwlock *lock;
212 	pthread_rwlockattr_t attr;
213 	int ret;
214 
215 	lock = (void *) mmap(NULL, sizeof(struct fio_rwlock),
216 				PROT_READ | PROT_WRITE,
217 				OS_MAP_ANON | MAP_SHARED, -1, 0);
218 	if (lock == MAP_FAILED) {
219 		perror("mmap rwlock");
220 		lock = NULL;
221 		goto err;
222 	}
223 
224 	lock->magic = FIO_RWLOCK_MAGIC;
225 
226 	ret = pthread_rwlockattr_init(&attr);
227 	if (ret) {
228 		log_err("pthread_rwlockattr_init: %s\n", strerror(ret));
229 		goto err;
230 	}
231 #ifdef FIO_HAVE_PSHARED_MUTEX
232 	ret = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
233 	if (ret) {
234 		log_err("pthread_rwlockattr_setpshared: %s\n", strerror(ret));
235 		goto destroy_attr;
236 	}
237 
238 	ret = pthread_rwlock_init(&lock->lock, &attr);
239 #else
240 	ret = pthread_rwlock_init(&lock->lock, NULL);
241 #endif
242 
243 	if (ret) {
244 		log_err("pthread_rwlock_init: %s\n", strerror(ret));
245 		goto destroy_attr;
246 	}
247 
248 	pthread_rwlockattr_destroy(&attr);
249 
250 	return lock;
251 destroy_attr:
252 	pthread_rwlockattr_destroy(&attr);
253 err:
254 	if (lock)
255 		fio_rwlock_remove(lock);
256 	return NULL;
257 }
258