void ipc_mqueue_move( ipc_mqueue_t dest, ipc_mqueue_t source, const ipc_port_t port) { ipc_kmsg_queue_t oldq, newq; ipc_thread_queue_t blockedq; ipc_kmsg_t kmsg, next; ipc_thread_t th; oldq = &source->imq_messages; newq = &dest->imq_messages; blockedq = &dest->imq_threads; for (kmsg = ipc_kmsg_queue_first(oldq); kmsg != IKM_NULL; kmsg = next) { next = ipc_kmsg_queue_next(oldq, kmsg); /* only move messages sent to port */ if (kmsg->ikm_header.msgh_remote_port != (mach_port_t) port) continue; ipc_kmsg_rmqueue(oldq, kmsg); /* before adding kmsg to newq, check for a blocked receiver */ while ((th = ipc_thread_dequeue(blockedq)) != ITH_NULL) { assert(ipc_kmsg_queue_empty(newq)); thread_go(th); /* check if the receiver can handle the message */ if (kmsg->ikm_header.msgh_size <= th->ith_msize) { th->ith_state = MACH_MSG_SUCCESS; th->ith_kmsg = kmsg; th->ith_seqno = port->ip_seqno++; goto next_kmsg; } th->ith_state = MACH_RCV_TOO_LARGE; th->ith_msize = kmsg->ikm_header.msgh_size; } /* didn't find a receiver to handle the message */ ipc_kmsg_enqueue(newq, kmsg); next_kmsg:; } }
void ipc_mqueue_move( ipc_mqueue_t dest, ipc_mqueue_t source, ipc_port_t port) { ipc_kmsg_queue_t oldq, newq; ipc_thread_queue_t blockedq; ipc_kmsg_t kmsg, next; ipc_thread_t th; oldq = &source->imq_messages; newq = &dest->imq_messages; blockedq = &dest->imq_threads; for (kmsg = ipc_kmsg_queue_first(oldq); kmsg != IKM_NULL; kmsg = next) { next = ipc_kmsg_queue_next(oldq, kmsg); /* only move messages sent to port */ if (kmsg->ikm_header.msgh_remote_port != (mach_port_t) port) continue; ipc_kmsg_rmqueue(oldq, kmsg); /* before adding kmsg to newq, check for a blocked receiver */ if ((th = ipc_thread_dequeue(blockedq)) != ITH_NULL) { assert(ipc_kmsg_queue_empty(newq)); /* * Got a receiver. Hand the receiver the message * and process the next one. */ th->ith_state = MACH_MSG_SUCCESS; th->ith_kmsg = kmsg; th->ith_seqno = port->ip_seqno++; thread_go(th); } else { /* didn't find a receiver; enqueue the message */ ipc_kmsg_enqueue(newq, kmsg); } } }
/* flipc_port_destroy() is called to convert a flipc port back to a * local-only ipc port (i.e., the port has no remaining off-node rights). * This will dispose of any undelivered flipc messages, generating NAKs if * needed. <lport> must be locked on entry and is not unlocked on return. */ static void flipc_port_destroy(ipc_port_t lport) { /* Ensure parameter is valid, and linked to an fport with a valid name */ assert(IP_VALID(lport)); ipc_mqueue_t port_mq = &lport->ip_messages; flipc_port_t fport = port_mq->data.port.fport; assert(FPORT_VALID(fport)); assert(MNL_NAME_VALID(fport->obj.name)); /* Dispose of any undelivered messages */ int m = port_mq->data.port.msgcount; if (m > 0) { ipc_kmsg_t kmsg; #ifdef DEBUG printf("flipc: destroying %p with %d undelivered msgs\n", lport, m); #endif /* Logic was lifted from ipc_mqueue_select_on_thread() */ while (m--) { kmsg = ipc_kmsg_queue_first(&port_mq->imq_messages); assert(kmsg != IKM_NULL); ipc_kmsg_rmqueue(&port_mq->imq_messages, kmsg); if (fport->state == FPORT_STATE_PRINCIPAL) flipc_msg_ack(kmsg->ikm_node, port_mq, FALSE); ipc_mqueue_release_msgcount(port_mq, NULL); port_mq->imq_seqno++; } } /* Remove from name hash table, unlink co-structures, and free fport */ mnl_obj_remove(fport->obj.name); lport->ip_messages.data.port.fport = FPORT_NULL; fport->lport = IP_NULL; zfree(flipc_port_zone, fport); }
/* * Routine: ipc_mqueue_add * Purpose: * Associate the portset's mqueue with the port's mqueue. * This has to be done so that posting the port will wakeup * a portset waiter. If there are waiters on the portset * mqueue and messages on the port mqueue, try to match them * up now. * Conditions: * May block. */ kern_return_t ipc_mqueue_add( ipc_mqueue_t port_mqueue, ipc_mqueue_t set_mqueue, wait_queue_link_t wql) { wait_queue_t port_waitq = &port_mqueue->imq_wait_queue; wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue; ipc_kmsg_queue_t kmsgq; ipc_kmsg_t kmsg, next; kern_return_t kr; spl_t s; kr = wait_queue_link_noalloc(port_waitq, set_waitq, wql); if (kr != KERN_SUCCESS) return kr; /* * Now that the set has been added to the port, there may be * messages queued on the port and threads waiting on the set * waitq. Lets get them together. */ s = splsched(); imq_lock(port_mqueue); kmsgq = &port_mqueue->imq_messages; for (kmsg = ipc_kmsg_queue_first(kmsgq); kmsg != IKM_NULL; kmsg = next) { next = ipc_kmsg_queue_next(kmsgq, kmsg); for (;;) { thread_t th; mach_msg_size_t msize; th = wait_queue_wakeup64_identity_locked( port_waitq, IPC_MQUEUE_RECEIVE, THREAD_AWAKENED, FALSE); /* waitq/mqueue still locked, thread locked */ if (th == THREAD_NULL) goto leave; /* * If the receiver waited with a facility not directly * related to Mach messaging, then it isn't prepared to get * handed the message directly. Just set it running, and * go look for another thread that can. */ if (th->ith_state != MACH_RCV_IN_PROGRESS) { thread_unlock(th); continue; } /* * Found a receiver. see if they can handle the message * correctly (the message is not too large for them, or * they didn't care to be informed that the message was * too large). If they can't handle it, take them off * the list and let them go back and figure it out and * just move onto the next. */ msize = ipc_kmsg_copyout_size(kmsg, th->map); if (th->ith_msize < (msize + REQUESTED_TRAILER_SIZE(thread_is_64bit(th), th->ith_option))) { th->ith_state = MACH_RCV_TOO_LARGE; th->ith_msize = msize; if (th->ith_option & MACH_RCV_LARGE) { /* * let him go without message */ th->ith_receiver_name = port_mqueue->imq_receiver_name; th->ith_kmsg = IKM_NULL; th->ith_seqno = 0; thread_unlock(th); continue; /* find another thread */ } } else { th->ith_state = MACH_MSG_SUCCESS; } /* * This thread is going to take this message, * so give it to him. */ ipc_kmsg_rmqueue(kmsgq, kmsg); ipc_mqueue_release_msgcount(port_mqueue); th->ith_kmsg = kmsg; th->ith_seqno = port_mqueue->imq_seqno++; thread_unlock(th); break; /* go to next message */ } } leave: imq_unlock(port_mqueue); splx(s); return KERN_SUCCESS; }