xref: /xnu-11215/osfmk/tests/test_thread_call.c (revision a5e72196)
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
test_once_callback(thread_call_param_t param0,thread_call_param_t param1)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
test_once_thread_call(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
test_signal_callback(__unused thread_call_param_t param0,__unused thread_call_param_t param1)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
test_signal_thread_call(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
test_thread_call(void)203 test_thread_call(void)
204 {
205 	test_once_thread_call();
206 	test_signal_thread_call();
207 
208 	return KERN_SUCCESS;
209 }
210