]> git.saurik.com Git - apple/xnu.git/blame - tests/turnstiles_test.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / tests / turnstiles_test.c
CommitLineData
d9a64523
A
1/*
2 * turnstiles_test: Tests turnstile kernel primitive.
3 */
4
5#ifdef T_NAMESPACE
6#undef T_NAMESPACE
7#endif
8
9#include <darwintest.h>
10#include <darwintest_multiprocess.h>
11
12#include <pthread.h>
13#include <launch.h>
14#include <servers/bootstrap.h>
15#include <stdlib.h>
16#include <sys/event.h>
17#include <unistd.h>
18#include <crt_externs.h>
19#include <sys/sysctl.h>
20#include <sys/types.h>
21
22#define SYSCTL_TURNSTILE_TEST_DEFAULT 1
23#define SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE 2
24
25
26T_GLOBAL_META(T_META_NAMESPACE("xnu.turnstiles_test"));
27
28static void
29thread_create_at_qos(qos_class_t qos, void * (*function)(void *), int type)
30{
31 qos_class_t qos_thread;
32 pthread_t thread;
33 pthread_attr_t attr;
34 int ret;
35
36 ret = setpriority(PRIO_DARWIN_ROLE, 0, PRIO_DARWIN_ROLE_UI_FOCAL);
37 if (ret != 0) {
38 T_LOG("set priority failed\n");
39 }
40
41 pthread_attr_init(&attr);
42 pthread_attr_set_qos_class_np(&attr, qos, 0);
43 pthread_create(&thread, &attr, function, (void *)type);
44
45 T_LOG("pthread created\n");
46 pthread_get_qos_class_np(thread, &qos_thread, NULL);
47 T_EXPECT_EQ(qos_thread, (qos_class_t)qos, NULL);
48}
49
50static int
51get_pri(thread_t thread_port) {
52 kern_return_t kr;
53
54 thread_extended_info_data_t extended_info;
55 mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT;
56 kr = thread_info(thread_port, THREAD_EXTENDED_INFO,
57 (thread_info_t)&extended_info, &count);
58
59 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_info");
60 return extended_info.pth_curpri;
61}
62
63static void
64turnstile_prim_lock(int type)
65{
66 int ret;
67 uint64_t tid;
68 int in_val = type;
69 pthread_threadid_np(NULL, &tid);
70 T_LOG("sysctlbyname lock called from thread %llu \n", tid);
71 ret = sysctlbyname("kern.turnstiles_test_lock", NULL, 0, &in_val, sizeof(in_val));
72 T_LOG("sysctlbyname lock returned from thread %llu with value %d \n", tid, ret);
73}
74
75static void
76turnstile_prim_unlock(int type)
77{
78 int ret;
79 uint64_t tid;
80 int in_val = type;
81 pthread_threadid_np(NULL, &tid);
82 T_LOG("sysctlbyname unlock called from thread %llu \n", tid);
83 ret = sysctlbyname("kern.turnstiles_test_unlock", NULL, 0, &in_val, sizeof(in_val));
84 T_LOG("sysctlbyname unlock returned from thread %llu with value %d \n", tid, ret);
85}
86
87static void *
88take_lock_check_priority(void * arg)
89{
90 int old_pri = get_pri(mach_thread_self());
91 int unboosted_pri;
92 int boosted_pri;
93 int after_unlock_pri;
94 uint64_t tid;
95 int type = (int)arg;
96
97 pthread_threadid_np(NULL, &tid);
98
99 T_ASSERT_EQ(old_pri, 37, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri);
100
101 /* Take the test lock */
102 turnstile_prim_lock(type);
103
104 unboosted_pri = get_pri(mach_thread_self());
105 T_ASSERT_EQ(unboosted_pri, 37, "thread(%llu) priority after acquiring the lock (uncontended) is %d\n", tid, unboosted_pri);
106
107 sleep(8);
108
109 /* Check for elevated priority */
110 boosted_pri = get_pri(mach_thread_self());
111 T_ASSERT_EQ(boosted_pri, 47, "thread(%llu) priority after contention by 47 thread is %d\n", tid, boosted_pri);
112
113 /* Drop the lock */
114 turnstile_prim_unlock(type);
115
116 /* Check for regular priority */
117 after_unlock_pri = get_pri(mach_thread_self());
118 T_ASSERT_EQ(after_unlock_pri, 37, "thread(%llu) priority after dropping lock is %d\n", tid, after_unlock_pri);
119
120 return NULL;
121}
122
123static void *
124try_to_take_lock_and_unlock(void *arg)
125{
126 uint64_t tid;
127 int type = (int)arg;
128
129 pthread_threadid_np(NULL, &tid);
130 sleep(4);
131
132 int old_pri = get_pri(mach_thread_self());
133 T_ASSERT_EQ(old_pri, 47, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri);
134
135 /* Try taking the test lock */
136 turnstile_prim_lock(type);
137 sleep (2);
138 turnstile_prim_unlock(type);
139 return NULL;
140}
141
142static void *
143take_lock_and_exit(void * arg)
144{
145 int old_pri = get_pri(mach_thread_self());
146 int unboosted_pri;
147 int boosted_pri;
148 uint64_t tid;
149 int type = (int)arg;
150
151 pthread_threadid_np(NULL, &tid);
152
153 T_ASSERT_EQ(old_pri, 37, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri);
154
155 /* Take the test lock */
156 turnstile_prim_lock(type);
157
158 unboosted_pri = get_pri(mach_thread_self());
159 T_ASSERT_EQ(unboosted_pri, 37, "thread(%llu) priority after acquiring the lock (uncontended) is %d\n", tid, unboosted_pri);
160
161 sleep(8);
162
163 /* Check for elevated priority */
164 boosted_pri = get_pri(mach_thread_self());
165 T_ASSERT_EQ(boosted_pri, 47, "thread(%llu) priority after contention by 47 thread is %d\n", tid, boosted_pri);
166
167 /* return without unlocking the lock */
168 return NULL;
169}
170
171static void *
172unlock_an_owner_exited_lock(void *arg)
173{
174 uint64_t tid;
175 int type = (int)arg;
176
177 pthread_threadid_np(NULL, &tid);
178 sleep(12);
179
180 int old_pri = get_pri(mach_thread_self());
181 T_ASSERT_EQ(old_pri, 47, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri);
182
183 /* Unlock the test lock causing the turnstile code to call thread_deallocate_safe */
184 turnstile_prim_unlock(type);
185 return NULL;
186}
187
188/*
189 * Test 1: test if lock contended by a UI thread boosts the owner to UI qos.
190 */
191static void
192test1(int type)
193{
194 T_LOG("Test 1: test if lock contended by a UI thread boosts the owner to UI qos");
195
196 /* Create a thread at IN and take lock */
197 thread_create_at_qos(QOS_CLASS_USER_INITIATED, &take_lock_check_priority, type);
198
199 /* Create a thread at UI and try to take lock */
200 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type);
201
202 sleep(12);
203 return;
204}
205
206/*
207 * Test 2: test if lock contended by a 2 UI thread boosts the owner to UI qos.
208 */
209static void
210test2(int type)
211{
212 T_LOG("Test 2: test if lock contended by a 2 UI thread boosts the owner to UI qos");
213
214 /* Create a thread at IN and take lock */
215 thread_create_at_qos(QOS_CLASS_USER_INITIATED, &take_lock_check_priority, type);
216
217 /* Create a thread at UI and try to take lock */
218 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type);
219
220 /* Create a thread at UI and try to take lock */
221 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type);
222
223 sleep(16);
224 return;
225}
226
227/*
228 * Test 3: test if lock owner thread exiting without unlocking allows turnstile to work correctly.
229 */
230static void
231test3(int type)
232{
233 T_LOG("Test 3: test if lock owner thread exiting without unlocking allows turnstile to work correctly");
234
235 /* Create a thread at IN and take lock */
236 thread_create_at_qos(QOS_CLASS_USER_INITIATED, &take_lock_and_exit, type);
237
238 /* Create a thread at UI and try to take lock */
239 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type);
240
241 /* Create a thread at UI and try to take lock */
242 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &unlock_an_owner_exited_lock, type);
243
244 sleep(16);
245 return;
246}
247
248T_DECL(turnstile_test, "Turnstile test", T_META_ASROOT(YES))
249{
250 test1(SYSCTL_TURNSTILE_TEST_DEFAULT);
251 test2(SYSCTL_TURNSTILE_TEST_DEFAULT);
252 test3(SYSCTL_TURNSTILE_TEST_DEFAULT);
253
254 test1(SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE);
255 test2(SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE);
256 test3(SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE);
257
258}