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