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 | { | |
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 | } |