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