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