Merge branch 'testing' of https://github.com/vincentkfu/fio
[fio.git] / fio_sem.c
CommitLineData
3d2d14bc 1#include <stdio.h>
971caeb1
BVA
2#include <string.h>
3#include <sys/mman.h>
4#include <assert.h>
0ffccc21
BVA
5#ifdef CONFIG_VALGRIND_DEV
6#include <valgrind/valgrind.h>
7#else
8#define RUNNING_ON_VALGRIND 0
9#endif
971caeb1 10
971caeb1
BVA
11#include "fio_sem.h"
12#include "pshared.h"
13#include "os/os.h"
14#include "fio_time.h"
15#include "gettime.h"
16
17void __fio_sem_remove(struct fio_sem *sem)
18{
19 assert(sem->magic == FIO_SEM_MAGIC);
0ffccc21 20 pthread_mutex_destroy(&sem->lock);
971caeb1
BVA
21 pthread_cond_destroy(&sem->cond);
22
23 /*
0ffccc21
BVA
24 * When not running on Valgrind, ensure any subsequent attempt to grab
25 * this semaphore will fail with an assert, instead of just silently
26 * hanging. When running on Valgrind, let Valgrind detect
27 * use-after-free.
28 */
29 if (!RUNNING_ON_VALGRIND)
30 memset(sem, 0, sizeof(*sem));
971caeb1
BVA
31}
32
33void fio_sem_remove(struct fio_sem *sem)
34{
35 __fio_sem_remove(sem);
36 munmap((void *) sem, sizeof(*sem));
37}
38
39int __fio_sem_init(struct fio_sem *sem, int value)
40{
41 int ret;
42
43 sem->value = value;
0ffccc21
BVA
44 /* Initialize .waiters explicitly for Valgrind. */
45 sem->waiters = 0;
971caeb1
BVA
46 sem->magic = FIO_SEM_MAGIC;
47
48 ret = mutex_cond_init_pshared(&sem->lock, &sem->cond);
49 if (ret)
50 return ret;
51
52 return 0;
53}
54
55struct fio_sem *fio_sem_init(int value)
56{
57 struct fio_sem *sem = NULL;
58
59 sem = (void *) mmap(NULL, sizeof(struct fio_sem),
60 PROT_READ | PROT_WRITE,
61 OS_MAP_ANON | MAP_SHARED, -1, 0);
62 if (sem == MAP_FAILED) {
63 perror("mmap semaphore");
64 return NULL;
65 }
66
67 if (!__fio_sem_init(sem, value))
68 return sem;
69
70 fio_sem_remove(sem);
71 return NULL;
72}
73
74static bool sem_timed_out(struct timespec *t, unsigned int msecs)
75{
76 struct timeval tv;
77 struct timespec now;
78
79 gettimeofday(&tv, NULL);
80 now.tv_sec = tv.tv_sec;
81 now.tv_nsec = tv.tv_usec * 1000;
82
83 return mtime_since(t, &now) >= msecs;
84}
85
86int fio_sem_down_timeout(struct fio_sem *sem, unsigned int msecs)
87{
971caeb1
BVA
88 struct timespec base;
89 struct timespec t;
90 int ret = 0;
91
92 assert(sem->magic == FIO_SEM_MAGIC);
93
78b66d32
BVA
94#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK
95 clock_gettime(CLOCK_MONOTONIC, &t);
96#else
97 clock_gettime(CLOCK_REALTIME, &t);
98#endif
99
100 base = t;
971caeb1
BVA
101
102 t.tv_sec += msecs / 1000;
103 t.tv_nsec += ((msecs * 1000000ULL) % 1000000000);
104 if (t.tv_nsec >= 1000000000) {
105 t.tv_nsec -= 1000000000;
106 t.tv_sec++;
107 }
108
109 pthread_mutex_lock(&sem->lock);
110
111 sem->waiters++;
112 while (!sem->value && !ret) {
113 /*
114 * Some platforms (FreeBSD 9?) seems to return timed out
115 * way too early, double check.
116 */
117 ret = pthread_cond_timedwait(&sem->cond, &sem->lock, &t);
118 if (ret == ETIMEDOUT && !sem_timed_out(&base, msecs))
119 ret = 0;
120 }
121 sem->waiters--;
122
123 if (!ret) {
124 sem->value--;
125 pthread_mutex_unlock(&sem->lock);
126 return 0;
127 }
128
129 pthread_mutex_unlock(&sem->lock);
130 return ret;
131}
132
133bool fio_sem_down_trylock(struct fio_sem *sem)
134{
135 bool ret = true;
136
137 assert(sem->magic == FIO_SEM_MAGIC);
138
139 pthread_mutex_lock(&sem->lock);
140 if (sem->value) {
141 sem->value--;
142 ret = false;
143 }
144 pthread_mutex_unlock(&sem->lock);
145
146 return ret;
147}
148
149void fio_sem_down(struct fio_sem *sem)
150{
151 assert(sem->magic == FIO_SEM_MAGIC);
152
153 pthread_mutex_lock(&sem->lock);
154
155 while (!sem->value) {
156 sem->waiters++;
157 pthread_cond_wait(&sem->cond, &sem->lock);
158 sem->waiters--;
159 }
160
161 sem->value--;
162 pthread_mutex_unlock(&sem->lock);
163}
164
165void fio_sem_up(struct fio_sem *sem)
166{
167 int do_wake = 0;
168
169 assert(sem->magic == FIO_SEM_MAGIC);
170
171 pthread_mutex_lock(&sem->lock);
172 read_barrier();
173 if (!sem->value && sem->waiters)
174 do_wake = 1;
175 sem->value++;
176
177 if (do_wake)
178 pthread_cond_signal(&sem->cond);
179
180 pthread_mutex_unlock(&sem->lock);
181}