parlib: Add static initializers for mutexes / 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
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         static uth_mutex_t static_mtx = UTH_MUTEX_INIT;
91         static uth_cond_var_t static_cv = UTH_COND_VAR_INIT;
92
93         /* Also testing the static initializers.  Note we never free these. */
94         args->cv = &static_cv;
95         args->mtx = &static_mtx;
96
97         for (int i = 0; i < 1000; i += 10) {
98                 args->flag = FALSE;
99                 args->wait_sleep = i;
100                 args->sig_sleep = 1000 - i;
101
102                 ret = pthread_create(&waiter, 0, __cv_waiter, args);
103                 UT_ASSERT(!ret);
104                 ret = pthread_create(&signaller, 0, __cv_signaller, args);
105                 UT_ASSERT(!ret);
106                 ret = pthread_join(waiter, &wait_join);
107                 UT_ASSERT(!ret);
108                 ret = pthread_join(signaller, &sig_join);
109                 UT_ASSERT(!ret);
110                 UT_ASSERT_M("Waiter Failed", wait_join == PTH_TEST_TRUE);
111                 UT_ASSERT_M("Signaller Failed", sig_join == PTH_TEST_TRUE);
112         }
113
114         return TRUE;
115 }
116
117 bool test_broadcast(void)
118 {
119         #define NR_WAITERS 20
120         struct common_args local_a, *args = &local_a;
121         pthread_t bcaster, waiters[NR_WAITERS];
122         void *bcast_join, *wait_joins[NR_WAITERS];
123         int ret;
124
125         args->cv = uth_cond_var_alloc();
126         args->mtx = uth_mutex_alloc();
127         args->flag = FALSE;
128         args->wait_sleep = 0;
129         args->sig_sleep = 1000;
130
131         for (int i = 0; i < NR_WAITERS; i++) {
132                 ret = pthread_create(&waiters[i], 0, __cv_waiter, args);
133                 UT_ASSERT(!ret);
134         }
135         ret = pthread_create(&bcaster, 0, __cv_broadcaster, args);
136         UT_ASSERT(!ret);
137
138         ret = pthread_join(bcaster, &bcast_join);
139         UT_ASSERT(!ret);
140         UT_ASSERT_M("Broadcaster Failed", bcast_join == PTH_TEST_TRUE);
141         for (int i = 0; i < NR_WAITERS; i++) {
142                 ret = pthread_join(waiters[i], &wait_joins[i]);
143                 UT_ASSERT(!ret);
144                 UT_ASSERT_M("Waiter Failed", wait_joins[i] == PTH_TEST_TRUE);
145         }
146
147         uth_cond_var_free(args->cv);
148         uth_mutex_free(args->mtx);
149         return TRUE;
150 }
151 /* <--- End definition of test cases ---> */
152
153 struct utest utests[] = {
154         UTEST_REG(signal_no_wait),
155         UTEST_REG(signal),
156         UTEST_REG(broadcast),
157 };
158 int num_utests = sizeof(utests) / sizeof(struct utest);
159
160 int main(int argc, char *argv[])
161 {
162         // Run test suite passing it all the args as whitelist of what tests to run.
163         char **whitelist = &argv[1];
164         int whitelist_len = argc - 1;
165
166         RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len);
167 }