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, ¬ify); 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