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 ( ...@@ -659,28 +659,29 @@ nice_agent_send (
const gchar *buf); const gchar *buf);
/** /**
* nice_agent_send_full: * nice_agent_send_messages_nonblocking:
* @agent: a #NiceAgent * @agent: a #NiceAgent
* @stream_id: the ID of the stream to send to * @stream_id: the ID of the stream to send to
* @component_id: the ID of the component 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 * @messages: (array length=n_messages): array of messages to send, of at least
* size * @n_messages entries in length
* @buf_len: length of valid data in @buf, in bytes * @n_messages: number of entries in @messages
* @cancellable: (allow-none): a #GCancellable to cancel the operation from * @cancellable: (allow-none): a #GCancellable to cancel the operation from
* another thread, or %NULL * another thread, or %NULL
* @error: (allow-none): return location for a #GError, 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 * Sends multiple messages on the socket identified by the given
* pair. Transmission is non-blocking, so a %G_IO_ERROR_WOULD_BLOCK error may be * stream/component pair. Transmission is non-blocking, so a
* returned if the send buffer is full. * %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 * 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 * %NICE_COMPONENT_STATE_READY or, as a special case, in any state if it was
* previously ready and was then restarted. * previously ready and was then restarted.
* *
* On success, the number of bytes written to the socket will be returned (which * On success, the number of messages written to the socket will be returned,
* will always be @buf_len when in non-reliable mode, and may be less than * which may be less than @n_messages if transmission would have blocked
* @buf_len when in reliable mode). * 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 * 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 * reliable and the socket is not yet connected, %G_IO_ERROR_BROKEN_PIPE will be
...@@ -690,18 +691,17 @@ nice_agent_send ( ...@@ -690,18 +691,17 @@ nice_agent_send (
* invalid or not yet connected, %G_IO_ERROR_BROKEN_PIPE will be returned. * invalid or not yet connected, %G_IO_ERROR_BROKEN_PIPE will be returned.
* %G_IO_ERROR_FAILED will be returned for other errors. * %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 * Returns: the number of messages sent (may be zero), or -1 on error
* error
* *
* Since: 0.1.5 * Since: 0.1.5
*/ */
gssize gint
nice_agent_send_full ( nice_agent_send_messages_nonblocking (
NiceAgent *agent, NiceAgent *agent,
guint stream_id, guint stream_id,
guint component_id, guint component_id,
const guint8 *buf, const NiceOutputMessage *messages,
gsize buf_len, guint n_messages,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
......
...@@ -348,7 +348,8 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count ...@@ -348,7 +348,8 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count
GCancellable *cancellable, GError **error) GCancellable *cancellable, GError **error)
{ {
NiceOutputStream *self = NICE_OUTPUT_STREAM (stream); NiceOutputStream *self = NICE_OUTPUT_STREAM (stream);
gssize len = -1, _len; gssize len = -1;
gint n_sent_messages;
GError *child_error = NULL; GError *child_error = NULL;
NiceAgent *agent = NULL; /* owned */ NiceAgent *agent = NULL; /* owned */
gulong cancel_id = 0, writeable_id; gulong cancel_id = 0, writeable_id;
...@@ -401,6 +402,11 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count ...@@ -401,6 +402,11 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count
do { 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 /* Have to unlock while calling into the agent because
* it will take the agent lock which will cause a deadlock if one of * it will take the agent lock which will cause a deadlock if one of
* the callbacks is called. * the callbacks is called.
...@@ -408,23 +414,24 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count ...@@ -408,23 +414,24 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count
write_data->writable = FALSE; write_data->writable = FALSE;
g_mutex_unlock (&write_data->mutex); g_mutex_unlock (&write_data->mutex);
_len = nice_agent_send_full (agent, self->priv->stream_id, n_sent_messages = nice_agent_send_messages_nonblocking (agent,
self->priv->component_id, (guint8 *) buffer + len, count - len, self->priv->stream_id, self->priv->component_id, &local_message, 1,
cancellable, &child_error); cancellable, &child_error);
g_mutex_lock (&write_data->mutex); 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)) { g_error_matches (child_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
/* EWOULDBLOCK. */ /* EWOULDBLOCK. */
g_clear_error (&child_error); g_clear_error (&child_error);
if (!write_data->writable && !write_data->error) if (!write_data->writable && !write_data->error)
g_cond_wait (&write_data->cond, &write_data->mutex); g_cond_wait (&write_data->cond, &write_data->mutex);
} else if (_len > 0) { } else if (n_sent_messages > 0) {
/* Success. */ /* Success. */
len += _len; len = count;
} else { } else {
/* Other error. */ /* Other error. */
len = _len; len = n_sent_messages;
break; break;
} }
} while ((gsize) len < count); } while ((gsize) len < count);
...@@ -517,7 +524,9 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream, ...@@ -517,7 +524,9 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream,
{ {
NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
NiceAgent *agent; /* owned */ 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. */ /* Closed streams are not writeable. */
if (g_output_stream_is_closed (G_OUTPUT_STREAM (stream))) { if (g_output_stream_is_closed (G_OUTPUT_STREAM (stream))) {
...@@ -544,12 +553,12 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream, ...@@ -544,12 +553,12 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream,
return -1; return -1;
} }
len = nice_agent_send_full (agent, priv->stream_id, priv->component_id, n_sent_messages = nice_agent_send_messages_nonblocking (agent,
buffer, count, NULL, error); priv->stream_id, priv->component_id, &local_message, 1, NULL, error);
g_object_unref (agent); g_object_unref (agent);
return len; return (n_sent_messages == 1) ? (gssize) count : n_sent_messages;
} }
static GSource * static GSource *
......
...@@ -1875,3 +1875,15 @@ pseudo_tcp_socket_can_send (PseudoTcpSocket *self) ...@@ -1875,3 +1875,15 @@ pseudo_tcp_socket_can_send (PseudoTcpSocket *self)
return (pseudo_tcp_fifo_get_write_remaining (&priv->sbuf) != 0); 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); ...@@ -458,6 +458,18 @@ gint pseudo_tcp_socket_get_available_bytes (PseudoTcpSocket *self);
gboolean pseudo_tcp_socket_can_send (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 G_END_DECLS
#endif /* _PSEUDOTCP_H */ #endif /* _PSEUDOTCP_H */
......
...@@ -24,7 +24,7 @@ nice_agent_get_remote_candidates ...@@ -24,7 +24,7 @@ nice_agent_get_remote_candidates
nice_agent_get_local_candidates nice_agent_get_local_candidates
nice_agent_get_selected_pair nice_agent_get_selected_pair
nice_agent_send nice_agent_send
nice_agent_send_full nice_agent_send_messages_nonblocking
nice_agent_recv nice_agent_recv
nice_agent_recv_messages nice_agent_recv_messages
nice_agent_recv_nonblocking nice_agent_recv_nonblocking
......
...@@ -42,7 +42,7 @@ nice_agent_parse_remote_stream_sdp ...@@ -42,7 +42,7 @@ nice_agent_parse_remote_stream_sdp
nice_agent_remove_stream nice_agent_remove_stream
nice_agent_restart nice_agent_restart
nice_agent_send nice_agent_send
nice_agent_send_full nice_agent_send_messages_nonblocking
nice_agent_set_port_range nice_agent_set_port_range
nice_agent_set_relay_info nice_agent_set_relay_info
nice_agent_set_remote_candidates 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