1 /*
2  * Copyright (c) 2021 intel, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr.h>
7 #include <ztest.h>
8 #include <sys/mutex.h>
9 
10 #define HIGH_T1	 0xaaa
11 #define HIGH_T2	 0xbbb
12 #define LOW_PRO	 0xccc
13 
14 #define STACKSIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE)
15 
16 static K_THREAD_STACK_DEFINE(thread_low_stack, STACKSIZE);
17 static struct k_thread thread_low_data;
18 static K_THREAD_STACK_DEFINE(thread_high_stack1, STACKSIZE);
19 static struct k_thread thread_high_data1;
20 static K_THREAD_STACK_DEFINE(thread_high_stack2, STACKSIZE);
21 static struct k_thread thread_high_data2;
22 SYS_MUTEX_DEFINE(mutex);
23 static uint32_t flag[3];
24 
25 /* The order of threads getting mutex is judged by index
26  * self addition.
27  */
28 static uint32_t index;
29 
low_prio_wait_for_mutex(void * p1,void * p2,void * p3)30 static void low_prio_wait_for_mutex(void *p1, void *p2, void *p3)
31 {
32 	struct sys_mutex *pmutex = p1;
33 
34 	sys_mutex_lock(pmutex, K_FOREVER);
35 
36 	flag[index] = LOW_PRO;
37 
38 	index++;
39 	/* Keep mutex for a while */
40 	k_sleep(K_MSEC(10));
41 
42 	sys_mutex_unlock(pmutex);
43 }
44 
high_prio_t1_wait_for_mutex(void * p1,void * p2,void * p3)45 static void high_prio_t1_wait_for_mutex(void *p1, void *p2, void *p3)
46 {
47 	struct sys_mutex *pmutex = p1;
48 
49 	sys_mutex_lock(pmutex, K_FOREVER);
50 
51 	flag[index] = HIGH_T1;
52 
53 	index++;
54 	/* Keep mutex for a while */
55 	k_sleep(K_MSEC(10));
56 
57 	sys_mutex_unlock(pmutex);
58 }
59 
high_prio_t2_wait_for_mutex(void * p1,void * p2,void * p3)60 static void high_prio_t2_wait_for_mutex(void *p1, void *p2, void *p3)
61 {
62 	struct sys_mutex *pmutex = p1;
63 
64 	sys_mutex_lock(pmutex, K_FOREVER);
65 
66 	flag[index] = HIGH_T2;
67 
68 	index++;
69 	/* Keep mutex for a while */
70 	k_sleep(K_MSEC(10));
71 
72 	sys_mutex_unlock(pmutex);
73 }
74 
75 /**
76  * @brief Test multi-threads to take mutex.
77  *
78  * @details Define three threads, and set a higher priority for two of them,
79  * and set a lower priority for the last one. Then Add a delay between
80  * creating the two high priority threads.
81  * Test point:
82  * 1. Any number of threads may wait on a mutex locked by others
83  * simultaneously.
84  * 2. When the mutex is released, it is took by the highest priority
85  * thread that has waited longest.
86  *
87  * @ingroup kernel_mutex_tests
88  */
test_mutex_multithread_competition(void)89 void test_mutex_multithread_competition(void)
90 {
91 	int old_prio = k_thread_priority_get(k_current_get());
92 	int prio = 10;
93 
94 	sys_mutex_lock(&mutex, K_NO_WAIT);
95 
96 	k_thread_priority_set(k_current_get(), prio);
97 
98 	k_thread_create(&thread_high_data1, thread_high_stack1, STACKSIZE,
99 			high_prio_t1_wait_for_mutex,
100 			&mutex, NULL, NULL,
101 			prio + 2, 0, K_NO_WAIT);
102 
103 	/* Thread thread_high_data1 wait more time than thread_high_data2 */
104 	k_sleep(K_MSEC(10));
105 
106 	k_thread_create(&thread_low_data, thread_low_stack, STACKSIZE,
107 			low_prio_wait_for_mutex,
108 			&mutex, NULL, NULL,
109 			prio + 4, 0, K_NO_WAIT);
110 
111 	k_thread_create(&thread_high_data2, thread_high_stack2, STACKSIZE,
112 			high_prio_t2_wait_for_mutex,
113 			&mutex, NULL, NULL,
114 			prio + 2, 0, K_NO_WAIT);
115 
116 	/* Release mutex by current thread */
117 	sys_mutex_unlock(&mutex);
118 
119 	/* Wait for thread exiting */
120 	k_thread_join(&thread_low_data, K_FOREVER);
121 	k_thread_join(&thread_high_data1, K_FOREVER);
122 	k_thread_join(&thread_high_data2, K_FOREVER);
123 
124 	/* The mutex should be keep by high prio t1 thread */
125 	zassert_true(flag[0] == HIGH_T1,
126 	"The highest priority thread failed to take the mutex.");
127 
128 	/* The mutex should be keep by high prio t2 thread */
129 	zassert_true(flag[1] == HIGH_T2,
130 	"The higher priority thread failed to take the mutex.");
131 
132 	/* The mutex should be keep by low prio thread */
133 	zassert_true(flag[2] == LOW_PRO,
134 	"The low priority thread failed to take the mutex.");
135 
136 	/* Revert priority of the main thread */
137 	k_thread_priority_set(k_current_get(), old_prio);
138 }
139