parlib: Add 'timed' functions for sems/mtxs/cvs
[akaros.git] / user / utest / cv.c
1 /* Copyright (c) 2016 Google, Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 #include <utest/utest.h>
6 #include <parlib/uthread.h>
7 #include <pthread.h>
8 #include <time.h>
9
10 TEST_SUITE("CV");
11
12 /* <--- Begin definition of test cases ---> */
13
14 struct common_args {
15         uth_cond_var_t                          *cv;
16         uth_mutex_t                                     *mtx;
17         bool                                            flag;
18         unsigned int                            wait_sleep;
19         unsigned int                            sig_sleep;
20 };
21
22 #define PTH_TEST_TRUE                   (void*)1
23
24 bool test_signal_no_wait(void)
25 {
26         uth_cond_var_t *cv = uth_cond_var_alloc();
27
28         uth_cond_var_broadcast(cv);
29         uth_cond_var_signal(cv);
30         pthread_yield();
31         uth_cond_var_free(cv);
32         return TRUE;
33 }
34
35 void *__cv_signaller(void *arg)
36 {
37         struct common_args *args = (struct common_args*)arg;
38
39         uthread_usleep(args->sig_sleep);
40         uth_mutex_lock(args->mtx);
41         args->flag = TRUE;
42         uth_mutex_unlock(args->mtx);
43         /* We can actually signal outside the mutex if we want.  Remember the
44          * invariant: whenever the flag is set from FALSE to TRUE, all waiters that
45          * saw FALSE are on the CV's waitqueue.  That's true even after we unlock
46          * the mutex. */
47         uth_cond_var_signal(args->cv);
48         return PTH_TEST_TRUE;
49 }
50
51 void *__cv_broadcaster(void *arg)
52 {
53         struct common_args *args = (struct common_args*)arg;
54
55         uthread_usleep(args->sig_sleep);
56         uth_mutex_lock(args->mtx);
57         args->flag = TRUE;
58         uth_mutex_unlock(args->mtx);
59         /* We can actually signal outside the mutex if we want.  Remember the
60          * invariant: whenever the flag is set from FALSE to TRUE, all waiters that
61          * saw FALSE are on the CV's waitqueue.  That's true even after we unlock
62          * the mutex. */
63         uth_cond_var_broadcast(args->cv);
64         return PTH_TEST_TRUE;
65 }
66
67 void *__cv_waiter(void *arg)
68 {
69         struct common_args *args = (struct common_args*)arg;
70
71         uthread_usleep(args->wait_sleep);
72         uth_mutex_lock(args->mtx);
73         while (!args->flag)
74                 uth_cond_var_wait(args->cv, args->mtx);
75         UT_ASSERT(args->flag);
76         uth_mutex_unlock(args->mtx);
77         return PTH_TEST_TRUE;
78 }
79
80 /* Basic one signaller, one receiver.  We want to vary the amount of time the
81  * sender and receiver delays, starting with (1ms, 0ms) and ending with (0ms,
82  * 1ms).  At each extreme, such as with the sender waiting 1ms, the
83  * receiver/waiter should hit the "check and wait" point well before the
84  * sender/signaller hits the "change state and signal" point. */
85 bool test_signal(void)
86 {
87         struct common_args local_a, *args = &local_a;
88         pthread_t signaller, waiter;
89         void *sig_join, *wait_join;
90         int ret;
91         static uth_mutex_t static_mtx = UTH_MUTEX_INIT;
92         static uth_cond_var_t static_cv = UTH_COND_VAR_INIT;
93
94         /* Also testing the static initializers.  Note we never free these. */
95         args->cv = &static_cv;
96         args->mtx = &static_mtx;
97
98         for (int i = 0; i < 1000; i += 10) {
99                 args->flag = FALSE;
100                 args->wait_sleep = i;
101                 args->sig_sleep = 1000 - i;
102
103                 ret = pthread_create(&waiter, 0, __cv_waiter, args);
104                 UT_ASSERT(!ret);
105                 ret = pthread_create(&signaller, 0, __cv_signaller, args);
106                 UT_ASSERT(!ret);
107                 ret = pthread_join(waiter, &wait_join);
108                 UT_ASSERT(!ret);
109                 ret = pthread_join(signaller, &sig_join);
110                 UT_ASSERT(!ret);
111                 UT_ASSERT_M("Waiter Failed", wait_join == PTH_TEST_TRUE);
112                 UT_ASSERT_M("Signaller Failed", sig_join == PTH_TEST_TRUE);
113         }
114
115         return TRUE;
116 }
117
118 bool test_broadcast(void)
119 {
120         #define NR_WAITERS 20
121         struct common_args local_a, *args = &local_a;
122         pthread_t bcaster, waiters[NR_WAITERS];
123         void *bcast_join, *wait_joins[NR_WAITERS];
124         int ret;
125
126         args->cv = uth_cond_var_alloc();
127         args->mtx = uth_mutex_alloc();
128         args->flag = FALSE;
129         args->wait_sleep = 0;
130         args->sig_sleep = 1000;
131
132         for (int i = 0; i < NR_WAITERS; i++) {
133                 ret = pthread_create(&waiters[i], 0, __cv_waiter, args);
134                 UT_ASSERT(!ret);
135         }
136         ret = pthread_create(&bcaster, 0, __cv_broadcaster, args);
137         UT_ASSERT(!ret);
138
139         ret = pthread_join(bcaster, &bcast_join);
140         UT_ASSERT(!ret);
141         UT_ASSERT_M("Broadcaster Failed", bcast_join == PTH_TEST_TRUE);
142         for (int i = 0; i < NR_WAITERS; i++) {
143                 ret = pthread_join(waiters[i], &wait_joins[i]);
144                 UT_ASSERT(!ret);
145                 UT_ASSERT_M("Waiter Failed", wait_joins[i] == PTH_TEST_TRUE);
146         }
147
148         uth_cond_var_free(args->cv);
149         uth_mutex_free(args->mtx);
150         return TRUE;
151 }
152
153 static bool __test_recurse(struct uth_recurse_mutex *r_mtx)
154 {
155         bool test;
156
157         uth_recurse_mutex_lock(r_mtx);
158         /* should be one lock deep */
159         UT_ASSERT(r_mtx->count == 1);
160         UT_ASSERT(r_mtx->lockholder == current_uthread);
161
162         test = uth_recurse_mutex_trylock(r_mtx);
163         UT_ASSERT_M("Failed to re(try)lock our owned mutex!", test);
164         /* should be two locks deep */
165         UT_ASSERT(r_mtx->count == 2);
166         UT_ASSERT(r_mtx->lockholder == current_uthread);
167
168         uth_recurse_mutex_lock(r_mtx);
169         /* should be three locks deep */
170         UT_ASSERT(r_mtx->count == 3);
171         UT_ASSERT(r_mtx->lockholder == current_uthread);
172
173         uth_recurse_mutex_unlock(r_mtx);
174         /* should be two locks deep */
175         UT_ASSERT(r_mtx->count == 2);
176         UT_ASSERT(r_mtx->lockholder == current_uthread);
177
178         uth_recurse_mutex_unlock(r_mtx);
179         /* should be one lock deep */
180         UT_ASSERT(r_mtx->count == 1);
181         UT_ASSERT(r_mtx->lockholder == current_uthread);
182
183         uth_recurse_mutex_unlock(r_mtx);
184         /* should be unlocked */
185         UT_ASSERT(r_mtx->count == 0);
186         UT_ASSERT(!r_mtx->lockholder);
187
188         return TRUE;
189 }
190
191 bool test_recurse(void)
192 {
193         uth_recurse_mutex_t *r_mtx;
194
195         r_mtx = uth_recurse_mutex_alloc();
196         UT_ASSERT(r_mtx);
197         if (!__test_recurse(r_mtx))
198                 return FALSE;
199         uth_recurse_mutex_free(r_mtx);
200
201         return TRUE;
202 }
203
204 bool test_recurse_static(void)
205 {
206         static uth_recurse_mutex_t static_recurse_mtx = UTH_RECURSE_MUTEX_INIT;
207
208         return __test_recurse(&static_recurse_mtx);
209 }
210
211 static bool __test_semaphore(struct uth_semaphore *sem, int count)
212 {
213         bool can_down;
214
215         UT_ASSERT(count > 2);   /* for our own use */
216         UT_ASSERT(sem->count == count);
217         /* should be able to down it count times without blocking */
218         for (int i = 0; i < count; i++) {
219                 uth_semaphore_down(sem);
220                 UT_ASSERT(sem->count == count - (i + 1));
221         }
222
223         /* shouldn't be able to down, since we grabbed them all */
224         can_down = uth_semaphore_trydown(sem);
225         UT_ASSERT(!can_down);
226         UT_ASSERT(sem->count == 0);
227
228         uth_semaphore_up(sem);
229         UT_ASSERT(sem->count == 1);
230         can_down = uth_semaphore_trydown(sem);
231         UT_ASSERT(can_down);
232         UT_ASSERT(sem->count == 0);
233
234         for (int i = 0; i < count; i++) {
235                 uth_semaphore_up(sem);
236                 UT_ASSERT(sem->count == i + 1);
237         }
238
239         return TRUE;
240 }
241
242 bool test_semaphore(void)
243 {
244         uth_semaphore_t *sem;
245
246         sem = uth_semaphore_alloc(5);
247         UT_ASSERT(sem);
248         if (!__test_semaphore(sem, 5))
249                 return FALSE;
250         uth_semaphore_free(sem);
251
252         return TRUE;
253 }
254
255 bool test_semaphore_static(void)
256 {
257         static uth_semaphore_t static_semaphore = UTH_SEMAPHORE_INIT(5);
258
259         return __test_semaphore(&static_semaphore, 5);
260 }
261
262 bool test_semaphore_timeout(void)
263 {
264         static uth_semaphore_t sem = UTH_SEMAPHORE_INIT(1);
265         struct timespec timeout[1];
266         int ret;
267         bool got_it;
268
269         ret = clock_gettime(CLOCK_REALTIME, timeout);
270         UT_ASSERT(!ret);
271         timeout->tv_nsec += 500000000;
272         got_it = uth_semaphore_timed_down(&sem, timeout);
273         UT_ASSERT(got_it);
274
275         /* Second time we still hold the sem and would block and should time out. */
276         UT_ASSERT(sem.count == 0);
277         ret = clock_gettime(CLOCK_REALTIME, timeout);
278         UT_ASSERT(!ret);
279         timeout->tv_nsec += 500000000;
280         got_it = uth_semaphore_timed_down(&sem, timeout);
281         UT_ASSERT(!got_it);
282
283         return TRUE;
284 }
285
286 bool test_cv_timeout(void)
287 {
288         static uth_mutex_t mtx = UTH_MUTEX_INIT;
289         static uth_cond_var_t cv = UTH_COND_VAR_INIT;
290         struct timespec timeout[1];
291         int ret;
292         bool was_signalled;
293
294         uth_mutex_lock(&mtx);
295         ret = clock_gettime(CLOCK_REALTIME, timeout);
296         UT_ASSERT(!ret);
297         timeout->tv_nsec += 500000000;
298         was_signalled = uth_cond_var_timed_wait(&cv, &mtx, timeout);
299         UT_ASSERT(!was_signalled);
300         UT_ASSERT(mtx.count == 0);      /* semaphore's count variable */
301         uth_mutex_unlock(&mtx);
302         UT_ASSERT(mtx.count == 1);
303
304         return TRUE;
305 }
306
307 bool test_cv_recurse_timeout(void)
308 {
309         static uth_recurse_mutex_t r_mtx = UTH_RECURSE_MUTEX_INIT;
310         static uth_cond_var_t cv = UTH_COND_VAR_INIT;
311         struct timespec timeout[1];
312         int ret;
313         bool was_signalled;
314
315         /* Get three-deep locks, make sure the bookkeeping is right */
316         uth_recurse_mutex_lock(&r_mtx);
317         uth_recurse_mutex_lock(&r_mtx);
318         uth_recurse_mutex_lock(&r_mtx);
319         UT_ASSERT(r_mtx.count == 3);
320         UT_ASSERT(r_mtx.mtx.count == 0);
321
322         ret = clock_gettime(CLOCK_REALTIME, timeout);
323         UT_ASSERT(!ret);
324         timeout->tv_nsec += 500000000;
325         was_signalled = uth_cond_var_timed_wait_recurse(&cv, &r_mtx, timeout);
326         UT_ASSERT(!was_signalled);
327         UT_ASSERT(r_mtx.count == 3);
328         UT_ASSERT(r_mtx.mtx.count == 0);
329
330         /* Unlock our three locks, then make sure the semaphore/mtx is unlocked. */
331         uth_recurse_mutex_unlock(&r_mtx);
332         uth_recurse_mutex_unlock(&r_mtx);
333         uth_recurse_mutex_unlock(&r_mtx);
334         UT_ASSERT(r_mtx.count == 0);
335         UT_ASSERT(r_mtx.mtx.count == 1);
336
337         return TRUE;
338 }
339
340 /* <--- End definition of test cases ---> */
341
342 struct utest utests[] = {
343         UTEST_REG(signal_no_wait),
344         UTEST_REG(signal),
345         UTEST_REG(broadcast),
346         UTEST_REG(recurse),
347         UTEST_REG(recurse_static),
348         UTEST_REG(semaphore),
349         UTEST_REG(semaphore_static),
350         UTEST_REG(semaphore_timeout),
351         UTEST_REG(cv_timeout),
352         UTEST_REG(cv_recurse_timeout),
353 };
354 int num_utests = sizeof(utests) / sizeof(struct utest);
355
356 int main(int argc, char *argv[])
357 {
358         // Run test suite passing it all the args as whitelist of what tests to run.
359         char **whitelist = &argv[1];
360         int whitelist_len = argc - 1;
361
362         RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len);
363 }