/* * Routine: ipc_mqueue_peek * Purpose: * Peek at a (non-set) message queue to see if it has a message * matching the sequence number provided (if zero, then the * first message in the queue) and return vital info about the * message. * * Conditions: * Locks may be held by callers, so this routine cannot block. * Caller holds reference on the message queue. */ unsigned ipc_mqueue_peek(ipc_mqueue_t mq, mach_port_seqno_t *seqnop, mach_msg_size_t *msg_sizep, mach_msg_id_t *msg_idp, mach_msg_max_trailer_t *msg_trailerp) { ipc_kmsg_queue_t kmsgq; ipc_kmsg_t kmsg; mach_port_seqno_t seqno, msgoff; int res = 0; spl_t s; assert(!imq_is_set(mq)); s = splsched(); imq_lock(mq); seqno = (seqnop != NULL) ? seqno = *seqnop : 0; if (seqno == 0) { seqno = mq->imq_seqno; msgoff = 0; } else if (seqno >= mq->imq_seqno && seqno < mq->imq_seqno + mq->imq_msgcount) { msgoff = seqno - mq->imq_seqno; } else goto out; /* look for the message that would match that seqno */ kmsgq = &mq->imq_messages; kmsg = ipc_kmsg_queue_first(kmsgq); while (msgoff-- && kmsg != IKM_NULL) { kmsg = ipc_kmsg_queue_next(kmsgq, kmsg); } if (kmsg == IKM_NULL) goto out; /* found one - return the requested info */ if (seqnop != NULL) *seqnop = seqno; if (msg_sizep != NULL) *msg_sizep = kmsg->ikm_header->msgh_size; if (msg_idp != NULL) *msg_idp = kmsg->ikm_header->msgh_id; if (msg_trailerp != NULL) memcpy(msg_trailerp, (mach_msg_max_trailer_t *)((vm_offset_t)kmsg->ikm_header + round_msg(kmsg->ikm_header->msgh_size)), sizeof(mach_msg_max_trailer_t)); res = 1; out: imq_unlock(mq); splx(s); return res; }
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); } } }
/* * 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; }