6a159f80f45a6799207b9c3301086ef731d775a4
[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
152 static bool __test_recurse(struct uth_recurse_mutex *r_mtx)
153 {
154         bool test;
155
156         uth_recurse_mutex_lock(r_mtx);
157         /* should be one lock deep */
158         UT_ASSERT(r_mtx->count == 1);
159         UT_ASSERT(r_mtx->lockholder == current_uthread);
160
161         test = uth_recurse_mutex_trylock(r_mtx);
162         UT_ASSERT_M("Failed to re(try)lock our owned mutex!", test);
163         /* should be two locks deep */
164         UT_ASSERT(r_mtx->count == 2);
165         UT_ASSERT(r_mtx->lockholder == current_uthread);
166
167         uth_recurse_mutex_lock(r_mtx);
168         /* should be three locks deep */
169         UT_ASSERT(r_mtx->count == 3);
170         UT_ASSERT(r_mtx->lockholder == current_uthread);
171
172         uth_recurse_mutex_unlock(r_mtx);
173         /* should be two locks deep */
174         UT_ASSERT(r_mtx->count == 2);
175         UT_ASSERT(r_mtx->lockholder == current_uthread);
176
177         uth_recurse_mutex_unlock(r_mtx);
178         /* should be one lock deep */
179         UT_ASSERT(r_mtx->count == 1);
180         UT_ASSERT(r_mtx->lockholder == current_uthread);
181
182         uth_recurse_mutex_unlock(r_mtx);
183         /* should be unlocked */
184         UT_ASSERT(r_mtx->count == 0);
185         UT_ASSERT(!r_mtx->lockholder);
186
187         return TRUE;
188 }
189
190 bool test_recurse(void)
191 {
192         uth_recurse_mutex_t *r_mtx;
193
194         r_mtx = uth_recurse_mutex_alloc();
195         UT_ASSERT(r_mtx);
196         if (!__test_recurse(r_mtx))
197                 return FALSE;
198         uth_recurse_mutex_free(r_mtx);
199
200         return TRUE;
201 }
202
203 bool test_recurse_static(void)
204 {
205         static uth_recurse_mutex_t static_recurse_mtx = UTH_RECURSE_MUTEX_INIT;
206
207         return __test_recurse(&static_recurse_mtx);
208 }
209
210 static bool __test_semaphore(struct uth_semaphore *sem, int count)
211 {
212         bool can_down;
213
214         UT_ASSERT(count > 2);   /* for our own use */
215         UT_ASSERT(sem->count == count);
216         /* should be able to down it count times without blocking */
217         for (int i = 0; i < count; i++) {
218                 uth_semaphore_down(sem);
219                 UT_ASSERT(sem->count == count - (i + 1));
220         }
221
222         /* shouldn't be able to down, since we grabbed them all */
223         can_down = uth_semaphore_trydown(sem);
224         UT_ASSERT(!can_down);
225         UT_ASSERT(sem->count == 0);
226
227         uth_semaphore_up(sem);
228         UT_ASSERT(sem->count == 1);
229         can_down = uth_semaphore_trydown(sem);
230         UT_ASSERT(can_down);
231         UT_ASSERT(sem->count == 0);
232
233         for (int i = 0; i < count; i++) {
234                 uth_semaphore_up(sem);
235                 UT_ASSERT(sem->count == i + 1);
236         }
237
238         return TRUE;
239 }
240
241 bool test_semaphore(void)
242 {
243         uth_semaphore_t *sem;
244
245         sem = uth_semaphore_alloc(5);
246         UT_ASSERT(sem);
247         if (!__test_semaphore(sem, 5))
248                 return FALSE;
249         uth_semaphore_free(sem);
250
251         return TRUE;
252 }
253
254 bool test_semaphore_static(void)
255 {
256         static uth_semaphore_t static_semaphore = UTH_SEMAPHORE_INIT(5);
257
258         return __test_semaphore(&static_semaphore, 5);
259 }
260
261 /* <--- End definition of test cases ---> */
262
263 struct utest utests[] = {
264         UTEST_REG(signal_no_wait),
265         UTEST_REG(signal),
266         UTEST_REG(broadcast),
267         UTEST_REG(recurse),
268         UTEST_REG(recurse_static),
269         UTEST_REG(semaphore),
270         UTEST_REG(semaphore_static),
271 };
272 int num_utests = sizeof(utests) / sizeof(struct utest);
273
274 int main(int argc, char *argv[])
275 {
276         // Run test suite passing it all the args as whitelist of what tests to run.
277         char **whitelist = &argv[1];
278         int whitelist_len = argc - 1;
279
280         RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len);
281 }