void ipc_pset_add( ipc_pset_t pset, ipc_port_t port) { assert(ips_active(pset)); assert(ip_active(port)); assert(port->ip_pset == IPS_NULL); port->ip_pset = pset; port->ip_cur_target = &pset->ips_target; ips_reference(pset); imq_lock(&port->ip_messages); imq_lock(&pset->ips_messages); /* move messages from port's queue to the port set's queue */ ipc_mqueue_move(&pset->ips_messages, &port->ip_messages, port); imq_unlock(&pset->ips_messages); assert(ipc_kmsg_queue_empty(&port->ip_messages.imq_messages)); /* wake up threads waiting to receive from the port */ ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_CHANGED); assert(ipc_thread_queue_empty(&port->ip_messages.imq_threads)); imq_unlock(&port->ip_messages); }
static int filt_machportattach( struct knote *kn) { mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident; uint64_t wq_link_id = waitq_link_reserve(NULL); ipc_pset_t pset = IPS_NULL; int result = ENOSYS; kern_return_t kr; kr = ipc_object_translate(current_space(), name, MACH_PORT_RIGHT_PORT_SET, (ipc_object_t *)&pset); if (kr != KERN_SUCCESS) { waitq_link_release(wq_link_id); return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP); } /* We've got a lock on pset */ /* * Bind the portset wait queue directly to knote/kqueue. * This allows us to just use wait_queue foo to effect a wakeup, * rather than having to call knote() from the Mach code on each * message. */ result = knote_link_waitq(kn, &pset->ips_messages.imq_wait_queue, &wq_link_id); if (result == 0) { waitq_link_release(wq_link_id); /* keep a reference for the knote */ kn->kn_ptr.p_pset = pset; ips_reference(pset); ips_unlock(pset); return 0; } ips_unlock(pset); waitq_link_release(wq_link_id); return result; }
static int filt_machport( struct knote *kn, __unused long hint) { mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident; ipc_pset_t pset = IPS_NULL; wait_result_t wresult; thread_t self = current_thread(); kern_return_t kr; mach_msg_option_t option; mach_msg_size_t size; /* never called from below */ assert(hint == 0); /* * called from user context. Have to validate the * name. If it changed, we have an EOF situation. */ kr = ipc_object_translate(current_space(), name, MACH_PORT_RIGHT_PORT_SET, (ipc_object_t *)&pset); if (kr != KERN_SUCCESS || pset != kn->kn_ptr.p_pset || !ips_active(pset)) { kn->kn_data = 0; kn->kn_flags |= (EV_EOF | EV_ONESHOT); if (pset != IPS_NULL) { ips_unlock(pset); } return(1); } /* just use the reference from here on out */ ips_reference(pset); ips_unlock(pset); /* * Only honor supported receive options. If no options are * provided, just force a MACH_RCV_TOO_LARGE to detect the * name of the port and sizeof the waiting message. */ option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_LARGE_IDENTITY| MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER); if (option & MACH_RCV_MSG) { self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0]; size = (mach_msg_size_t)kn->kn_ext[1]; } else { option = MACH_RCV_LARGE; self->ith_msg_addr = 0; size = 0; } /* * Set up to receive a message or the notification of a * too large message. But never allow this call to wait. * If the user provided aditional options, like trailer * options, pass those through here. But we don't support * scatter lists through this interface. */ self->ith_object = (ipc_object_t)pset; self->ith_msize = size; self->ith_option = option; self->ith_receiver_name = MACH_PORT_NULL; self->ith_continuation = NULL; option |= MACH_RCV_TIMEOUT; // never wait self->ith_state = MACH_RCV_IN_PROGRESS; wresult = ipc_mqueue_receive_on_thread( &pset->ips_messages, option, size, /* max_size */ 0, /* immediate timeout */ THREAD_INTERRUPTIBLE, self); assert(wresult == THREAD_NOT_WAITING); assert(self->ith_state != MACH_RCV_IN_PROGRESS); /* * If we timed out, just release the reference on the * portset and return zero. */ if (self->ith_state == MACH_RCV_TIMED_OUT) { ips_release(pset); return 0; } /* * If we weren't attempting to receive a message * directly, we need to return the port name in * the kevent structure. */ if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) { assert(self->ith_state == MACH_RCV_TOO_LARGE); assert(self->ith_kmsg == IKM_NULL); kn->kn_data = self->ith_receiver_name; ips_release(pset); return 1; } /* * Attempt to receive the message directly, returning * the results in the fflags field. */ assert(option & MACH_RCV_MSG); kn->kn_ext[1] = self->ith_msize; kn->kn_data = MACH_PORT_NULL; kn->kn_fflags = mach_msg_receive_results(); /* kmsg and pset reference consumed */ /* * if the user asked for the identity of ports containing a * a too-large message, return it in the data field (as we * do for messages we didn't try to receive). */ if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) && (option & MACH_RCV_LARGE_IDENTITY)) kn->kn_data = self->ith_receiver_name; return 1; }