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