mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-09-20 09:16:15 +02:00
Fix g_socket_get_available() with TCP on Windows
Windows needs a special inefficient hack to implement g_socket_get_available() correctly for UDP sockets, but that hack isn't needed for TCP, and in fact, might give the wrong answer in that case. Fix it to only use the hack with UDP. Also, fix that case to handle non-blocking sockets as well. And add a test case for g_socket_get_available() with TCP. https://bugzilla.gnome.org/show_bug.cgi?id=723422
This commit is contained in:
parent
d690b3dcd0
commit
074df39681
@ -2478,8 +2478,10 @@ g_socket_get_available_bytes (GSocket *socket)
|
|||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
const gint bufsize = 64 * 1024;
|
const gint bufsize = 64 * 1024;
|
||||||
static guchar *buf = NULL;
|
static guchar *buf = NULL;
|
||||||
#endif
|
u_long avail;
|
||||||
|
#else
|
||||||
gint avail;
|
gint avail;
|
||||||
|
#endif
|
||||||
|
|
||||||
g_return_val_if_fail (G_IS_SOCKET (socket), -1);
|
g_return_val_if_fail (G_IS_SOCKET (socket), -1);
|
||||||
|
|
||||||
@ -2490,10 +2492,20 @@ g_socket_get_available_bytes (GSocket *socket)
|
|||||||
if (ioctl (socket->priv->fd, FIONREAD, &avail) < 0)
|
if (ioctl (socket->priv->fd, FIONREAD, &avail) < 0)
|
||||||
avail = -1;
|
avail = -1;
|
||||||
#else
|
#else
|
||||||
if (G_UNLIKELY (g_once_init_enter (&buf)))
|
if (socket->priv->type == G_SOCKET_TYPE_DATAGRAM)
|
||||||
g_once_init_leave (&buf, g_malloc (bufsize));
|
{
|
||||||
|
if (G_UNLIKELY (g_once_init_enter (&buf)))
|
||||||
|
g_once_init_leave (&buf, g_malloc (bufsize));
|
||||||
|
|
||||||
avail = recv (socket->priv->fd, buf, bufsize, MSG_PEEK);
|
avail = recv (socket->priv->fd, buf, bufsize, MSG_PEEK);
|
||||||
|
if (avail == -1 && get_socket_errno () == WSAEWOULDBLOCK)
|
||||||
|
avail = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ioctlsocket (socket->priv->fd, FIONREAD, &avail) < 0)
|
||||||
|
avail = -1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return avail;
|
return avail;
|
||||||
|
@ -896,41 +896,63 @@ test_reuse_udp (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_datagram_get_available (void)
|
test_get_available (gconstpointer user_data)
|
||||||
{
|
{
|
||||||
|
GSocketType socket_type = GPOINTER_TO_UINT (user_data);
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
GSocket *server, *client;
|
GSocket *listener, *server, *client;
|
||||||
GInetAddress *addr;
|
GInetAddress *addr;
|
||||||
GSocketAddress *saddr;
|
GSocketAddress *saddr;
|
||||||
gchar data[] = "0123456789abcdef";
|
gchar data[] = "0123456789abcdef";
|
||||||
gchar buf[34];
|
gchar buf[34];
|
||||||
gssize nread;
|
gssize nread;
|
||||||
|
|
||||||
server = g_socket_new (G_SOCKET_FAMILY_IPV4,
|
listener = g_socket_new (G_SOCKET_FAMILY_IPV4,
|
||||||
G_SOCKET_TYPE_DATAGRAM,
|
socket_type,
|
||||||
G_SOCKET_PROTOCOL_DEFAULT,
|
G_SOCKET_PROTOCOL_DEFAULT,
|
||||||
&err);
|
&err);
|
||||||
g_assert_no_error (err);
|
g_assert_no_error (err);
|
||||||
g_assert (G_IS_SOCKET (server));
|
g_assert (G_IS_SOCKET (listener));
|
||||||
|
|
||||||
client = g_socket_new (G_SOCKET_FAMILY_IPV4,
|
client = g_socket_new (G_SOCKET_FAMILY_IPV4,
|
||||||
G_SOCKET_TYPE_DATAGRAM,
|
socket_type,
|
||||||
G_SOCKET_PROTOCOL_DEFAULT,
|
G_SOCKET_PROTOCOL_DEFAULT,
|
||||||
&err);
|
&err);
|
||||||
g_assert_no_error (err);
|
g_assert_no_error (err);
|
||||||
g_assert (G_IS_SOCKET (client));
|
g_assert (G_IS_SOCKET (client));
|
||||||
|
|
||||||
|
if (socket_type == G_SOCKET_TYPE_STREAM)
|
||||||
|
{
|
||||||
|
g_socket_set_option (client, IPPROTO_TCP, TCP_NODELAY, TRUE, &err);
|
||||||
|
g_assert_no_error (err);
|
||||||
|
}
|
||||||
|
|
||||||
addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
|
addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
|
||||||
saddr = g_inet_socket_address_new (addr, 0);
|
saddr = g_inet_socket_address_new (addr, 0);
|
||||||
|
|
||||||
g_socket_bind (server, saddr, TRUE, &err);
|
g_socket_bind (listener, saddr, TRUE, &err);
|
||||||
g_assert_no_error (err);
|
g_assert_no_error (err);
|
||||||
g_object_unref (saddr);
|
g_object_unref (saddr);
|
||||||
g_object_unref (addr);
|
g_object_unref (addr);
|
||||||
|
|
||||||
saddr = g_socket_get_local_address (server, &err);
|
saddr = g_socket_get_local_address (listener, &err);
|
||||||
g_assert_no_error (err);
|
g_assert_no_error (err);
|
||||||
|
|
||||||
|
if (socket_type == G_SOCKET_TYPE_STREAM)
|
||||||
|
{
|
||||||
|
g_socket_listen (listener, &err);
|
||||||
|
g_assert_no_error (err);
|
||||||
|
g_socket_connect (client, saddr, NULL, &err);
|
||||||
|
g_assert_no_error (err);
|
||||||
|
|
||||||
|
server = g_socket_accept (listener, NULL, &err);
|
||||||
|
g_assert_no_error (err);
|
||||||
|
g_socket_set_blocking (server, FALSE);
|
||||||
|
g_object_unref (listener);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
server = listener;
|
||||||
|
|
||||||
g_socket_send_to (client, saddr, data, sizeof (data), NULL, &err);
|
g_socket_send_to (client, saddr, data, sizeof (data), NULL, &err);
|
||||||
g_assert_no_error (err);
|
g_assert_no_error (err);
|
||||||
|
|
||||||
@ -941,24 +963,60 @@ test_datagram_get_available (void)
|
|||||||
g_socket_send_to (client, saddr, data, sizeof (data), NULL, &err);
|
g_socket_send_to (client, saddr, data, sizeof (data), NULL, &err);
|
||||||
g_assert_no_error (err);
|
g_assert_no_error (err);
|
||||||
|
|
||||||
/* g_socket_condition_wait() won't help here since the socket is
|
/* We need to wait until the data has actually been copied into the
|
||||||
* definitely already readable. So there's a race condition here, but
|
* server socket's buffers, but g_socket_condition_wait() won't help
|
||||||
* at least the failure mode is passes-when-it-shouldn't, not
|
* here since the socket is definitely already readable. So there's
|
||||||
* fails-when-it-shouldn't.
|
* a race condition in checking its available bytes. In the TCP
|
||||||
|
* case, we poll for a bit until the new data shows up. In the UDP
|
||||||
|
* case, there's not much we can do, but at least the failure mode
|
||||||
|
* is passes-when-it-shouldn't, not fails-when-it-shouldn't.
|
||||||
*/
|
*/
|
||||||
g_usleep (100000);
|
if (socket_type == G_SOCKET_TYPE_STREAM)
|
||||||
g_assert_cmpint (g_socket_get_available_bytes (server), ==, sizeof (data));
|
{
|
||||||
|
int tries;
|
||||||
|
|
||||||
|
for (tries = 0; tries < 100; tries++)
|
||||||
|
{
|
||||||
|
if (g_socket_get_available_bytes (server) > sizeof (data))
|
||||||
|
break;
|
||||||
|
g_usleep (100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_cmpint (g_socket_get_available_bytes (server), ==, 2 * sizeof (data));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_usleep (100000);
|
||||||
|
g_assert_cmpint (g_socket_get_available_bytes (server), ==, sizeof (data));
|
||||||
|
}
|
||||||
|
|
||||||
g_assert_cmpint (sizeof (buf), >=, 2 * sizeof (data));
|
g_assert_cmpint (sizeof (buf), >=, 2 * sizeof (data));
|
||||||
nread = g_socket_receive (server, buf, sizeof (buf), NULL, &err);
|
nread = g_socket_receive (server, buf, sizeof (buf), NULL, &err);
|
||||||
g_assert_cmpint (nread, ==, sizeof (data));
|
|
||||||
g_assert_no_error (err);
|
g_assert_no_error (err);
|
||||||
|
|
||||||
g_assert_cmpint (g_socket_get_available_bytes (server), ==, sizeof (data));
|
if (socket_type == G_SOCKET_TYPE_STREAM)
|
||||||
|
{
|
||||||
|
g_assert_cmpint (nread, ==, 2 * sizeof (data));
|
||||||
|
g_assert_cmpint (g_socket_get_available_bytes (server), ==, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_assert_cmpint (nread, ==, sizeof (data));
|
||||||
|
g_assert_cmpint (g_socket_get_available_bytes (server), ==, sizeof (data));
|
||||||
|
}
|
||||||
|
|
||||||
nread = g_socket_receive (server, buf, sizeof (buf), NULL, &err);
|
nread = g_socket_receive (server, buf, sizeof (buf), NULL, &err);
|
||||||
g_assert_cmpint (nread, ==, sizeof (data));
|
if (socket_type == G_SOCKET_TYPE_STREAM)
|
||||||
g_assert_no_error (err);
|
{
|
||||||
|
g_assert_cmpint (nread, ==, -1);
|
||||||
|
g_assert_error (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK);
|
||||||
|
g_clear_error (&err);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_assert_cmpint (nread, ==, sizeof (data));
|
||||||
|
g_assert_no_error (err);
|
||||||
|
}
|
||||||
|
|
||||||
g_assert_cmpint (g_socket_get_available_bytes (server), ==, 0);
|
g_assert_cmpint (g_socket_get_available_bytes (server), ==, 0);
|
||||||
|
|
||||||
@ -993,7 +1051,10 @@ main (int argc,
|
|||||||
#endif
|
#endif
|
||||||
g_test_add_func ("/socket/reuse/tcp", test_reuse_tcp);
|
g_test_add_func ("/socket/reuse/tcp", test_reuse_tcp);
|
||||||
g_test_add_func ("/socket/reuse/udp", test_reuse_udp);
|
g_test_add_func ("/socket/reuse/udp", test_reuse_udp);
|
||||||
g_test_add_func ("/socket/datagram_get_available", test_datagram_get_available);
|
g_test_add_data_func ("/socket/get_available/datagram", GUINT_TO_POINTER (G_SOCKET_TYPE_DATAGRAM),
|
||||||
|
test_get_available);
|
||||||
|
g_test_add_data_func ("/socket/get_available/stream", GUINT_TO_POINTER (G_SOCKET_TYPE_STREAM),
|
||||||
|
test_get_available);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user