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/sys_clock.h>
13 #include <zephyr/ztest.h>
14
15 static const int valid_mtx_types[] = {
16 mtx_plain,
17 mtx_timed,
18 mtx_plain | mtx_recursive,
19 mtx_timed | mtx_recursive,
20 };
21
22 static mtx_t mutex;
23 static thrd_t th;
24
ZTEST(libc_mtx,test_mtx_init)25 ZTEST(libc_mtx, test_mtx_init)
26 {
27 zassert_not_equal(thrd_success, mtx_init(NULL, FORTY_TWO));
28 zassert_not_equal(thrd_success, mtx_init(&mutex, FORTY_TWO));
29
30 if (false) {
31 /* pthread_mutexattr_init() is not hardened against this */
32 zassert_not_equal(thrd_success, mtx_init(NULL, mtx_plain));
33 zassert_not_equal(thrd_success, mtx_init((mtx_t *)BIOS_FOOD, FORTY_TWO));
34 }
35
36 for (size_t i = 0; i < ARRAY_SIZE(valid_mtx_types); ++i) {
37 int type = valid_mtx_types[i];
38
39 zassert_equal(thrd_success, mtx_init(&mutex, type));
40 mtx_destroy(&mutex);
41 }
42 }
43
ZTEST(libc_mtx,test_mtx_destroy)44 ZTEST(libc_mtx, test_mtx_destroy)
45 {
46 if (false) {
47 /* degenerate cases */
48 /* pthread_mutex_destroy() is not hardened against these */
49 mtx_destroy(NULL);
50 mtx_destroy((mtx_t *)BIOS_FOOD);
51 }
52
53 zassert_equal(thrd_success, mtx_init(&mutex, mtx_plain));
54 mtx_destroy(&mutex);
55 }
56
ZTEST(libc_mtx,test_mtx_lock)57 ZTEST(libc_mtx, test_mtx_lock)
58 {
59 if (false) {
60 /* pthread_mutex_lock() is not hardened against this */
61 zassert_not_equal(thrd_success, mtx_lock(NULL));
62 zassert_not_equal(thrd_success, mtx_lock((mtx_t *)BIOS_FOOD));
63 }
64
65 /* test plain mutex */
66 for (size_t i = 0; i < ARRAY_SIZE(valid_mtx_types); ++i) {
67 int type = valid_mtx_types[i];
68
69 zassert_equal(thrd_success, mtx_init(&mutex, type));
70 zassert_equal(thrd_success, mtx_lock(&mutex));
71 if ((type & mtx_recursive) == 0) {
72 if (false) {
73 /* pthread_mutex_lock() is not hardened against this */
74 zassert_not_equal(thrd_success, mtx_lock((&mutex)));
75 }
76 } else {
77 zassert_equal(thrd_success, mtx_lock(&mutex));
78 zassert_equal(thrd_success, mtx_unlock(&mutex));
79 }
80 zassert_equal(thrd_success, mtx_unlock(&mutex));
81 mtx_destroy(&mutex);
82 }
83 }
84
85 #define TIMEDLOCK_TIMEOUT_MS 200
86 #define TIMEDLOCK_TIMEOUT_DELAY_MS 100
87
88 BUILD_ASSERT(TIMEDLOCK_TIMEOUT_DELAY_MS >= 100, "TIMEDLOCK_TIMEOUT_DELAY_MS too small");
89 BUILD_ASSERT(TIMEDLOCK_TIMEOUT_MS >= 2 * TIMEDLOCK_TIMEOUT_DELAY_MS,
90 "TIMEDLOCK_TIMEOUT_MS too small");
91
mtx_timedlock_fn(void * arg)92 static int mtx_timedlock_fn(void *arg)
93 {
94 struct timespec time_point;
95 mtx_t *mtx = (mtx_t *)arg;
96
97 zassume_ok(clock_gettime(CLOCK_MONOTONIC, &time_point));
98 timespec_add_ms(&time_point, TIMEDLOCK_TIMEOUT_MS);
99
100 return mtx_timedlock(mtx, &time_point);
101 }
102
ZTEST(libc_mtx,test_mtx_timedlock)103 ZTEST(libc_mtx, test_mtx_timedlock)
104 {
105 int ret;
106
107 /*
108 * mtx_timed here is technically unnecessary, because all pthreads can
109 * be used for timed locks, but that is sort of peeking into the
110 * implementation
111 */
112 zassert_equal(thrd_success, mtx_init(&mutex, mtx_timed));
113
114 printk("Expecting timedlock with timeout of %d ms to fail\n", TIMEDLOCK_TIMEOUT_MS);
115 zassert_equal(thrd_success, mtx_lock(&mutex));
116 zassert_equal(thrd_success, thrd_create(&th, mtx_timedlock_fn, &mutex));
117 zassert_equal(thrd_success, thrd_join(th, &ret));
118 /* ensure timeout occurs */
119 zassert_equal(thrd_timedout, ret);
120
121 printk("Expecting timedlock with timeout of %d ms to succeed after 100ms\n",
122 TIMEDLOCK_TIMEOUT_MS);
123 zassert_equal(thrd_success, thrd_create(&th, mtx_timedlock_fn, &mutex));
124 /* unlock before timeout expires */
125 k_msleep(TIMEDLOCK_TIMEOUT_DELAY_MS);
126 zassert_equal(thrd_success, mtx_unlock(&mutex));
127 zassert_equal(thrd_success, thrd_join(th, &ret));
128 /* ensure lock is successful, in spite of delay */
129 zassert_equal(thrd_success, ret);
130
131 mtx_destroy(&mutex);
132 }
133
mtx_trylock_fn(void * arg)134 static int mtx_trylock_fn(void *arg)
135 {
136 mtx_t *mtx = (mtx_t *)arg;
137
138 return mtx_trylock(mtx);
139 }
140
ZTEST(libc_mtx,test_mtx_trylock)141 ZTEST(libc_mtx, test_mtx_trylock)
142 {
143 int ret;
144
145 zassert_equal(thrd_success, mtx_init(&mutex, mtx_plain));
146
147 /* ensure trylock fails when lock is held */
148 zassert_equal(thrd_success, mtx_lock(&mutex));
149 zassert_equal(thrd_success, thrd_create(&th, mtx_trylock_fn, &mutex));
150 zassert_equal(thrd_success, thrd_join(th, &ret));
151 /* ensure lock fails */
152 zassert_equal(thrd_busy, ret);
153
154 mtx_destroy(&mutex);
155 }
156
ZTEST(libc_mtx,test_mtx_unlock)157 ZTEST(libc_mtx, test_mtx_unlock)
158 {
159 mtx_t mtx = (mtx_t)BIOS_FOOD;
160
161 /* degenerate case */
162 zassert_not_equal(thrd_success, mtx_unlock(&mtx));
163
164 zassert_equal(thrd_success, mtx_init(&mtx, mtx_plain));
165 zassert_equal(thrd_success, mtx_lock(&mtx));
166 zassert_equal(thrd_success, mtx_unlock(&mtx));
167 mtx_destroy(&mtx);
168 }
169
170 ZTEST_SUITE(libc_mtx, NULL, NULL, NULL, NULL, NULL);
171