return message_data_reserve(ms, m, cleaned_up);
}
+/** Cancel a reservation made by fr_message_and_data_reserve(), returning the slot to the set.
+ *
+ * Both rings are uncommitted at this point (write_offset was not advanced).
+ * Clearing the message fields is sufficient: the next reserve overwrites the
+ * slot, and the data ring's reserved field is replaced by the new reservation size.
+ *
+ * @param[in] ms the message set the reservation was made against.
+ * @param[in] m the previously reserved message to cancel.
+ */
+void fr_message_and_data_reset(fr_message_set_t *ms, fr_message_t *m)
+{
+ (void) talloc_get_type_abort(ms, fr_message_set_t);
+ fr_assert(m->status == FR_MESSAGE_USED);
+ m->status = FR_MESSAGE_FREE;
+ m->rb = NULL;
+ m->data = NULL;
+ m->data_size = 0;
+ m->rb_size = 0;
+}
+
/** Commit a previously reserved message, allocating exactly total_size bytes of packet data.
*
* Commits both the message ring slot and the data ring buffer. total_size is the
fr_message_set_t *fr_message_set_create(TALLOC_CTX *ctx, int num_messages, size_t message_size, size_t ring_buffer_size, bool unlimited_size) CC_HINT(nonnull);
fr_message_t *fr_message_and_data_reserve(fr_message_set_t *ms, size_t reserve_size) CC_HINT(nonnull);
+void fr_message_and_data_reset(fr_message_set_t *ms, fr_message_t *m) CC_HINT(nonnull);
fr_message_t *fr_message_and_data_commit(fr_message_set_t *ms, fr_message_t *m, size_t total_size) CC_HINT(nonnull(1));
fr_message_t *fr_message_and_data_alloc(fr_message_set_t *ms, size_t size) CC_HINT(nonnull);
fr_message_t *fr_message_and_data_commit_with_leftover(fr_message_set_t *ms, fr_message_t *m, size_t actual_packet_size,
cd->m.data, cd->m.rb_size, &s->leftover);
if (data_size == 0) {
/*
- * Cache the message for later. This is
- * important for stream sockets, which can do
- * partial reads into the current buffer. We
- * need to be able to give the same buffer back
- * to the stream socket for subsequent reads.
+ * Cache the message only when there are leftover
+ * bytes from a partial stream read. The buffer
+ * must be handed back on the next call so the
+ * stream can append to it.
*
- * Since we have a message set for each
- * fr_io_socket_t, no "head of line"
- * blocking issues can happen for stream sockets.
+ * Without leftover bytes the reservation is
+ * discarded; the next call makes a fresh one.
+ * Holding an uncommitted reservation is unsafe:
+ * any allocation on the same message set (e.g.
+ * from a callback inside app_io->read) returns
+ * the same ring-buffer slot and zeroes it out.
*/
- s->cd = cd;
+ if (s->leftover) {
+ s->cd = cd;
+ } else {
+ fr_message_and_data_reset(s->ms, &cd->m);
+ s->cd = NULL;
+ }
return;
}