/* * Initialize a new shared message queue. */ shm_mq * shm_mq_create(void *address, Size size) { shm_mq *mq = address; Size data_offset = MAXALIGN(offsetof(shm_mq, mq_ring)); /* If the size isn't MAXALIGN'd, just discard the odd bytes. */ size = MAXALIGN_DOWN(size); /* Queue size must be large enough to hold some data. */ Assert(size > data_offset); /* Initialize queue header. */ SpinLockInit(&mq->mq_mutex); mq->mq_receiver = NULL; mq->mq_sender = NULL; mq->mq_bytes_read = 0; mq->mq_bytes_written = 0; mq->mq_ring_size = size - data_offset; mq->mq_detached = false; mq->mq_ring_offset = data_offset - offsetof(shm_mq, mq_ring); return mq; }
/* * Write a message into a shared message queue, gathered from multiple * addresses. * * When nowait = false, we'll wait on our process latch when the ring buffer * fills up, and then continue writing once the receiver has drained some data. * The process latch is reset after each wait. * * When nowait = true, we do not manipulate the state of the process latch; * instead, if the buffer becomes full, we return SHM_MQ_WOULD_BLOCK. In * this case, the caller should call this function again, with the same * arguments, each time the process latch is set. (Once begun, the sending * of a message cannot be aborted except by detaching from the queue; changing * the length or payload will corrupt the queue.) */ shm_mq_result shm_mq_sendv(shm_mq_handle *mqh, shm_mq_iovec *iov, int iovcnt, bool nowait) { shm_mq_result res; shm_mq *mq = mqh->mqh_queue; Size nbytes = 0; Size bytes_written; int i; int which_iov = 0; Size offset; Assert(mq->mq_sender == MyProc); /* Compute total size of write. */ for (i = 0; i < iovcnt; ++i) nbytes += iov[i].len; /* Try to write, or finish writing, the length word into the buffer. */ while (!mqh->mqh_length_word_complete) { Assert(mqh->mqh_partial_bytes < sizeof(Size)); res = shm_mq_send_bytes(mqh, sizeof(Size) - mqh->mqh_partial_bytes, ((char *) &nbytes) +mqh->mqh_partial_bytes, nowait, &bytes_written); mqh->mqh_partial_bytes += bytes_written; if (res != SHM_MQ_SUCCESS) return res; if (mqh->mqh_partial_bytes >= sizeof(Size)) { Assert(mqh->mqh_partial_bytes == sizeof(Size)); mqh->mqh_partial_bytes = 0; mqh->mqh_length_word_complete = true; } /* Length word can't be split unless bigger than required alignment. */ Assert(mqh->mqh_length_word_complete || sizeof(Size) > MAXIMUM_ALIGNOF); } /* Write the actual data bytes into the buffer. */ Assert(mqh->mqh_partial_bytes <= nbytes); offset = mqh->mqh_partial_bytes; do { Size chunksize; /* Figure out which bytes need to be sent next. */ if (offset >= iov[which_iov].len) { offset -= iov[which_iov].len; ++which_iov; if (which_iov >= iovcnt) break; continue; } /* * We want to avoid copying the data if at all possible, but every * chunk of bytes we write into the queue has to be MAXALIGN'd, except * the last. Thus, if a chunk other than the last one ends on a * non-MAXALIGN'd boundary, we have to combine the tail end of its * data with data from one or more following chunks until we either * reach the last chunk or accumulate a number of bytes which is * MAXALIGN'd. */ if (which_iov + 1 < iovcnt && offset + MAXIMUM_ALIGNOF > iov[which_iov].len) { char tmpbuf[MAXIMUM_ALIGNOF]; int j = 0; for (;;) { if (offset < iov[which_iov].len) { tmpbuf[j] = iov[which_iov].data[offset]; j++; offset++; if (j == MAXIMUM_ALIGNOF) break; } else { offset -= iov[which_iov].len; which_iov++; if (which_iov >= iovcnt) break; } } res = shm_mq_send_bytes(mqh, j, tmpbuf, nowait, &bytes_written); mqh->mqh_partial_bytes += bytes_written; if (res != SHM_MQ_SUCCESS) return res; continue; } /* * If this is the last chunk, we can write all the data, even if it * isn't a multiple of MAXIMUM_ALIGNOF. Otherwise, we need to * MAXALIGN_DOWN the write size. */ chunksize = iov[which_iov].len - offset; if (which_iov + 1 < iovcnt) chunksize = MAXALIGN_DOWN(chunksize); res = shm_mq_send_bytes(mqh, chunksize, &iov[which_iov].data[offset], nowait, &bytes_written); mqh->mqh_partial_bytes += bytes_written; offset += bytes_written; if (res != SHM_MQ_SUCCESS) return res; } while (mqh->mqh_partial_bytes < nbytes); /* Reset for next message. */ mqh->mqh_partial_bytes = 0; mqh->mqh_length_word_complete = false; /* Notify receiver of the newly-written data, and return. */ return shm_mq_notify_receiver(mq); }