/* * Write a message into a shared message queue. * * 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_send(shm_mq_handle *mqh, Size nbytes, void *data, bool nowait) { shm_mq_result res; shm_mq *mq = mqh->mqh_queue; Size bytes_written; Assert(mq->mq_sender == MyProc); /* 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); res = shm_mq_send_bytes(mqh, nbytes - mqh->mqh_partial_bytes, ((char *) data) + mqh->mqh_partial_bytes, nowait, &bytes_written); if (res == SHM_MQ_WOULD_BLOCK) mqh->mqh_partial_bytes += bytes_written; else { mqh->mqh_partial_bytes = 0; mqh->mqh_length_word_complete = false; } if (res != SHM_MQ_SUCCESS) return res; /* Notify receiver of the newly-written data, and return. */ return shm_mq_notify_receiver(mq); }
/* * Write bytes into a shared message queue. */ static shm_mq_result shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, void *data, bool nowait, Size *bytes_written) { shm_mq *mq = mqh->mqh_queue; Size sent = 0; uint64 used; Size ringsize = mq->mq_ring_size; Size available; while (sent < nbytes) { bool detached; uint64 rb; /* Compute number of ring buffer bytes used and available. */ rb = shm_mq_get_bytes_read(mq, &detached); Assert(mq->mq_bytes_written >= rb); used = mq->mq_bytes_written - rb; Assert(used <= ringsize); available = Min(ringsize - used, nbytes - sent); /* Bail out if the queue has been detached. */ if (detached) return SHM_MQ_DETACHED; if (available == 0) { shm_mq_result res; /* * The queue is full, so if the receiver isn't yet known to be * attached, we must wait for that to happen. */ if (!mqh->mqh_counterparty_attached) { if (nowait) { if (shm_mq_get_receiver(mq) == NULL) return SHM_MQ_WOULD_BLOCK; } else if (!shm_mq_wait_internal(mq, &mq->mq_receiver, mqh->mqh_handle)) { mq->mq_detached = true; return SHM_MQ_DETACHED; } mqh->mqh_counterparty_attached = true; } /* Let the receiver know that we need them to read some data. */ res = shm_mq_notify_receiver(mq); if (res != SHM_MQ_SUCCESS) { *bytes_written = sent; return res; } /* Skip manipulation of our latch if nowait = true. */ if (nowait) { *bytes_written = sent; return SHM_MQ_WOULD_BLOCK; } /* * Wait for our latch to be set. It might already be set for * some unrelated reason, but that'll just result in one extra * trip through the loop. It's worth it to avoid resetting the * latch at top of loop, because setting an already-set latch is * much cheaper than setting one that has been reset. */ WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0); /* An interrupt may have occurred while we were waiting. */ CHECK_FOR_INTERRUPTS(); /* Reset the latch so we don't spin. */ ResetLatch(&MyProc->procLatch); } else { Size offset = mq->mq_bytes_written % (uint64) ringsize; Size sendnow = Min(available, ringsize - offset); /* Write as much data as we can via a single memcpy(). */ memcpy(&mq->mq_ring[mq->mq_ring_offset + offset], (char *) data + sent, sendnow); sent += sendnow; /* * Update count of bytes written, with alignment padding. Note * that this will never actually insert any padding except at the * end of a run of bytes, because the buffer size is a multiple of * MAXIMUM_ALIGNOF, and each read is as well. */ Assert(sent == nbytes || sendnow == MAXALIGN(sendnow)); shm_mq_inc_bytes_written(mq, MAXALIGN(sendnow)); /* * For efficiency, we don't set the reader's latch here. We'll * do that only when the buffer fills up or after writing an * entire message. */ } } *bytes_written = sent; return SHM_MQ_SUCCESS; }
/* * 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); }