Commit f7b15f14 authored by Philip Withnall's avatar Philip Withnall Committed by Olivier Crête

agent: Add support for vectored I/O for sends

Add one new public function, nice_agent_send_messages_nonblocking(),
which replaces nice_agent_send_full(). This isn’t an API break, because
nice_agent_send_full() hasn’t been in a release yet. The new API allows
sending multiple messages in a single call, and supports vectors of
buffers to transmit the messages from.

The existing nice_agent_send() API has been left untouched, although
it’s a bit of a bugbear because it’s non-blocking and doesn’t fit with
the new *_nonblocking() naming scheme. Oh well.

This doesn’t bring any notable changes to the number of memcpy()s on the
critical path: it remains at zero for the common cases and common socket
types. It introduces the possibility for future work to eliminate some
memcpy()s in more complex socket types, like tcp-turn and tcp-bsd, but
these optimisations have not been made yet. FIXME comments have been
added.

This includes modifications to the test-send-recv unit test to cover the
new API.
parent 515481e6
This diff is collapsed.
......@@ -659,28 +659,29 @@ nice_agent_send (
const gchar *buf);
/**
* nice_agent_send_full:
* nice_agent_send_messages_nonblocking:
* @agent: a #NiceAgent
* @stream_id: the ID of the stream to send to
* @component_id: the ID of the component to send to
* @buf: (array length=buf_len): data to transmit, of at least @buf_len bytes in
* size
* @buf_len: length of valid data in @buf, in bytes
* @messages: (array length=n_messages): array of messages to send, of at least
* @n_messages entries in length
* @n_messages: number of entries in @messages
* @cancellable: (allow-none): a #GCancellable to cancel the operation from
* another thread, or %NULL
* @error: (allow-none): return location for a #GError, or %NULL
*
* Sends the data in @buf on the socket identified by the given stream/component
* pair. Transmission is non-blocking, so a %G_IO_ERROR_WOULD_BLOCK error may be
* returned if the send buffer is full.
* Sends multiple messages on the socket identified by the given
* stream/component pair. Transmission is non-blocking, so a
* %G_IO_ERROR_WOULD_BLOCK error may be returned if the send buffer is full.
*
* As with nice_agent_send(), the given component must be in
* %NICE_COMPONENT_STATE_READY or, as a special case, in any state if it was
* previously ready and was then restarted.
*
* On success, the number of bytes written to the socket will be returned (which
* will always be @buf_len when in non-reliable mode, and may be less than
* @buf_len when in reliable mode).
* On success, the number of messages written to the socket will be returned,
* which may be less than @n_messages if transmission would have blocked
* part-way through. Zero will be returned if @n_messages is zero, or if
* transmission would have blocked on the first message.
*
* On failure, -1 will be returned and @error will be set. If the #NiceAgent is
* reliable and the socket is not yet connected, %G_IO_ERROR_BROKEN_PIPE will be
......@@ -690,18 +691,17 @@ nice_agent_send (
* invalid or not yet connected, %G_IO_ERROR_BROKEN_PIPE will be returned.
* %G_IO_ERROR_FAILED will be returned for other errors.
*
* Returns: the number of bytes sent (guaranteed to be greater than 0), or -1 on
* error
* Returns: the number of messages sent (may be zero), or -1 on error
*
* Since: 0.1.5
*/
gssize
nice_agent_send_full (
gint
nice_agent_send_messages_nonblocking (
NiceAgent *agent,
guint stream_id,
guint component_id,
const guint8 *buf,
gsize buf_len,
const NiceOutputMessage *messages,
guint n_messages,
GCancellable *cancellable,
GError **error);
......
......@@ -348,7 +348,8 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count
GCancellable *cancellable, GError **error)
{
NiceOutputStream *self = NICE_OUTPUT_STREAM (stream);
gssize len = -1, _len;
gssize len = -1;
gint n_sent_messages;
GError *child_error = NULL;
NiceAgent *agent = NULL; /* owned */
gulong cancel_id = 0, writeable_id;
......@@ -401,6 +402,11 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count
do {
GOutputVector local_buf = { (const guint8 *) buffer + len, count - len };
NiceOutputMessage local_message = {
&local_buf, 1, NULL, count - len
};
/* Have to unlock while calling into the agent because
* it will take the agent lock which will cause a deadlock if one of
* the callbacks is called.
......@@ -408,23 +414,24 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count
write_data->writable = FALSE;
g_mutex_unlock (&write_data->mutex);
_len = nice_agent_send_full (agent, self->priv->stream_id,
self->priv->component_id, (guint8 *) buffer + len, count - len,
n_sent_messages = nice_agent_send_messages_nonblocking (agent,
self->priv->stream_id, self->priv->component_id, &local_message, 1,
cancellable, &child_error);
g_mutex_lock (&write_data->mutex);
if (_len == -1 &&
if (n_sent_messages == -1 &&
g_error_matches (child_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
/* EWOULDBLOCK. */
g_clear_error (&child_error);
if (!write_data->writable && !write_data->error)
g_cond_wait (&write_data->cond, &write_data->mutex);
} else if (_len > 0) {
} else if (n_sent_messages > 0) {
/* Success. */
len += _len;
len = count;
} else {
/* Other error. */
len = _len;
len = n_sent_messages;
break;
}
} while ((gsize) len < count);
......@@ -517,7 +524,9 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream,
{
NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
NiceAgent *agent; /* owned */
gssize len;
GOutputVector local_buf = { buffer, count };
NiceOutputMessage local_message = { &local_buf, 1, NULL, count };
gint n_sent_messages;
/* Closed streams are not writeable. */
if (g_output_stream_is_closed (G_OUTPUT_STREAM (stream))) {
......@@ -544,12 +553,12 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream,
return -1;
}
len = nice_agent_send_full (agent, priv->stream_id, priv->component_id,
buffer, count, NULL, error);
n_sent_messages = nice_agent_send_messages_nonblocking (agent,
priv->stream_id, priv->component_id, &local_message, 1, NULL, error);
g_object_unref (agent);
return len;
return (n_sent_messages == 1) ? (gssize) count : n_sent_messages;
}
static GSource *
......
......@@ -1875,3 +1875,15 @@ pseudo_tcp_socket_can_send (PseudoTcpSocket *self)
return (pseudo_tcp_fifo_get_write_remaining (&priv->sbuf) != 0);
}
gsize
pseudo_tcp_socket_get_available_send_space (PseudoTcpSocket *self)
{
PseudoTcpSocketPrivate *priv = self->priv;
if (priv->state != TCP_ESTABLISHED) {
return 0;
}
return pseudo_tcp_fifo_get_write_remaining (&priv->sbuf);
}
......@@ -458,6 +458,18 @@ gint pseudo_tcp_socket_get_available_bytes (PseudoTcpSocket *self);
gboolean pseudo_tcp_socket_can_send (PseudoTcpSocket *self);
/**
* pseudo_tcp_socket_get_available_send_space:
* @self: The #PseudoTcpSocket object.
*
* Gets the number of bytes of space available in the transmission buffer.
*
* Returns: The numbero f bytes, or 0 if the connection is not established.
*
* Since: 0.1.5
*/
gsize pseudo_tcp_socket_get_available_send_space (PseudoTcpSocket *self);
G_END_DECLS
#endif /* _PSEUDOTCP_H */
......
......@@ -24,7 +24,7 @@ nice_agent_get_remote_candidates
nice_agent_get_local_candidates
nice_agent_get_selected_pair
nice_agent_send
nice_agent_send_full
nice_agent_send_messages_nonblocking
nice_agent_recv
nice_agent_recv_messages
nice_agent_recv_nonblocking
......
......@@ -42,7 +42,7 @@ nice_agent_parse_remote_stream_sdp
nice_agent_remove_stream
nice_agent_restart
nice_agent_send
nice_agent_send_full
nice_agent_send_messages_nonblocking
nice_agent_set_port_range
nice_agent_set_relay_info
nice_agent_set_remote_candidates
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment