Commit | Line | Data |
---|---|---|
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 | ||
17 | void __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 | ||
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; | |
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 | ||
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 | { | |
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 | ||
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 | 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 | } |