1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <pthread.h>
9 #include <time.h>
10
11 #include <zephyr/sys/util.h>
12 #include <zephyr/ztest.h>
13
14 #define SLEEP_MS 100
15
16 static pthread_mutex_t mutex;
17
normal_mutex_entry(void * p1)18 static void *normal_mutex_entry(void *p1)
19 {
20 int i, rc;
21
22 /* Sleep for maximum 300 ms as main thread is sleeping for 100 ms */
23
24 for (i = 0; i < 3; i++) {
25 rc = pthread_mutex_trylock(&mutex);
26 if (rc == 0) {
27 break;
28 }
29 k_msleep(SLEEP_MS);
30 }
31
32 zassert_false(rc, "try lock failed");
33 TC_PRINT("mutex lock is taken\n");
34 zassert_false(pthread_mutex_unlock(&mutex), "mutex unlock is failed");
35 return NULL;
36 }
37
recursive_mutex_entry(void * p1)38 static void *recursive_mutex_entry(void *p1)
39 {
40 zassert_false(pthread_mutex_lock(&mutex), "mutex is not taken");
41 zassert_false(pthread_mutex_lock(&mutex), "mutex is not taken 2nd time");
42 TC_PRINT("recursive mutex lock is taken\n");
43 zassert_false(pthread_mutex_unlock(&mutex), "mutex is not unlocked");
44 zassert_false(pthread_mutex_unlock(&mutex), "mutex is not unlocked");
45 return NULL;
46 }
47
test_mutex_common(int type,void * (* entry)(void * arg))48 static void test_mutex_common(int type, void *(*entry)(void *arg))
49 {
50 pthread_t th;
51 int protocol;
52 int actual_type;
53 pthread_mutexattr_t mut_attr;
54 struct sched_param schedparam;
55
56 schedparam.sched_priority = 2;
57
58 zassert_ok(pthread_mutexattr_init(&mut_attr));
59 zassert_ok(pthread_mutexattr_settype(&mut_attr, type), "setting mutex type is failed");
60 zassert_ok(pthread_mutex_init(&mutex, &mut_attr), "mutex initialization is failed");
61
62 zassert_ok(pthread_mutexattr_gettype(&mut_attr, &actual_type),
63 "reading mutex type is failed");
64 zassert_not_ok(pthread_mutexattr_getprotocol(NULL, &protocol));
65 zassert_not_ok(pthread_mutexattr_getprotocol(&mut_attr, NULL));
66 zassert_not_ok(pthread_mutexattr_getprotocol(NULL, NULL));
67
68 zassert_not_ok(pthread_mutexattr_setprotocol(&mut_attr, PTHREAD_PRIO_INHERIT));
69 zassert_not_ok(pthread_mutexattr_setprotocol(&mut_attr, PTHREAD_PRIO_PROTECT));
70 zassert_ok(pthread_mutexattr_setprotocol(&mut_attr, PTHREAD_PRIO_NONE));
71 zassert_ok(pthread_mutexattr_getprotocol(&mut_attr, &protocol),
72 "reading mutex protocol is failed");
73 zassert_ok(pthread_mutexattr_destroy(&mut_attr));
74
75 zassert_ok(pthread_mutex_lock(&mutex));
76
77 zassert_equal(actual_type, type, "mutex type is not normal");
78 zassert_equal(protocol, PTHREAD_PRIO_NONE, "mutex protocol is not prio_none");
79
80 zassert_ok(pthread_create(&th, NULL, entry, NULL));
81
82 k_msleep(SLEEP_MS);
83 zassert_ok(pthread_mutex_unlock(&mutex));
84
85 zassert_ok(pthread_join(th, NULL));
86 zassert_ok(pthread_mutex_destroy(&mutex), "Destroying mutex is failed");
87 }
88
ZTEST(mutex,test_mutex_prioceiling_stubs)89 ZTEST(mutex, test_mutex_prioceiling_stubs)
90 {
91 #ifdef CONFIG_POSIX_THREAD_PRIO_PROTECT
92 zassert_equal(pthread_mutex_getprioceiling(NULL, NULL), ENOSYS);
93 zassert_equal(pthread_mutex_setprioceiling(NULL, 0, NULL), ENOSYS);
94 zassert_equal(pthread_mutexattr_getprioceiling(NULL, NULL), ENOSYS);
95 zassert_equal(pthread_mutexattr_setprioceiling(NULL, 0), ENOSYS);
96 #else
97 ztest_test_skip();
98 #endif /* CONFIG_POSIX_THREAD_PRIO_PROTECT */
99 }
100
101 /**
102 * @brief Test to demonstrate PTHREAD_MUTEX_NORMAL
103 *
104 * @details Mutex type is setup as normal. pthread_mutex_trylock
105 * and pthread_mutex_lock are tested with mutex type being
106 * normal.
107 */
ZTEST(mutex,test_mutex_normal)108 ZTEST(mutex, test_mutex_normal)
109 {
110 test_mutex_common(PTHREAD_MUTEX_NORMAL, normal_mutex_entry);
111 }
112
113 /**
114 * @brief Test to demonstrate PTHREAD_MUTEX_RECURSIVE
115 *
116 * @details Mutex type is setup as recursive. mutex will be locked
117 * twice and unlocked for the same number of time.
118 *
119 */
ZTEST(mutex,test_mutex_recursive)120 ZTEST(mutex, test_mutex_recursive)
121 {
122 test_mutex_common(PTHREAD_MUTEX_RECURSIVE, recursive_mutex_entry);
123 }
124
125 /**
126 * @brief Test to demonstrate limited mutex resources
127 *
128 * @details Exactly CONFIG_MAX_PTHREAD_MUTEX_COUNT can be in use at once.
129 */
ZTEST(mutex,test_mutex_resource_exhausted)130 ZTEST(mutex, test_mutex_resource_exhausted)
131 {
132 size_t i;
133 pthread_mutex_t m[CONFIG_MAX_PTHREAD_MUTEX_COUNT + 1];
134
135 for (i = 0; i < CONFIG_MAX_PTHREAD_MUTEX_COUNT; ++i) {
136 zassert_ok(pthread_mutex_init(&m[i], NULL), "failed to init mutex %zu", i);
137 }
138
139 /* try to initialize one more than CONFIG_MAX_PTHREAD_MUTEX_COUNT */
140 zassert_equal(i, CONFIG_MAX_PTHREAD_MUTEX_COUNT);
141 zassert_not_equal(0, pthread_mutex_init(&m[i], NULL),
142 "should not have initialized mutex %zu", i);
143
144 for (; i > 0; --i) {
145 zassert_ok(pthread_mutex_destroy(&m[i - 1]), "failed to destroy mutex %zu", i - 1);
146 }
147 }
148
149 /**
150 * @brief Test to that there are no mutex resource leaks
151 *
152 * @details Demonstrate that mutexes may be used over and over again.
153 */
ZTEST(mutex,test_mutex_resource_leak)154 ZTEST(mutex, test_mutex_resource_leak)
155 {
156 pthread_mutex_t m;
157
158 for (size_t i = 0; i < 2 * CONFIG_MAX_PTHREAD_MUTEX_COUNT; ++i) {
159 zassert_ok(pthread_mutex_init(&m, NULL), "failed to init mutex %zu", i);
160 zassert_ok(pthread_mutex_destroy(&m), "failed to destroy mutex %zu", i);
161 }
162 }
163
164 #define TIMEDLOCK_TIMEOUT_MS 200
165 #define TIMEDLOCK_TIMEOUT_DELAY_MS 100
166
167 BUILD_ASSERT(TIMEDLOCK_TIMEOUT_DELAY_MS >= 100, "TIMEDLOCK_TIMEOUT_DELAY_MS too small");
168 BUILD_ASSERT(TIMEDLOCK_TIMEOUT_MS >= 2 * TIMEDLOCK_TIMEOUT_DELAY_MS,
169 "TIMEDLOCK_TIMEOUT_MS too small");
170
timespec_add_ms(struct timespec * ts,uint32_t ms)171 static void timespec_add_ms(struct timespec *ts, uint32_t ms)
172 {
173 bool oflow;
174
175 ts->tv_nsec += ms * NSEC_PER_MSEC;
176 oflow = ts->tv_nsec >= NSEC_PER_SEC;
177 ts->tv_sec += oflow;
178 ts->tv_nsec -= oflow * NSEC_PER_SEC;
179 }
180
test_mutex_timedlock_fn(void * arg)181 static void *test_mutex_timedlock_fn(void *arg)
182 {
183 struct timespec time_point;
184 pthread_mutex_t *mtx = (pthread_mutex_t *)arg;
185
186 zassume_ok(clock_gettime(CLOCK_REALTIME, &time_point));
187 timespec_add_ms(&time_point, TIMEDLOCK_TIMEOUT_MS);
188
189 return INT_TO_POINTER(pthread_mutex_timedlock(mtx, &time_point));
190 }
191
192 /** @brief Test to verify @ref pthread_mutex_timedlock returns ETIMEDOUT */
ZTEST(mutex,test_mutex_timedlock)193 ZTEST(mutex, test_mutex_timedlock)
194 {
195 void *ret;
196 pthread_t th;
197
198 zassert_ok(pthread_mutex_init(&mutex, NULL));
199
200 printk("Expecting timedlock with timeout of %d ms to fail\n", TIMEDLOCK_TIMEOUT_MS);
201 zassert_ok(pthread_mutex_lock(&mutex));
202 zassert_ok(pthread_create(&th, NULL, test_mutex_timedlock_fn, &mutex));
203 zassert_ok(pthread_join(th, &ret));
204 /* ensure timeout occurs */
205 zassert_equal(ETIMEDOUT, POINTER_TO_INT(ret));
206
207 printk("Expecting timedlock with timeout of %d ms to succeed after 100ms\n",
208 TIMEDLOCK_TIMEOUT_MS);
209 zassert_ok(pthread_create(&th, NULL, test_mutex_timedlock_fn, &mutex));
210 /* unlock before timeout expires */
211 k_msleep(TIMEDLOCK_TIMEOUT_DELAY_MS);
212 zassert_ok(pthread_mutex_unlock(&mutex));
213 zassert_ok(pthread_join(th, &ret));
214 /* ensure lock is successful, in spite of delay */
215 zassert_ok(POINTER_TO_INT(ret));
216
217 zassert_ok(pthread_mutex_destroy(&mutex));
218 }
219
before(void * arg)220 static void before(void *arg)
221 {
222 ARG_UNUSED(arg);
223
224 if (!IS_ENABLED(CONFIG_DYNAMIC_THREAD)) {
225 /* skip redundant testing if there is no thread pool / heap allocation */
226 ztest_test_skip();
227 }
228 }
229
230 ZTEST_SUITE(mutex, NULL, NULL, before, NULL, NULL);
231