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