t/zbd: update test case 42
[fio.git] / fio_sem.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <sys/mman.h>
4 #include <assert.h>
5 #ifdef CONFIG_VALGRIND_DEV
6 #include <valgrind/valgrind.h>
7 #else
8 #define RUNNING_ON_VALGRIND 0
9 #endif
10
11 #include "fio_sem.h"
12 #include "pshared.h"
13 #include "os/os.h"
14 #include "fio_time.h"
15 #include "gettime.h"
16
17 void __fio_sem_remove(struct fio_sem *sem)
18 {
19         assert(sem->magic == FIO_SEM_MAGIC);
20         pthread_mutex_destroy(&sem->lock);
21         pthread_cond_destroy(&sem->cond);
22
23         /*
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));
31 }
32
33 void fio_sem_remove(struct fio_sem *sem)
34 {
35         __fio_sem_remove(sem);
36         munmap((void *) sem, sizeof(*sem));
37 }
38
39 int __fio_sem_init(struct fio_sem *sem, int value)
40 {
41         int ret;
42
43         sem->value = value;
44         /* Initialize .waiters explicitly for Valgrind. */
45         sem->waiters = 0;
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
55 struct 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
74 static 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
86 int fio_sem_down_timeout(struct fio_sem *sem, unsigned int msecs)
87 {
88         struct timespec base;
89         struct timespec t;
90         int ret = 0;
91
92         assert(sem->magic == FIO_SEM_MAGIC);
93
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;
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
133 bool 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
149 void 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
165 void 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         if (!sem->value && sem->waiters)
173                 do_wake = 1;
174         sem->value++;
175
176         if (do_wake)
177                 pthread_cond_signal(&sem->cond);
178
179         pthread_mutex_unlock(&sem->lock);
180 }