t/io_uring: clarify polled support is fs + device
[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 timeval tv_s;
89         struct timespec base;
90         struct timespec t;
91         int ret = 0;
92
93         assert(sem->magic == FIO_SEM_MAGIC);
94
95         gettimeofday(&tv_s, NULL);
96         base.tv_sec = t.tv_sec = tv_s.tv_sec;
97         base.tv_nsec = t.tv_nsec = tv_s.tv_usec * 1000;
98
99         t.tv_sec += msecs / 1000;
100         t.tv_nsec += ((msecs * 1000000ULL) % 1000000000);
101         if (t.tv_nsec >= 1000000000) {
102                 t.tv_nsec -= 1000000000;
103                 t.tv_sec++;
104         }
105
106         pthread_mutex_lock(&sem->lock);
107
108         sem->waiters++;
109         while (!sem->value && !ret) {
110                 /*
111                  * Some platforms (FreeBSD 9?) seems to return timed out
112                  * way too early, double check.
113                  */
114                 ret = pthread_cond_timedwait(&sem->cond, &sem->lock, &t);
115                 if (ret == ETIMEDOUT && !sem_timed_out(&base, msecs))
116                         ret = 0;
117         }
118         sem->waiters--;
119
120         if (!ret) {
121                 sem->value--;
122                 pthread_mutex_unlock(&sem->lock);
123                 return 0;
124         }
125
126         pthread_mutex_unlock(&sem->lock);
127         return ret;
128 }
129
130 bool fio_sem_down_trylock(struct fio_sem *sem)
131 {
132         bool ret = true;
133
134         assert(sem->magic == FIO_SEM_MAGIC);
135
136         pthread_mutex_lock(&sem->lock);
137         if (sem->value) {
138                 sem->value--;
139                 ret = false;
140         }
141         pthread_mutex_unlock(&sem->lock);
142
143         return ret;
144 }
145
146 void fio_sem_down(struct fio_sem *sem)
147 {
148         assert(sem->magic == FIO_SEM_MAGIC);
149
150         pthread_mutex_lock(&sem->lock);
151
152         while (!sem->value) {
153                 sem->waiters++;
154                 pthread_cond_wait(&sem->cond, &sem->lock);
155                 sem->waiters--;
156         }
157
158         sem->value--;
159         pthread_mutex_unlock(&sem->lock);
160 }
161
162 void fio_sem_up(struct fio_sem *sem)
163 {
164         int do_wake = 0;
165
166         assert(sem->magic == FIO_SEM_MAGIC);
167
168         pthread_mutex_lock(&sem->lock);
169         read_barrier();
170         if (!sem->value && sem->waiters)
171                 do_wake = 1;
172         sem->value++;
173
174         if (do_wake)
175                 pthread_cond_signal(&sem->cond);
176
177         pthread_mutex_unlock(&sem->lock);
178 }