1 /*
2 * Copyright (c) 2023, Meta
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "thrd.h"
8
9 #include <stdint.h>
10 #include <threads.h>
11
12 #include <zephyr/ztest.h>
13
14 #define WAIT_TIME_MS 100
15
16 static struct libc_cnd_fixture {
17 /* shared between threads in tests */
18 cnd_t cond;
19 mtx_t mutex;
20
21 /* de-duplicate local variables in test cases */
22 int res1;
23 int res2;
24 thrd_t thrd1;
25 thrd_t thrd2;
26 bool do_timedwait;
27 bool is_broadcast;
28 struct timespec time_point;
29 } _libc_cnd_fixture;
30
ZTEST_F(libc_cnd,test_cnd_init_destroy)31 ZTEST_F(libc_cnd, test_cnd_init_destroy)
32 {
33 /* degenerate cases */
34 if (false) {
35 /* pthread_cond_init() pthread_cond_destroy() are not hardened against these */
36 zassert_equal(thrd_error, cnd_init(NULL));
37 zassert_equal(thrd_error, cnd_init((cnd_t *)BIOS_FOOD));
38 cnd_destroy(NULL);
39 cnd_destroy((cnd_t *)BIOS_FOOD);
40 }
41
42 /* happy path tested in before() / after() */
43 }
44
ZTEST_F(libc_cnd,test_cnd_errors)45 ZTEST_F(libc_cnd, test_cnd_errors)
46 {
47 /* degenerate test cases */
48 if (false) {
49 /* pthread_cond_*() are not hardened against these */
50 zassert_equal(thrd_error, cnd_signal(NULL));
51 zassert_equal(thrd_error, cnd_broadcast(NULL));
52 zassert_equal(thrd_error, cnd_wait(NULL, NULL));
53 zassert_equal(thrd_error, cnd_wait(NULL, &fixture->mutex));
54 zassert_equal(thrd_error, cnd_wait(&fixture->cond, NULL));
55 zassert_equal(thrd_error, cnd_timedwait(NULL, NULL, NULL));
56 zassert_equal(thrd_error, cnd_timedwait(NULL, NULL, &fixture->time_point));
57 zassert_equal(thrd_error, cnd_timedwait(NULL, &fixture->mutex, NULL));
58 zassert_equal(thrd_error,
59 cnd_timedwait(NULL, &fixture->mutex, &fixture->time_point));
60 zassert_equal(thrd_error, cnd_timedwait(&fixture->cond, NULL, NULL));
61 zassert_equal(thrd_error,
62 cnd_timedwait(&fixture->cond, NULL, &fixture->time_point));
63 zassert_equal(thrd_error, cnd_timedwait(&fixture->cond, &fixture->mutex, NULL));
64 }
65 }
66
test_cnd_thread_fn(void * arg)67 static int test_cnd_thread_fn(void *arg)
68 {
69 int res = thrd_success;
70 struct timespec time_point;
71 struct libc_cnd_fixture *const fixture = arg;
72
73 if (fixture->do_timedwait) {
74 zassume_ok(clock_gettime(CLOCK_MONOTONIC, &time_point));
75 timespec_add_ms(&time_point, WAIT_TIME_MS);
76 res = cnd_timedwait(&fixture->cond, &fixture->mutex, &time_point);
77 } else {
78 res = cnd_wait(&fixture->cond, &fixture->mutex);
79 }
80
81 if (fixture->is_broadcast) {
82 /* re-signal so that the next thread wakes up too */
83 zassert_equal(thrd_success, cnd_signal(&fixture->cond));
84 }
85
86 (void)mtx_unlock(&fixture->mutex);
87
88 return res;
89 }
90
tst_cnd_common(struct libc_cnd_fixture * fixture,size_t wait_ms,bool th2,int exp1,int exp2)91 static void tst_cnd_common(struct libc_cnd_fixture *fixture, size_t wait_ms, bool th2, int exp1,
92 int exp2)
93 {
94 zassert_equal(thrd_success, mtx_lock(&fixture->mutex));
95
96 zassert_equal(thrd_success, thrd_create(&fixture->thrd1, test_cnd_thread_fn, fixture));
97 if (th2) {
98 zassert_equal(thrd_success,
99 thrd_create(&fixture->thrd2, test_cnd_thread_fn, fixture));
100 }
101
102 k_msleep(wait_ms);
103
104 if (fixture->is_broadcast) {
105 zassert_equal(thrd_success, cnd_broadcast(&fixture->cond));
106 } else {
107 zassert_equal(thrd_success, cnd_signal(&fixture->cond));
108 }
109
110 zassert_equal(thrd_success, mtx_unlock(&fixture->mutex));
111
112 zassert_equal(thrd_success, thrd_join(fixture->thrd1, &fixture->res1));
113 if (th2) {
114 zassert_equal(thrd_success, thrd_join(fixture->thrd2, &fixture->res2));
115 }
116
117 zassert_equal(exp1, fixture->res1);
118 if (th2) {
119 zassert_equal(exp2, fixture->res2);
120 }
121 }
122
ZTEST_F(libc_cnd,test_cnd_signal_wait)123 ZTEST_F(libc_cnd, test_cnd_signal_wait)
124 {
125 tst_cnd_common(fixture, WAIT_TIME_MS / 2, false, thrd_success, DONT_CARE);
126 }
127
ZTEST_F(libc_cnd,test_cnd_signal_timedwait)128 ZTEST_F(libc_cnd, test_cnd_signal_timedwait)
129 {
130 fixture->do_timedwait = true;
131 tst_cnd_common(fixture, WAIT_TIME_MS / 2, false, thrd_success, DONT_CARE);
132 }
133
ZTEST_F(libc_cnd,test_cnd_timedwait_timeout)134 ZTEST_F(libc_cnd, test_cnd_timedwait_timeout)
135 {
136 fixture->do_timedwait = true;
137 tst_cnd_common(fixture, WAIT_TIME_MS * 2, false, thrd_timedout, DONT_CARE);
138 }
139
ZTEST_F(libc_cnd,test_cnd_broadcast_wait)140 ZTEST_F(libc_cnd, test_cnd_broadcast_wait)
141 {
142 fixture->is_broadcast = true;
143 tst_cnd_common(fixture, WAIT_TIME_MS, true, thrd_success, thrd_success);
144 }
145
setup(void)146 static void *setup(void)
147 {
148 return &_libc_cnd_fixture;
149 }
150
before(void * arg)151 static void before(void *arg)
152 {
153 struct libc_cnd_fixture *const fixture = arg;
154
155 *fixture = (struct libc_cnd_fixture){
156 .res1 = FORTY_TWO,
157 .res2 = SEVENTY_THREE,
158 };
159
160 zassert_equal(thrd_success, mtx_init(&fixture->mutex, mtx_plain));
161 zassert_equal(thrd_success, cnd_init(&fixture->cond));
162 }
163
after(void * arg)164 static void after(void *arg)
165 {
166 struct libc_cnd_fixture *const fixture = arg;
167
168 cnd_destroy(&fixture->cond);
169 mtx_destroy(&fixture->mutex);
170 }
171
172 ZTEST_SUITE(libc_cnd, NULL, setup, before, after, NULL);
173