]> git.saurik.com Git - apple/xnu.git/blob - osfmk/tests/test_thread_call.c
xnu-7195.60.75.tar.gz
[apple/xnu.git] / osfmk / tests / test_thread_call.c
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #if !(DEVELOPMENT || DEBUG)
30 #error "Testing is not enabled on RELEASE configurations"
31 #endif
32
33 #include <tests/xnupost.h>
34 #include <kern/thread_call.h>
35 #include <kern/locks.h>
36 #include <kern/sched_prim.h>
37
38 kern_return_t test_thread_call(void);
39
40 lck_grp_t test_lock_grp;
41 lck_mtx_t test_lock;
42
43 typedef enum {
44 TEST_ARG1 = 0x1234,
45 TEST_ARG2 = 0x3456,
46 } test_param;
47
48 int wait_for_callback;
49 int wait_for_main;
50
51 int once_callback_counter = 0;
52
53 static void
54 test_once_callback(thread_call_param_t param0,
55 thread_call_param_t param1)
56 {
57 T_ASSERT_EQ_INT((test_param)param0, TEST_ARG1, "param0 is correct");
58 T_ASSERT_EQ_INT((test_param)param1, TEST_ARG2, "param1 is correct");
59
60 once_callback_counter++;
61
62 T_ASSERT_EQ_INT(once_callback_counter, 1, "only one callback");
63
64 lck_mtx_lock(&test_lock);
65
66 thread_wakeup(&wait_for_callback);
67
68 uint64_t deadline;
69 clock_interval_to_deadline(10, NSEC_PER_SEC, &deadline);
70
71 kern_return_t kr;
72 /* wait for the main thread to finish, time out after 10s */
73 kr = lck_mtx_sleep_deadline(&test_lock, LCK_SLEEP_DEFAULT, &wait_for_main, THREAD_UNINT, deadline);
74 T_ASSERT_EQ_INT(kr, THREAD_AWAKENED, " callback woken by main function");
75
76 lck_mtx_unlock(&test_lock);
77
78 /* sleep for 1s to let the main thread begin the cancel and wait */
79 delay_for_interval(1, NSEC_PER_SEC);
80 }
81
82 static void
83 test_once_thread_call(void)
84 {
85 lck_grp_init(&test_lock_grp, "test_thread_call", LCK_GRP_ATTR_NULL);
86 lck_mtx_init(&test_lock, &test_lock_grp, LCK_ATTR_NULL);
87
88 thread_call_t call;
89 call = thread_call_allocate_with_options(&test_once_callback,
90 (thread_call_param_t)TEST_ARG1,
91 THREAD_CALL_PRIORITY_HIGH,
92 THREAD_CALL_OPTIONS_ONCE);
93
94 thread_call_param_t arg2_param = (thread_call_param_t)TEST_ARG2;
95
96 lck_mtx_lock(&test_lock);
97
98 thread_call_enter1(call, arg2_param);
99
100 uint64_t deadline;
101 clock_interval_to_deadline(10, NSEC_PER_SEC, &deadline);
102
103 kern_return_t kr;
104 /* wait for the call to execute, time out after 10s */
105 kr = lck_mtx_sleep_deadline(&test_lock, LCK_SLEEP_DEFAULT, &wait_for_callback, THREAD_UNINT, deadline);
106 T_ASSERT_EQ_INT(kr, THREAD_AWAKENED, "main function woken by callback");
107
108 lck_mtx_unlock(&test_lock);
109
110 /* at this point the callback is stuck waiting */
111
112 T_ASSERT_EQ_INT(once_callback_counter, 1, "callback fired");
113
114 boolean_t canceled, pending, freed;
115
116 canceled = thread_call_cancel(call);
117 T_ASSERT_EQ_INT(canceled, FALSE, "thread_call_cancel should not succeed");
118
119 pending = thread_call_enter1(call, arg2_param);
120 T_ASSERT_EQ_INT(pending, FALSE, "call should not be pending");
121
122 /* sleep for 10ms, the call should not execute */
123 delay_for_interval(10, NSEC_PER_MSEC);
124
125 canceled = thread_call_cancel(call);
126 T_ASSERT_EQ_INT(canceled, TRUE, "thread_call_cancel should succeed");
127
128 pending = thread_call_enter1(call, arg2_param);
129 T_ASSERT_EQ_INT(pending, FALSE, "call should not be pending");
130
131 freed = thread_call_free(call);
132 T_ASSERT_EQ_INT(freed, FALSE, "thread_call_free should not succeed");
133
134 pending = thread_call_enter1(call, arg2_param);
135 T_ASSERT_EQ_INT(pending, TRUE, "call should be pending");
136
137 thread_wakeup(&wait_for_main);
138
139 canceled = thread_call_cancel_wait(call);
140 T_ASSERT_EQ_INT(canceled, TRUE, "thread_call_cancel_wait should succeed");
141
142 canceled = thread_call_cancel(call);
143 T_ASSERT_EQ_INT(canceled, FALSE, "thread_call_cancel should not succeed");
144
145 freed = thread_call_free(call);
146 T_ASSERT_EQ_INT(freed, TRUE, "thread_call_free should succeed");
147 }
148
149 int signal_callback_counter = 0;
150
151 static void
152 test_signal_callback(__unused thread_call_param_t param0,
153 __unused thread_call_param_t param1)
154 {
155 /*
156 * ktest sometimes panics if you assert from interrupt context,
157 * and the serial logging will blow past the delay to wait for the interrupt
158 * so don't print in this context.
159 */
160
161 signal_callback_counter++;
162 }
163
164 static void
165 test_signal_thread_call(void)
166 {
167 thread_call_t call;
168 call = thread_call_allocate_with_options(&test_signal_callback,
169 (thread_call_param_t)TEST_ARG1,
170 THREAD_CALL_PRIORITY_HIGH,
171 THREAD_CALL_OPTIONS_ONCE | THREAD_CALL_OPTIONS_SIGNAL);
172
173 thread_call_param_t arg2_param = (thread_call_param_t)TEST_ARG2;
174
175 uint64_t deadline;
176
177 boolean_t canceled, pending, freed;
178
179 clock_interval_to_deadline(10, NSEC_PER_SEC, &deadline);
180 pending = thread_call_enter1_delayed(call, arg2_param, deadline);
181 T_ASSERT_EQ_INT(pending, FALSE, "call should not be pending");
182
183 canceled = thread_call_cancel(call);
184 T_ASSERT_EQ_INT(canceled, TRUE, "thread_call_cancel should succeed");
185
186 clock_interval_to_deadline(10, NSEC_PER_MSEC, &deadline);
187 pending = thread_call_enter1_delayed(call, arg2_param, deadline);
188 T_ASSERT_EQ_INT(pending, FALSE, "call should not be pending");
189
190 /* sleep for 50ms to let the interrupt fire */
191 delay_for_interval(50, NSEC_PER_MSEC);
192
193 T_ASSERT_EQ_INT(signal_callback_counter, 1, "callback fired");
194
195 canceled = thread_call_cancel(call);
196 T_ASSERT_EQ_INT(canceled, FALSE, "thread_call_cancel should not succeed");
197
198 freed = thread_call_free(call);
199 T_ASSERT_EQ_INT(freed, TRUE, "thread_call_free should succeed");
200 }
201
202 kern_return_t
203 test_thread_call(void)
204 {
205 test_once_thread_call();
206 test_signal_thread_call();
207
208 return KERN_SUCCESS;
209 }