+ * Routine: ipc_mqueue_peek_locked
+ * 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:
+ * The ipc_mqueue_t is locked by callers.
+ * Other locks may be held by callers, so this routine cannot block.
+ * Caller holds reference on the message queue.
+ */
+unsigned
+ipc_mqueue_peek_locked(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_t *kmsgp)
+{
+ ipc_kmsg_queue_t kmsgq;
+ ipc_kmsg_t kmsg;
+ mach_port_seqno_t seqno, msgoff;
+ unsigned res = 0;
+
+ assert(!imq_is_set(mq));
+
+ seqno = 0;
+ if (seqnop != NULL)
+ seqno = *seqnop;
+
+ 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));
+ if (kmsgp != NULL)
+ *kmsgp = kmsg;
+
+ res = 1;
+
+out:
+ return res;
+}
+
+
+/*
+ * 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:
+ * The ipc_mqueue_t is unlocked.
+ * 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_t *kmsgp)
+{
+ unsigned res;
+
+ imq_lock(mq);
+
+ res = ipc_mqueue_peek_locked(mq, seqnop, msg_sizep, msg_idp,
+ msg_trailerp, kmsgp);
+
+ imq_unlock(mq);
+ return res;
+}
+
+/*
+ * Routine: ipc_mqueue_release_peek_ref
+ * Purpose:
+ * Release the reference on an mqueue's associated port which was
+ * granted to a thread in ipc_mqueue_peek_on_thread (on the
+ * MACH_PEEK_MSG thread wakeup path).
+ *
+ * Conditions:
+ * The ipc_mqueue_t should be locked on entry.
+ * The ipc_mqueue_t will be _unlocked_ on return
+ * (and potentially invalid!)
+ *
+ */
+void ipc_mqueue_release_peek_ref(ipc_mqueue_t mq)
+{
+ assert(!imq_is_set(mq));
+ assert(imq_held(mq));
+
+ /*
+ * clear any preposts this mq may have generated
+ * (which would cause subsequent immediate wakeups)
+ */
+ waitq_clear_prepost_locked(&mq->imq_wait_queue);
+
+ imq_unlock(mq);
+
+ /*
+ * release the port reference: we need to do this outside the lock
+ * because we might be holding the last port reference!
+ **/
+ ip_release_mq(mq);
+}
+
+/*
+ * peek at the contained port message queues, break prepost iteration as soon
+ * as we spot a message on one of the message queues referenced by the set's
+ * prepost list. No need to lock each message queue, as only the head of each
+ * queue is checked. If a message wasn't there before we entered here, no need
+ * to find it (if we do, great).
+ */
+static int mqueue_peek_iterator(void *ctx, struct waitq *waitq,
+ struct waitq_set *wqset)
+{
+ ipc_mqueue_t port_mq = (ipc_mqueue_t)waitq;
+ ipc_kmsg_queue_t kmsgs = &port_mq->imq_messages;
+
+ (void)ctx;
+ (void)wqset;
+
+ if (ipc_kmsg_queue_first(kmsgs) != IKM_NULL)
+ return WQ_ITERATE_BREAK; /* break out of the prepost iteration */
+
+ return WQ_ITERATE_CONTINUE;
+}
+
+/*
+ * Routine: ipc_mqueue_set_peek