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