parlib: Change opaque mutex/cv types to be structs
[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
9 TEST_SUITE("CV");
10
11 /* <--- Begin definition of test cases ---> */
12
13 struct common_args {
14         uth_cond_var_t                          *cv;
15         uth_mutex_t                                     *mtx;
16         bool                                            flag;
17         unsigned int                            wait_sleep;
18         unsigned int                            sig_sleep;
19 };
20
21 #define PTH_TEST_TRUE                   (void*)1
22
23 bool test_signal_no_wait(void)
24 {
25         uth_cond_var_t *cv = uth_cond_var_alloc();
26
27         uth_cond_var_broadcast(cv);
28         uth_cond_var_signal(cv);
29         pthread_yield();
30         uth_cond_var_free(cv);
31         return TRUE;
32 }
33
34 void *__cv_signaller(void *arg)
35 {
36         struct common_args *args = (struct common_args*)arg;
37
38         uthread_usleep(args->sig_sleep);
39         uth_mutex_lock(args->mtx);
40         args->flag = TRUE;
41         uth_mutex_unlock(args->mtx);
42         /* We can actually signal outside the mutex if we want.  Remember the
43          * invariant: whenever the flag is set from FALSE to TRUE, all waiters that
44          * saw FALSE are on the CV's waitqueue.  That's true even after we unlock
45          * the mutex. */
46         uth_cond_var_signal(args->cv);
47         return PTH_TEST_TRUE;
48 }
49
50 void *__cv_broadcaster(void *arg)
51 {
52         struct common_args *args = (struct common_args*)arg;
53
54         uthread_usleep(args->sig_sleep);
55         uth_mutex_lock(args->mtx);
56         args->flag = TRUE;
57         uth_mutex_unlock(args->mtx);
58         /* We can actually signal outside the mutex if we want.  Remember the
59          * invariant: whenever the flag is set from FALSE to TRUE, all waiters that
60          * saw FALSE are on the CV's waitqueue.  That's true even after we unlock
61          * the mutex. */
62         uth_cond_var_broadcast(args->cv);
63         return PTH_TEST_TRUE;
64 }
65
66 void *__cv_waiter(void *arg)
67 {
68         struct common_args *args = (struct common_args*)arg;
69
70         uthread_usleep(args->wait_sleep);
71         uth_mutex_lock(args->mtx);
72         while (!args->flag)
73                 uth_cond_var_wait(args->cv, args->mtx);
74         UT_ASSERT(args->flag);
75         uth_mutex_unlock(args->mtx);
76         return PTH_TEST_TRUE;
77 }
78
79 /* Basic one signaller, one receiver.  We want to vary the amount of time the
80  * sender and receiver delays, starting with (1ms, 0ms) and ending with (0ms,
81  * 1ms).  At each extreme, such as with the sender waiting 1ms, the
82  * receiver/waiter should hit the "check and wait" point well before the
83  * sender/signaller hits the "change state and signal" point. */
84 bool test_signal(void)
85 {
86         struct common_args local_a, *args = &local_a;
87         pthread_t signaller, waiter;
88         void *sig_join, *wait_join;
89         int ret;
90
91         args->cv = uth_cond_var_alloc();
92         args->mtx = uth_mutex_alloc();
93
94         for (int i = 0; i < 1000; i += 10) {
95                 args->flag = FALSE;
96                 args->wait_sleep = i;
97                 args->sig_sleep = 1000 - i;
98
99                 ret = pthread_create(&waiter, 0, __cv_waiter, args);
100                 UT_ASSERT(!ret);
101                 ret = pthread_create(&signaller, 0, __cv_signaller, args);
102                 UT_ASSERT(!ret);
103                 ret = pthread_join(waiter, &wait_join);
104                 UT_ASSERT(!ret);
105                 ret = pthread_join(signaller, &sig_join);
106                 UT_ASSERT(!ret);
107                 UT_ASSERT_M("Waiter Failed", wait_join == PTH_TEST_TRUE);
108                 UT_ASSERT_M("Signaller Failed", sig_join == PTH_TEST_TRUE);
109         }
110
111         uth_cond_var_free(args->cv);
112         uth_mutex_free(args->mtx);
113         return TRUE;
114 }
115
116 bool test_broadcast(void)
117 {
118         #define NR_WAITERS 20
119         struct common_args local_a, *args = &local_a;
120         pthread_t bcaster, waiters[NR_WAITERS];
121         void *bcast_join, *wait_joins[NR_WAITERS];
122         int ret;
123
124         args->cv = uth_cond_var_alloc();
125         args->mtx = uth_mutex_alloc();
126         args->flag = FALSE;
127         args->wait_sleep = 0;
128         args->sig_sleep = 1000;
129
130         for (int i = 0; i < NR_WAITERS; i++) {
131                 ret = pthread_create(&waiters[i], 0, __cv_waiter, args);
132                 UT_ASSERT(!ret);
133         }
134         ret = pthread_create(&bcaster, 0, __cv_broadcaster, args);
135         UT_ASSERT(!ret);
136
137         ret = pthread_join(bcaster, &bcast_join);
138         UT_ASSERT(!ret);
139         UT_ASSERT_M("Broadcaster Failed", bcast_join == PTH_TEST_TRUE);
140         for (int i = 0; i < NR_WAITERS; i++) {
141                 ret = pthread_join(waiters[i], &wait_joins[i]);
142                 UT_ASSERT(!ret);
143                 UT_ASSERT_M("Waiter Failed", wait_joins[i] == PTH_TEST_TRUE);
144         }
145
146         uth_cond_var_free(args->cv);
147         uth_mutex_free(args->mtx);
148         return TRUE;
149 }
150 /* <--- End definition of test cases ---> */
151
152 struct utest utests[] = {
153         UTEST_REG(signal_no_wait),
154         UTEST_REG(signal),
155         UTEST_REG(broadcast),
156 };
157 int num_utests = sizeof(utests) / sizeof(struct utest);
158
159 int main(int argc, char *argv[])
160 {
161         // Run test suite passing it all the args as whitelist of what tests to run.
162         char **whitelist = &argv[1];
163         int whitelist_len = argc - 1;
164
165         RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len);
166 }