client: fix double removal of client on job file open failure
[fio.git] / mutex.c
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
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 void fio_mutex_remove(struct fio_mutex *mutex)
28 {
29         __fio_mutex_remove(mutex);
30         munmap((void *) mutex, sizeof(*mutex));
31 }
32
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
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
95 static bool mutex_timed_out(struct timeval *t, unsigned int msecs)
96 {
97         struct timeval now;
98
99         gettimeofday(&now, NULL);
100         return mtime_since(t, &now) >= msecs;
101 }
102
103 int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int msecs)
104 {
105         struct timeval tv_s;
106         struct timespec t;
107         int ret = 0;
108
109         assert(mutex->magic == FIO_MUTEX_MAGIC);
110
111         gettimeofday(&tv_s, NULL);
112         t.tv_sec = tv_s.tv_sec;
113         t.tv_nsec = tv_s.tv_usec * 1000;
114
115         t.tv_sec += msecs / 1000;
116         t.tv_nsec += ((msecs * 1000000) % 1000000000);
117         if (t.tv_nsec >= 1000000000) {
118                 t.tv_nsec -= 1000000000;
119                 t.tv_sec++;
120         }
121
122         pthread_mutex_lock(&mutex->lock);
123
124         mutex->waiters++;
125         while (!mutex->value && !ret) {
126                 /*
127                  * Some platforms (FreeBSD 9?) seems to return timed out
128                  * way too early, double check.
129                  */
130                 ret = pthread_cond_timedwait(&mutex->cond, &mutex->lock, &t);
131                 if (ret == ETIMEDOUT && !mutex_timed_out(&tv_s, msecs))
132                         ret = 0;
133         }
134         mutex->waiters--;
135
136         if (!ret) {
137                 mutex->value--;
138                 pthread_mutex_unlock(&mutex->lock);
139                 return 0;
140         }
141
142         pthread_mutex_unlock(&mutex->lock);
143         return ret;
144 }
145
146 bool fio_mutex_down_trylock(struct fio_mutex *mutex)
147 {
148         bool ret = true;
149
150         assert(mutex->magic == FIO_MUTEX_MAGIC);
151
152         pthread_mutex_lock(&mutex->lock);
153         if (mutex->value) {
154                 mutex->value--;
155                 ret = false;
156         }
157         pthread_mutex_unlock(&mutex->lock);
158
159         return ret;
160 }
161
162 void fio_mutex_down(struct fio_mutex *mutex)
163 {
164         assert(mutex->magic == FIO_MUTEX_MAGIC);
165
166         pthread_mutex_lock(&mutex->lock);
167
168         while (!mutex->value) {
169                 mutex->waiters++;
170                 pthread_cond_wait(&mutex->cond, &mutex->lock);
171                 mutex->waiters--;
172         }
173
174         mutex->value--;
175         pthread_mutex_unlock(&mutex->lock);
176 }
177
178 void fio_mutex_up(struct fio_mutex *mutex)
179 {
180         int do_wake = 0;
181
182         assert(mutex->magic == FIO_MUTEX_MAGIC);
183
184         pthread_mutex_lock(&mutex->lock);
185         read_barrier();
186         if (!mutex->value && mutex->waiters)
187                 do_wake = 1;
188         mutex->value++;
189         pthread_mutex_unlock(&mutex->lock);
190
191         if (do_wake)
192                 pthread_cond_signal(&mutex->cond);
193 }
194
195 void fio_rwlock_write(struct fio_rwlock *lock)
196 {
197         assert(lock->magic == FIO_RWLOCK_MAGIC);
198         pthread_rwlock_wrlock(&lock->lock);
199 }
200
201 void fio_rwlock_read(struct fio_rwlock *lock)
202 {
203         assert(lock->magic == FIO_RWLOCK_MAGIC);
204         pthread_rwlock_rdlock(&lock->lock);
205 }
206
207 void fio_rwlock_unlock(struct fio_rwlock *lock)
208 {
209         assert(lock->magic == FIO_RWLOCK_MAGIC);
210         pthread_rwlock_unlock(&lock->lock);
211 }
212
213 void fio_rwlock_remove(struct fio_rwlock *lock)
214 {
215         assert(lock->magic == FIO_RWLOCK_MAGIC);
216         munmap((void *) lock, sizeof(*lock));
217 }
218
219 struct fio_rwlock *fio_rwlock_init(void)
220 {
221         struct fio_rwlock *lock;
222         pthread_rwlockattr_t attr;
223         int ret;
224
225         lock = (void *) mmap(NULL, sizeof(struct fio_rwlock),
226                                 PROT_READ | PROT_WRITE,
227                                 OS_MAP_ANON | MAP_SHARED, -1, 0);
228         if (lock == MAP_FAILED) {
229                 perror("mmap rwlock");
230                 lock = NULL;
231                 goto err;
232         }
233
234         lock->magic = FIO_RWLOCK_MAGIC;
235
236         ret = pthread_rwlockattr_init(&attr);
237         if (ret) {
238                 log_err("pthread_rwlockattr_init: %s\n", strerror(ret));
239                 goto err;
240         }
241 #ifdef FIO_HAVE_PSHARED_MUTEX
242         ret = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
243         if (ret) {
244                 log_err("pthread_rwlockattr_setpshared: %s\n", strerror(ret));
245                 goto destroy_attr;
246         }
247
248         ret = pthread_rwlock_init(&lock->lock, &attr);
249 #else
250         ret = pthread_rwlock_init(&lock->lock, NULL);
251 #endif
252
253         if (ret) {
254                 log_err("pthread_rwlock_init: %s\n", strerror(ret));
255                 goto destroy_attr;
256         }
257
258         pthread_rwlockattr_destroy(&attr);
259
260         return lock;
261 destroy_attr:
262         pthread_rwlockattr_destroy(&attr);
263 err:
264         if (lock)
265                 fio_rwlock_remove(lock);
266         return NULL;
267 }