1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <stdio.h>
14#include <sync/sem.h>
15#include <sync/bin_sem.h>
16#include <sync/condition_var.h>
17#include <stdbool.h>
18
19#include "../test.h"
20#include "../helpers.h"
21
22static volatile int shared = 0;
23sync_bin_sem_t bin_sem = {{0}};
24
25sync_sem_t sem = {{0}};
26
27sync_bin_sem_t monitor_lock = {{0}};
28sync_cv_t consumer_cv = {{0}};
29sync_cv_t producer_cv = {{0}};
30sync_cv_t broadcaster_cv = {{0}};
31
32static int bin_sem_func(env_t env, int threadid)
33{
34    /* Take the semaphore */
35    sync_bin_sem_wait(&bin_sem);
36
37    /* Grab the value of the shared variable */
38    int value = shared;
39
40    /* Yield to give the other thread a chance to test the semaphore */
41    seL4_Yield();
42
43    /* Write back to the shared variable */
44    value += 1;
45    shared = value;
46
47    /* Signal the semaphore */
48    sync_bin_sem_post(&bin_sem);
49
50    return 0;
51}
52
53static int test_bin_sem(struct env *env)
54{
55    int error;
56    helper_thread_t thread1, thread2;
57
58    /* Create a binary semaphore */
59    error = sync_bin_sem_new(&(env->vka), &bin_sem, 1);
60    test_eq(error, 0);
61
62    /* Reset the shared variable */
63    shared = 0;
64
65    /* Create some threads that need mutual exclusion */
66    create_helper_thread(env, &thread1);
67    create_helper_thread(env, &thread2);
68    start_helper(env, &thread1, (helper_fn_t) bin_sem_func, (seL4_Word) env, 1, 0, 0);
69    start_helper(env, &thread2, (helper_fn_t) bin_sem_func, (seL4_Word) env, 2, 0, 0);
70
71    /* Wait for them to do their thing */
72    wait_for_helper(&thread1);
73    wait_for_helper(&thread2);
74    cleanup_helper(env, &thread1);
75    cleanup_helper(env, &thread2);
76
77    /* If the semaphore worked then this will be 2 */
78    test_eq(shared, 2);
79
80    /* Clean up */
81    sync_bin_sem_destroy(&env->vka, &bin_sem);
82
83    return sel4test_get_result();
84}
85DEFINE_TEST(SYNC001, "libsel4sync Test binary semaphores", test_bin_sem, true)
86
87static int
88sem_func(env_t env, int threadid)
89{
90    /* Take the semaphore */
91    sync_sem_wait(&sem);
92
93    /* Grab the value of the shared variable */
94    int value = shared;
95
96    /* Yield to give the other thread a chance to test the semaphore */
97    seL4_Yield();
98
99    /* Write back to the shared variable */
100    value += 1;
101    shared = value;
102
103    /* Signal the semaphore */
104    sync_sem_post(&sem);
105
106    return 0;
107}
108
109static int test_sem(struct env *env)
110{
111    int error;
112    helper_thread_t thread1, thread2;
113
114    /* Reset the shared variable */
115    shared = 0;
116
117    /* Create a binary semaphore */
118    error = sync_sem_new(&(env->vka), &sem, 1);
119    test_eq(error, 0);
120
121    /* Create some threads that need mutual exclusion */
122    create_helper_thread(env, &thread1);
123    create_helper_thread(env, &thread2);
124    start_helper(env, &thread1, (helper_fn_t) sem_func, (seL4_Word) env, 1, 0, 0);
125    start_helper(env, &thread2, (helper_fn_t) sem_func, (seL4_Word) env, 2, 0, 0);
126
127    /* Wait for them to do their thing */
128    wait_for_helper(&thread1);
129    wait_for_helper(&thread2);
130    cleanup_helper(env, &thread1);
131    cleanup_helper(env, &thread2);
132
133    /* If the semaphore worked then this will be 2 */
134    test_eq(shared, 2);
135
136    /* Clean up */
137    sync_sem_destroy(&(env->vka), &sem);
138
139    return sel4test_get_result();
140}
141DEFINE_TEST(SYNC002, "libsel4sync Test semaphores", test_sem, true)
142
143static int
144consumer_func(env_t env, int threadid)
145{
146    /* Take the monitor */
147    sync_bin_sem_wait(&monitor_lock);
148
149    /* Wait for a condition to be true */
150    while (shared == 0) {
151        sync_cv_wait(&monitor_lock, &consumer_cv);
152    }
153
154    /* Grab the value of the shared variable */
155    int value = shared;
156
157    seL4_Yield();
158
159    /* Write back to the shared variable */
160    value -= 1;
161    shared = value;
162
163    /* Signal the condition variable */
164    sync_cv_signal(&producer_cv);
165
166    /* Release the monitor */
167    sync_bin_sem_post(&monitor_lock);
168
169    return 0;
170}
171
172static int producer_func(env_t env, int threadid)
173{
174    /* Take the monitor */
175    sync_bin_sem_wait(&monitor_lock);
176
177    /* Wait for a condition to be true */
178    while (shared == 2) {
179        sync_cv_wait(&monitor_lock, &producer_cv);
180    }
181
182    /* Grab the value of the shared variable */
183    int value = shared;
184
185    seL4_Yield();
186
187    /* Write back to the shared variable */
188    value += 1;
189    shared = value;
190
191    /* Signal the condition variable */
192    sync_cv_signal(&consumer_cv);
193
194    /* Release the monitor */
195    sync_bin_sem_post(&monitor_lock);
196
197    return 0;
198}
199
200static int test_monitor(struct env *env)
201{
202    int error;
203    helper_thread_t consumer_thread1, consumer_thread2;
204    helper_thread_t producer_thread1, producer_thread2;
205
206    /* Create a lock for the monitor */
207    error = sync_bin_sem_new(&(env->vka), &monitor_lock, 1);
208    test_eq(error, 0);
209
210    /* Create a condition variable for consumers */
211    error = sync_cv_new(&(env->vka), &consumer_cv);
212    test_eq(error, 0);
213
214    /* Create a condition variable for producers */
215    error = sync_cv_new(&(env->vka), &producer_cv);
216    test_eq(error, 0);
217
218    /* Create the producer/consumer threads */
219    create_helper_thread(env, &consumer_thread1);
220    create_helper_thread(env, &consumer_thread2);
221    create_helper_thread(env, &producer_thread1);
222    create_helper_thread(env, &producer_thread2);
223
224    shared = 1;
225
226    start_helper(env, &consumer_thread1, (helper_fn_t) consumer_func, (seL4_Word) env, 1, 0, 0);
227    start_helper(env, &consumer_thread2, (helper_fn_t) consumer_func, (seL4_Word) env, 2, 0, 0);
228    start_helper(env, &producer_thread1, (helper_fn_t) producer_func, (seL4_Word) env, 3, 0, 0);
229    start_helper(env, &producer_thread2, (helper_fn_t) producer_func, (seL4_Word) env, 4, 0, 0);
230
231    /* Wait for them to do their thing */
232    wait_for_helper(&consumer_thread1);
233    wait_for_helper(&consumer_thread2);
234    wait_for_helper(&producer_thread1);
235    wait_for_helper(&producer_thread2);
236
237    cleanup_helper(env, &consumer_thread1);
238    cleanup_helper(env, &consumer_thread2);
239    cleanup_helper(env, &producer_thread1);
240    cleanup_helper(env, &producer_thread2);
241
242    /* If the monitor worked then this will be 1 */
243    test_eq(shared, 1);
244
245    /* Clean up */
246    sync_cv_destroy(&(env->vka), &consumer_cv);
247    sync_cv_destroy(&(env->vka), &producer_cv);
248    sync_bin_sem_destroy(&(env->vka), &monitor_lock);
249
250    return sel4test_get_result();
251}
252DEFINE_TEST(SYNC003, "libsel4sync Test monitors", test_monitor, true)
253
254static int
255broadcaster_func(env_t env, int threadid)
256{
257    /* Take the monitor */
258    sync_bin_sem_wait(&monitor_lock);
259
260    /* Wait for a condition to be true */
261    while (shared == 2) {
262        sync_cv_wait(&monitor_lock, &broadcaster_cv);
263    }
264
265    /* Grab the value of the shared variable */
266    int value = shared;
267
268    seL4_Yield();
269
270    /* Write back to the shared variable */
271    value += 3;
272    shared = value;
273
274    /* Signal the condition variable */
275    sync_cv_broadcast_release(&monitor_lock, &consumer_cv);
276
277    return 0;
278}
279
280static int test_monitor_broadcast(struct env *env)
281{
282    int error;
283    helper_thread_t broadcaster_thread;
284    helper_thread_t consumer_thread1, consumer_thread2, consumer_thread3;
285
286    /* Create a lock for the monitor */
287    error = sync_bin_sem_new(&(env->vka), &monitor_lock, 1);
288    test_eq(error, 0);
289
290    /* Create a condition variable for consumers */
291    error = sync_cv_new(&(env->vka), &consumer_cv);
292    test_eq(error, 0);
293
294    /* Create a condition variable for the broadcaster */
295    error = sync_cv_new(&(env->vka), &broadcaster_cv);
296    test_eq(error, 0);
297
298    /* Create the broadcaster/consumer threads */
299    create_helper_thread(env, &consumer_thread1);
300    create_helper_thread(env, &consumer_thread2);
301    create_helper_thread(env, &consumer_thread3);
302    create_helper_thread(env, &broadcaster_thread);
303
304    shared = 0;
305
306    start_helper(env, &broadcaster_thread, (helper_fn_t) broadcaster_func, (seL4_Word) env, 1, 0, 0);
307    start_helper(env, &consumer_thread1, (helper_fn_t) consumer_func, (seL4_Word) env, 2, 0, 0);
308    start_helper(env, &consumer_thread2, (helper_fn_t) consumer_func, (seL4_Word) env, 3, 0, 0);
309    start_helper(env, &consumer_thread3, (helper_fn_t) consumer_func, (seL4_Word) env, 4, 0, 0);
310
311    /* Wait for them to do their thing */
312    wait_for_helper(&broadcaster_thread);
313    wait_for_helper(&consumer_thread1);
314    wait_for_helper(&consumer_thread2);
315    wait_for_helper(&consumer_thread3);
316
317    cleanup_helper(env, &consumer_thread1);
318    cleanup_helper(env, &consumer_thread2);
319    cleanup_helper(env, &consumer_thread3);
320    cleanup_helper(env, &broadcaster_thread);
321
322    /* If the monitor worked then this will be 0 */
323    test_eq(shared, 0);
324
325    /* Check that the broadcast is over */
326    test_assert(!consumer_cv.broadcasting);
327
328    /* Clean up */
329    sync_cv_destroy(&(env->vka), &consumer_cv);
330    sync_cv_destroy(&(env->vka), &broadcaster_cv);
331    sync_bin_sem_destroy(&(env->vka), &monitor_lock);
332
333    return sel4test_get_result();
334}
335DEFINE_TEST(SYNC004, "libsel4sync Test monitors - broadcast", test_monitor_broadcast, true)
336