From 547311bfd8661e25e588e1f434f15c5f2f32c3a7 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Thu, 24 Jun 2010 13:09:14 -0400 Subject: [PATCH] Always do async vs sync correctly in GSocketConnection streams Previously if a GSocketConnection had a blocking GSocket, it would sometimes block during asynchonous I/O, and if it had a non-blocking socket, it would sometimes return G_IO_ERROR_WOULD_BLOCK from synchronous I/O. This fixes the connection to not depend on the socket state. https://bugzilla.gnome.org/show_bug.cgi?id=616458 --- docs/reference/gio/gio-sections.txt | 2 ++ gio/gio.symbols | 2 ++ gio/gsocket.c | 70 ++++++++++++++++++++++++++++++++++--- gio/gsocket.h | 13 +++++++ gio/gsocketinputstream.c | 16 +++++---- gio/gsocketoutputstream.c | 16 +++++---- 6 files changed, 101 insertions(+), 18 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 7ed6cfd..cd23dea 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1716,9 +1716,11 @@ g_socket_check_connect_result g_socket_receive g_socket_receive_from g_socket_receive_message +g_socket_receive_with_blocking g_socket_send g_socket_send_to g_socket_send_message +g_socket_send_with_blocking g_socket_close g_socket_is_closed g_socket_shutdown diff --git a/gio/gio.symbols b/gio/gio.symbols index 68ea6d4..eb23edd 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1271,9 +1271,11 @@ g_socket_new_from_fd g_socket_receive g_socket_receive_from g_socket_receive_message +g_socket_receive_with_blocking g_socket_send g_socket_send_message g_socket_send_to +g_socket_send_with_blocking g_socket_set_blocking g_socket_set_timeout g_socket_set_keepalive diff --git a/gio/gsocket.c b/gio/gsocket.c index 3a26a7d..880ba38 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -1722,6 +1722,37 @@ g_socket_receive (GSocket *socket, GCancellable *cancellable, GError **error) { + return g_socket_receive_with_blocking (socket, buffer, size, + socket->priv->blocking, + cancellable, error); +} + +/** + * g_socket_receive_with_blocking: + * @socket: a #GSocket + * @buffer: a buffer to read data into (which should be at least @size + * bytes long). + * @size: the number of bytes you want to read from the socket + * @blocking: whether to do blocking or non-blocking I/O + * @cancellable: a %GCancellable or %NULL + * @error: #GError for error reporting, or %NULL to ignore. + * + * This behaves exactly the same as g_socket_receive(), except that + * the choice of blocking or non-blocking behavior is determined by + * the @blocking argument rather than by @socket's properties. + * + * Returns: Number of bytes read, or -1 on error + * + * Since: 2.26 + */ +gssize +g_socket_receive_with_blocking (GSocket *socket, + gchar *buffer, + gsize size, + gboolean blocking, + GCancellable *cancellable, + GError **error) +{ gssize ret; g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE); @@ -1734,7 +1765,7 @@ g_socket_receive (GSocket *socket, while (1) { - if (socket->priv->blocking && + if (blocking && !g_socket_condition_wait (socket, G_IO_IN, cancellable, error)) return -1; @@ -1746,7 +1777,7 @@ g_socket_receive (GSocket *socket, if (errsv == EINTR) continue; - if (socket->priv->blocking) + if (blocking) { #ifdef WSAEWOULDBLOCK if (errsv == WSAEWOULDBLOCK) @@ -1862,6 +1893,37 @@ g_socket_send (GSocket *socket, GCancellable *cancellable, GError **error) { + return g_socket_send_with_blocking (socket, buffer, size, + socket->priv->blocking, + cancellable, error); +} + +/** + * g_socket_send_with_blocking: + * @socket: a #GSocket + * @buffer: the buffer containing the data to send. + * @size: the number of bytes to send + * @blocking: whether to do blocking or non-blocking I/O + * @cancellable: a %GCancellable or %NULL + * @error: #GError for error reporting, or %NULL to ignore. + * + * This behaves exactly the same as g_socket_send(), except that + * the choice of blocking or non-blocking behavior is determined by + * the @blocking argument rather than by @socket's properties. + * + * Returns: Number of bytes written (which may be less than @size), or -1 + * on error + * + * Since: 2.26 + */ +gssize +g_socket_send_with_blocking (GSocket *socket, + const gchar *buffer, + gsize size, + gboolean blocking, + GCancellable *cancellable, + GError **error) +{ gssize ret; g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE); @@ -1874,7 +1936,7 @@ g_socket_send (GSocket *socket, while (1) { - if (socket->priv->blocking && + if (blocking && !g_socket_condition_wait (socket, G_IO_OUT, cancellable, error)) return -1; @@ -1891,7 +1953,7 @@ g_socket_send (GSocket *socket, win32_unset_event_mask (socket, FD_WRITE); #endif - if (socket->priv->blocking) + if (blocking) { #ifdef WSAEWOULDBLOCK if (errsv == WSAEWOULDBLOCK) diff --git a/gio/gsocket.h b/gio/gsocket.h index b2476cc..d17de32 100644 --- a/gio/gsocket.h +++ b/gio/gsocket.h @@ -176,6 +176,19 @@ gboolean g_socket_speaks_ipv4 (GSocket GCredentials *g_socket_get_credentials (GSocket *socket, GError **error); +gssize g_socket_receive_with_blocking (GSocket *socket, + gchar *buffer, + gsize size, + gboolean blocking, + GCancellable *cancellable, + GError **error); +gssize g_socket_send_with_blocking (GSocket *socket, + const gchar *buffer, + gsize size, + gboolean blocking, + GCancellable *cancellable, + GError **error); + G_END_DECLS #endif /* __G_SOCKET_H__ */ diff --git a/gio/gsocketinputstream.c b/gio/gsocketinputstream.c index 4b26ed4..07c4740 100644 --- a/gio/gsocketinputstream.c +++ b/gio/gsocketinputstream.c @@ -111,8 +111,9 @@ g_socket_input_stream_read (GInputStream *stream, { GSocketInputStream *input_stream = G_SOCKET_INPUT_STREAM (stream); - return g_socket_receive (input_stream->priv->socket, buffer, count, - cancellable, error); + return g_socket_receive_with_blocking (input_stream->priv->socket, + buffer, count, TRUE, + cancellable, error); } static gboolean @@ -124,11 +125,12 @@ g_socket_input_stream_read_ready (GSocket *socket, GError *error = NULL; gssize result; - result = g_socket_receive (stream->priv->socket, - stream->priv->buffer, - stream->priv->count, - stream->priv->cancellable, - &error); + result = g_socket_receive_with_blocking (stream->priv->socket, + stream->priv->buffer, + stream->priv->count, + FALSE, + stream->priv->cancellable, + &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) return TRUE; diff --git a/gio/gsocketoutputstream.c b/gio/gsocketoutputstream.c index e79e261..b5d9412 100644 --- a/gio/gsocketoutputstream.c +++ b/gio/gsocketoutputstream.c @@ -113,8 +113,9 @@ g_socket_output_stream_write (GOutputStream *stream, { GSocketOutputStream *onput_stream = G_SOCKET_OUTPUT_STREAM (stream); - return g_socket_send (onput_stream->priv->socket, buffer, count, - cancellable, error); + return g_socket_send_with_blocking (onput_stream->priv->socket, + buffer, count, TRUE, + cancellable, error); } static gboolean @@ -126,11 +127,12 @@ g_socket_output_stream_write_ready (GSocket *socket, GError *error = NULL; gssize result; - result = g_socket_send (stream->priv->socket, - stream->priv->buffer, - stream->priv->count, - stream->priv->cancellable, - &error); + result = g_socket_send_with_blocking (stream->priv->socket, + stream->priv->buffer, + stream->priv->count, + FALSE, + stream->priv->cancellable, + &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) return TRUE; -- 2.7.4