/**
* Write to a channel the given packed cell.
*
- * Return 0 on success and the cell is freed so the caller MUST discard any
- * reference to it. On error, -1 is returned and the cell is untouched.
+ * Return 0 on success or -1 on error.
+ *
+ * Two possible errors can happen. Either the channel is not opened or the
+ * lower layer (specialized channel) failed to write it. In both cases, it is
+ * the caller responsability to free the cell.
*/
static int
write_packed_cell(channel_t *chan, packed_cell_t *cell)
* Write a packed cell to a channel using the write_cell() method. This is
* called by the transport-independent code to deliver a packed cell to a
* channel for transmission.
+ *
+ * Return 0 on success else a negative value. In both cases, the caller should
+ * not access the cell anymore, it is freed both on success and error.
*/
int
channel_write_packed_cell(channel_t *chan, packed_cell_t *cell)
{
+ int ret = -1;
+
tor_assert(chan);
tor_assert(cell);
log_debug(LD_CHANNEL, "Discarding %p on closing channel %p with "
"global ID "U64_FORMAT, cell, chan,
U64_PRINTF_ARG(chan->global_identifier));
- tor_free(cell);
- return -1;
+ goto end;
}
log_debug(LD_CHANNEL,
"Writing %p to channel %p with global ID "
U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier));
- return write_packed_cell(chan, cell);
+ ret = write_packed_cell(chan, cell);
+
+ end:
+ /* Whatever happens, we free the cell. Either an error occured or the cell
+ * was put on the connection outbuf, both cases we have ownership of the
+ * cell and we free it. */
+ packed_cell_free(cell);
+ return ret;
}
/**
*
* This implements the write_packed_cell method for channel_tls_t; given a
* channel_tls_t and a packed_cell_t, transmit the packed_cell_t.
+ *
+ * Return 0 on success or negative value on error. The caller must free the
+ * packed cell.
*/
static int
tor_assert(chan);
channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
size_t cell_network_size = get_cell_network_size(chan->wide_circ_ids);
- int written = 0;
tor_assert(tlschan);
tor_assert(packed_cell);
if (tlschan->conn) {
connection_buf_add(packed_cell->body, cell_network_size,
TO_CONN(tlschan->conn));
-
- /* This is where the cell is finished; used to be done from relay.c */
- packed_cell_free(packed_cell);
- ++written;
} else {
log_info(LD_CHANNEL,
"something called write_packed_cell on a tlschan "
"(%p with ID " U64_FORMAT " but no conn",
chan, U64_PRINTF_ARG(chan->global_identifier));
+ return -1;
}
- return written;
+ return 0;
}
/**
return cell;
}
-/** Insert <b>cell</b> as the head of the <b>queue</b>. */
-static void
-cell_insert_head(cell_queue_t *queue, packed_cell_t *cell)
-{
- tor_assert(queue);
- tor_assert(cell);
-
- TOR_SIMPLEQ_INSERT_HEAD(&queue->head, cell, next);
- ++queue->n;
-}
-
/** Return the total number of bytes used for each packed_cell in a queue.
* Approximate. */
size_t
/* this code is duplicated from some of the logic below. Ugly! XXXX */
tor_assert(destroy_queue->n > 0);
cell = cell_queue_pop(destroy_queue);
+ /* Send the DESTROY cell. It is very unlikely that this fails but just
+ * in case, get rid of the channel. */
if (channel_write_packed_cell(chan, cell) < 0) {
- cell_insert_head(destroy_queue, cell);
+ /* The cell has been freed. */
+ channel_mark_for_close(chan);
continue;
}
/* Update the cmux destroy counter */
DIRREQ_TUNNELED,
DIRREQ_CIRC_QUEUE_FLUSHED);
- /* Now send the cell */
+ /* Now send the cell. It is very unlikely that this fails but just in
+ * case, get rid of the channel. */
if (channel_write_packed_cell(chan, cell) < 0) {
- /* Unable to send the cell, put it back at the start of the circuit
- * queue so we can retry. */
- cell_insert_head(queue, cell);
+ /* The cell has been freed at this point. */
+ channel_mark_for_close(chan);
continue;
}
cell = NULL;
if (test_chan_accept_cells) {
/* Free the cell and bump the counter */
- packed_cell_free(packed_cell);
++test_cells_written;
rv = 1;
}
done:
free_fake_channel(ch1);
free_fake_channel(ch2);
- tor_free(p_cell);
UNMOCK(scheduler_channel_doesnt_want_writes);
UNMOCK(scheduler_release_channel);