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
22semaphore_t sender_sema = SEMAPHORE_NULL;
23mach_port_t msg_port = MACH_PORT_NULL;
24boolean_t msg_port_modref = FALSE;
25
26void *
27sender(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
162int
163main(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
266