mutex: more clock fixes
[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
11 #include "fio.h"
12 #include "log.h"
13 #include "mutex.h"
14 #include "arch/arch.h"
15 #include "os/os.h"
16 #include "helpers.h"
17 #include "time.h"
18 #include "gettime.h"
19
20 static clockid_t fio_clk_id = CLOCK_REALTIME;
21
22 void fio_mutex_remove(struct fio_mutex *mutex)
23 {
24         pthread_cond_destroy(&mutex->cond);
25         munmap((void *) mutex, sizeof(*mutex));
26 }
27
28 struct fio_mutex *fio_mutex_init(int value)
29 {
30         struct fio_mutex *mutex = NULL;
31         pthread_mutexattr_t attr;
32         pthread_condattr_t cond;
33         int ret;
34
35         mutex = (void *) mmap(NULL, sizeof(struct fio_mutex),
36                                 PROT_READ | PROT_WRITE,
37                                 OS_MAP_ANON | MAP_SHARED, -1, 0);
38         if (mutex == MAP_FAILED) {
39                 perror("mmap mutex");
40                 mutex = NULL;
41                 goto err;
42         }
43
44         mutex->value = value;
45
46         ret = pthread_mutexattr_init(&attr);
47         if (ret) {
48                 log_err("pthread_mutexattr_init: %s\n", strerror(ret));
49                 goto err;
50         }
51
52         /*
53          * Not all platforms support process shared mutexes (FreeBSD)
54          */
55 #ifdef FIO_HAVE_PSHARED_MUTEX
56         ret = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
57         if (ret) {
58                 log_err("pthread_mutexattr_setpshared: %s\n", strerror(ret));
59                 goto err;
60         }
61 #endif
62
63         pthread_condattr_init(&cond);
64 #ifdef FIO_HAVE_PSHARED_MUTEX
65         pthread_condattr_setpshared(&cond, PTHREAD_PROCESS_SHARED);
66 #endif
67 #ifdef FIO_HAVE_CLOCK_MONOTONIC
68         pthread_condattr_setclock(&cond, fio_clk_id);
69 #endif
70         pthread_cond_init(&mutex->cond, &cond);
71
72         ret = pthread_mutex_init(&mutex->lock, &attr);
73         if (ret) {
74                 log_err("pthread_mutex_init: %s\n", strerror(ret));
75                 goto err;
76         }
77
78         pthread_condattr_destroy(&cond);
79         pthread_mutexattr_destroy(&attr);
80
81         return mutex;
82 err:
83         if (mutex)
84                 fio_mutex_remove(mutex);
85
86         return NULL;
87 }
88
89 static int mutex_timed_out(struct timeval *t, unsigned int seconds)
90 {
91         return mtime_since_now(t) >= seconds * 1000;
92 }
93
94 int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int seconds)
95 {
96         struct timeval tv_s;
97         struct timespec t;
98         int ret = 0;
99
100         fio_gettime(&tv_s, NULL);
101
102         clock_gettime(fio_clk_id, &t);
103         t.tv_sec += seconds;
104
105         pthread_mutex_lock(&mutex->lock);
106
107         while (!mutex->value && !ret) {
108                 mutex->waiters++;
109
110                 /*
111                  * Some platforms (FreeBSD 9?) seems to return timed out
112                  * way too early, double check.
113                  */
114                 ret = pthread_cond_timedwait(&mutex->cond, &mutex->lock, &t);
115                 if (ret == ETIMEDOUT && !mutex_timed_out(&tv_s, seconds)) {
116                         pthread_mutex_lock(&mutex->lock);
117                         ret = 0;
118                 }
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
131 void fio_mutex_down(struct fio_mutex *mutex)
132 {
133         pthread_mutex_lock(&mutex->lock);
134
135         while (!mutex->value) {
136                 mutex->waiters++;
137                 pthread_cond_wait(&mutex->cond, &mutex->lock);
138                 mutex->waiters--;
139         }
140
141         mutex->value--;
142         pthread_mutex_unlock(&mutex->lock);
143 }
144
145 void fio_mutex_up(struct fio_mutex *mutex)
146 {
147         pthread_mutex_lock(&mutex->lock);
148         read_barrier();
149         if (!mutex->value && mutex->waiters)
150                 pthread_cond_signal(&mutex->cond);
151         mutex->value++;
152         pthread_mutex_unlock(&mutex->lock);
153 }
154
155 void fio_mutex_down_write(struct fio_mutex *mutex)
156 {
157         pthread_mutex_lock(&mutex->lock);
158
159         while (mutex->value != 0) {
160                 mutex->waiters++;
161                 pthread_cond_wait(&mutex->cond, &mutex->lock);
162                 mutex->waiters--;
163         }
164
165         mutex->value--;
166         pthread_mutex_unlock(&mutex->lock);
167 }
168
169 void fio_mutex_down_read(struct fio_mutex *mutex)
170 {
171         pthread_mutex_lock(&mutex->lock);
172
173         while (mutex->value < 0) {
174                 mutex->waiters++;
175                 pthread_cond_wait(&mutex->cond, &mutex->lock);
176                 mutex->waiters--;
177         }
178
179         mutex->value++;
180         pthread_mutex_unlock(&mutex->lock);
181 }
182
183 void fio_mutex_up_read(struct fio_mutex *mutex)
184 {
185         pthread_mutex_lock(&mutex->lock);
186         mutex->value--;
187         read_barrier();
188         if (mutex->value >= 0 && mutex->waiters)
189                 pthread_cond_signal(&mutex->cond);
190         pthread_mutex_unlock(&mutex->lock);
191 }
192
193 void fio_mutex_up_write(struct fio_mutex *mutex)
194 {
195         pthread_mutex_lock(&mutex->lock);
196         mutex->value++;
197         read_barrier();
198         if (mutex->value >= 0 && mutex->waiters)
199                 pthread_cond_signal(&mutex->cond);
200         pthread_mutex_unlock(&mutex->lock);
201 }
202
203 static void fio_init fio_mutex_global_init(void)
204 {
205 #ifdef FIO_HAVE_PTHREAD_CONDATTR_SETCLOCK
206 #ifdef FIO_HAVE_CLOCK_MONOTONIC
207         pthread_condattr_t cond;
208
209         pthread_condattr_init(&cond);
210
211         if (!pthread_condattr_setclock(&cond, CLOCK_MONOTONIC))
212                 fio_clk_id = CLOCK_MONOTONIC;
213
214         pthread_condattr_destroy(&cond);
215 #endif
216 #endif
217 }