]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/unit_tests/sprace_test_11891562_src/sprace_test_11891562.c
cf37ef7cd026dfdfc95bcdc2bd2903856bdc10d4
[apple/xnu.git] / tools / tests / unit_tests / sprace_test_11891562_src / sprace_test_11891562.c
1
2 /*
3 * File: sprace_test_11891562.c
4 * Test Description: The test ensures that there are no race conditions when multiple threads
5 * attempt to send messages to a mach port with a subset of threads waiting for a send possible
6 * notification.
7 * Radar: <rdar://problem/11891562>
8 */
9 #include <stdio.h>
10 #include <unistd.h>
11 #include <pthread.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <assert.h>
15 #include <stdlib.h>
16
17 #include <mach/mach.h>
18
19 #define VERBOSE 1
20 #define COUNT 3000000
21
22 semaphore_t sender_sema = SEMAPHORE_NULL;
23 mach_port_t msg_port = MACH_PORT_NULL;
24 boolean_t msg_port_modref = FALSE;
25
26 void *
27 sender(void *arg)
28 {
29 mach_msg_empty_send_t smsg;
30 mach_port_t notify, old_notify;
31 kern_return_t kr;
32 boolean_t msg_inited;
33 boolean_t use_sp = *(boolean_t *)arg;
34 int send_possible_count = 0;
35
36 fprintf(stderr, "starting a thread %susing send-possible notifications.\n",
37 (!use_sp) ? "not " : "");
38
39 if (use_sp) {
40 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &notify);
41 if (KERN_SUCCESS != kr) {
42 mach_error("mach_port_allocate(notify)", kr);
43 exit(1);
44 }
45
46 request:
47 kr = mach_port_request_notification(mach_task_self(), msg_port,
48 MACH_NOTIFY_SEND_POSSIBLE, 0 /* delayed */,
49 notify, MACH_MSG_TYPE_MAKE_SEND_ONCE,
50 &old_notify);
51 if (KERN_INVALID_ARGUMENT == kr && msg_port_modref)
52 goto done;
53
54 if (KERN_SUCCESS != kr) {
55 mach_error("mach_port_request_notification(MACH_NOTIFY_SEND_POSSIBLE)", kr);
56 exit(1);
57 }
58 if (MACH_PORT_NULL != old_notify) {
59 fprintf(stderr, "unexecpted old notify port (0x%x)\n", old_notify);
60 exit(1);
61 }
62 }
63
64 msg_inited = FALSE;
65
66 for (;;) {
67 mach_send_possible_notification_t nmsg;
68 mach_msg_option_t options;
69 mach_msg_return_t mret;
70
71 if (!msg_inited) {
72 mach_msg_option_t options;
73
74 smsg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
75 smsg.header.msgh_remote_port = msg_port;
76 smsg.header.msgh_local_port = MACH_PORT_NULL;
77 smsg.header.msgh_size = sizeof(smsg);
78 smsg.header.msgh_id = 0;
79 msg_inited = TRUE;
80 }
81
82 options = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
83 if (use_sp)
84 options |= MACH_SEND_NOTIFY;
85
86 mret = mach_msg(&smsg.header, options,
87 sizeof(smsg), 0,
88 MACH_PORT_NULL,
89 MACH_MSG_TIMEOUT_NONE /* immediate timeout */,
90 MACH_PORT_NULL);
91
92 if (MACH_MSG_SUCCESS == mret) {
93 msg_inited = FALSE;
94 continue;
95 }
96
97 if (MACH_SEND_INVALID_DEST == mret)
98 break;
99
100 if (MACH_SEND_TIMED_OUT != mret) {
101 mach_error("mach_msg(send)", mret);
102 exit(1);
103 }
104
105 if (use_sp) {
106
107 /* Wait for the send-possible notification */
108 mret = mach_msg(&nmsg.not_header, MACH_RCV_MSG | MACH_RCV_TIMEOUT,
109 0, sizeof(nmsg),
110 notify,
111 10000 /* 10 second timeout */,
112 MACH_PORT_NULL);
113
114 if (msg_port_modref)
115 goto done;
116
117 if (MACH_RCV_TIMED_OUT == mret) {
118 fprintf(stderr, "FAILED! Didn't receive send-possible notification\n");
119 exit(1);
120 }
121
122 if (MACH_MSG_SUCCESS != mret) {
123 mach_error("mach_msg_receive(notify)\n", mret);
124 exit(1);
125 }
126
127 switch (nmsg.not_header.msgh_id) {
128
129 case MACH_NOTIFY_SEND_POSSIBLE:
130 if (nmsg.not_port != msg_port) {
131 fprintf(stderr, "send possible notification about wrong port (0x%x != 0x%x)\n", nmsg.not_port, msg_port);
132 exit(1);
133 }
134 send_possible_count++;
135
136 semaphore_signal_all(sender_sema);
137 goto request;
138
139 case MACH_NOTIFY_DEAD_NAME:
140 if (nmsg.not_port != msg_port) {
141 fprintf(stderr, "dead name notification about wrong port (0x%x != 0x%x)\n", nmsg.not_port, msg_port);
142 exit(1);
143 }
144 goto done;
145 default:
146 fprintf(stderr, "unexected notify id (%d)\n", nmsg.not_header.msgh_id);
147 exit(1);
148 }
149 } else {
150 semaphore_wait(sender_sema);
151 }
152 }
153
154 done:
155 if (use_sp) {
156 mach_port_destroy(mach_task_self(), notify);
157 fprintf(stderr, "received %d send-possible notifications\n", send_possible_count);
158 }
159 return(NULL);
160 }
161
162 int
163 main(int argc, char **argv) {
164 mach_msg_return_t mret;
165 mach_port_limits_t limits;
166 pthread_t thread1, thread2, thread3;
167 boolean_t thread1_arg, thread2_arg, thread3_arg;
168 kern_return_t kr;
169 int i, res;
170
171 /* allocate receive and send right for the message port */
172 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &msg_port);
173 if (KERN_SUCCESS != kr) {
174 mach_error("mach_port_allocate(msg_port)", kr);
175 exit(1);
176 }
177 kr = mach_port_insert_right(mach_task_self(), msg_port, msg_port, MACH_MSG_TYPE_MAKE_SEND);
178 if (KERN_SUCCESS != kr) {
179 mach_error("mach_port_insert_right(msg_port)", kr);
180 exit(1);
181 }
182
183 /* bump its qlimit up enough to allow races to develop between threads */
184 limits.mpl_qlimit = 100;
185 kr = mach_port_set_attributes(mach_task_self(), msg_port,
186 MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, sizeof(limits)/sizeof(int));
187 if (KERN_SUCCESS != kr) {
188 mach_error("mach_port_allocate(msg_port)", kr);
189 exit(1);
190 }
191
192 kr = semaphore_create(mach_task_self(), &sender_sema, SYNC_POLICY_FIFO, 0 /* initial value */);
193 if (KERN_SUCCESS != kr) {
194 mach_error("semaphore_create(sender_sema)\n", kr);
195 exit(1);
196 }
197
198 thread1_arg = FALSE; /* don't use send-possible notifications */
199 res = pthread_create(&thread1, (pthread_attr_t *)NULL, sender, &thread1_arg);
200 if (res) {
201 perror("pthread_create(non-send-possible_thread-1)");
202 exit(1);
203 }
204
205 thread2_arg = FALSE; /* don't use send-possible notifications */
206 res = pthread_create(&thread2, (pthread_attr_t *)NULL, sender, &thread2_arg);
207 if (res) {
208 perror("pthread_create(non-send-possible_thread-2)");
209 exit(1);
210 }
211
212 thread3_arg = TRUE; /* use send-possible notifications */
213 res = pthread_create(&thread3, (pthread_attr_t *)NULL, sender, &thread3_arg);
214 if (res) {
215 perror("pthread_create(send-possible-thread-3)");
216 exit(1);
217 }
218
219 for (i=0; i < COUNT; i++) {
220 mach_msg_empty_rcv_t rmsg;
221
222 mret = mach_msg(&rmsg.header, MACH_RCV_MSG,
223 0, sizeof(rmsg),
224 msg_port,
225 MACH_MSG_TIMEOUT_NONE,
226 MACH_PORT_NULL);
227 if (MACH_MSG_SUCCESS != mret) {
228 mach_error("mach_msg_receive(msg_port)\n", mret);
229 exit(1);
230 }
231 }
232
233 msg_port_modref = TRUE;
234 kr = mach_port_mod_refs(mach_task_self(), msg_port, MACH_PORT_RIGHT_RECEIVE, -1);
235 if (KERN_SUCCESS != kr) {
236 mach_error("mach_port_mod_refs(msg_port)", kr);
237 exit(1);
238 }
239
240 kr = semaphore_destroy(mach_task_self(), sender_sema);
241 if (KERN_SUCCESS != kr) {
242 mach_error("semaphore_destroy(sender_sema)", kr);
243 exit(1);
244 }
245
246 res = pthread_join(thread1, NULL);
247 if (res) {
248 perror("pthread_join(thread1)");
249 exit(1);
250 }
251 res = pthread_join(thread2, NULL);
252 if (res) {
253 perror("pthread_join(thread2)");
254 exit(1);
255 }
256 res = pthread_join(thread3, NULL);
257 if (res) {
258 perror("pthread_join(thread3)");
259 exit(1);
260 }
261
262 printf("[PASSED] Test sprace_test_11891562 passed. \n");
263 exit(0);
264 }
265