From 1086507e75580083aef46ad3072e9ff7869c2bc4 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 17 Aug 2015 18:10:43 +0100 Subject: [PATCH] gsocket: Fix error behaviour of g_socket_send_messages() If an error in the underlying sendmmsg() syscall occurs after successfully sending one or more messages, g_socket_send_messages() should return the number of messages successfully sent, rather than an error. This mirrors the documented sendmmsg() behaviour. This is a slight behaviour change for g_socket_send_messages(), but as it relaxes the error reporting (reporting errors in fewer situations than before), it should not cause problems. https://bugzilla.gnome.org/show_bug.cgi?id=751924 --- gio/gsocket.c | 32 +++++++++++--------------------- gio/tests/socket.c | 21 +++++++++++++++++++-- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/gio/gsocket.c b/gio/gsocket.c index a0759d411..310238ef3 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -4295,7 +4295,9 @@ g_socket_send_message (GSocket *socket, * notified of a %G_IO_OUT condition. (On Windows in particular, this is * very common due to the way the underlying APIs work.) * - * On error -1 is returned and @error is set accordingly. + * On error -1 is returned and @error is set accordingly. An error will only + * be returned if zero messages could be sent; otherwise the number of messages + * successfully sent before the error will be returned. * * Returns: number of messages sent, or -1 on error. Note that the number of * messages sent may be smaller than @num_messages if the socket is @@ -4351,7 +4353,7 @@ g_socket_send_messages_with_timeout (GSocket *socket, #if !defined (G_OS_WIN32) && defined (HAVE_SENDMMSG) { struct mmsghdr *msgvec; - gint i, num_sent, result, max_sent; + gint i, num_sent; #ifdef UIO_MAXIOV #define MAX_NUM_MESSAGES UIO_MAXIOV @@ -4383,9 +4385,7 @@ g_socket_send_messages_with_timeout (GSocket *socket, } } - num_sent = result = 0; - max_sent = num_messages; - while (num_sent < num_messages) + for (num_sent = 0; num_sent < num_messages;) { gint ret; @@ -4418,32 +4418,22 @@ g_socket_send_messages_with_timeout (GSocket *socket, continue; } - if (num_sent > 0 && - (errsv == EWOULDBLOCK || - errsv == EAGAIN)) - { - max_sent = num_sent; - break; - } + /* If any messages were successfully sent, do not error. */ + if (num_sent > 0) + break; socket_set_error_lazy (error, errsv, _("Error sending message: %s")); - /* we have to iterate over all messages below now, because we don't - * know where between num_sent and num_messages the error occured */ - max_sent = num_messages; - - result = -1; - break; + return -1; } num_sent += ret; - result = num_sent; } - for (i = 0; i < max_sent; ++i) + for (i = 0; i < num_sent; ++i) messages[i].bytes_sent = msgvec[i].msg_len; - return result; + return num_sent; } #else { diff --git a/gio/tests/socket.c b/gio/tests/socket.c index 15fb61ad4..a5b25731f 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -703,14 +703,31 @@ test_ip_sync_dgram (GSocketFamily family) m[1].bytes_sent = 0; m[2].bytes_sent = 0; - /* now try to generate an error by omitting the destination address on [1] */ + /* now try to generate an early return by omitting the destination address on [1] */ m[1].address = NULL; len = g_socket_send_messages (client, m, G_N_ELEMENTS (m), 0, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (len, ==, 1); + + g_assert_cmpint (m[0].bytes_sent, ==, 3); + g_assert_cmpint (m[1].bytes_sent, ==, 0); + g_assert_cmpint (m[2].bytes_sent, ==, 0); + + /* reset since we're re-using the message structs */ + m[0].bytes_sent = 0; + m[1].bytes_sent = 0; + m[2].bytes_sent = 0; + + /* now try to generate an error by omitting all destination addresses */ + m[0].address = NULL; + m[1].address = NULL; + m[2].address = NULL; + len = g_socket_send_messages (client, m, G_N_ELEMENTS (m), 0, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); g_clear_error (&error); g_assert_cmpint (len, ==, -1); - g_assert_cmpint (m[0].bytes_sent, ==, 3); + g_assert_cmpint (m[0].bytes_sent, ==, 0); g_assert_cmpint (m[1].bytes_sent, ==, 0); g_assert_cmpint (m[2].bytes_sent, ==, 0);