gsocket: always try before waiting for condition

When implementing blocking operations on top of
nonblocking sockets we should always first try to
perform the operation and then if needed handle
EAGAIN and wait with g_socket_wait_condition.
This is an optimization since we avoid calling
wait condition when it is not needed, but most
importantly this fixes hangs on win32 where some
events (in particular FD_WRITE) are only emitted
after the operation fails with EWOULDBLOCK.

https://bugzilla.gnome.org/show_bug.cgi?id=732439
https://bugzilla.gnome.org/show_bug.cgi?id=741707
This commit is contained in:
Ignacio Casal Quinteiro 2014-12-22 16:38:50 +01:00 committed by Paolo Borelli
parent 4f4714285d
commit cf03e82478

View File

@ -2230,31 +2230,31 @@ g_socket_accept (GSocket *socket,
while (TRUE)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
G_IO_IN, cancellable, error))
return NULL;
if ((ret = accept (socket->priv->fd, NULL, 0)) < 0)
{
int errsv = get_socket_errno ();
win32_unset_event_mask (socket, FD_ACCEPT);
if (errsv == EINTR)
continue;
if (socket->priv->blocking)
{
#ifdef WSAEWOULDBLOCK
if (errsv == WSAEWOULDBLOCK)
continue;
if (errsv == WSAEWOULDBLOCK)
#else
if (errsv == EWOULDBLOCK ||
errsv == EAGAIN)
continue;
if (errsv == EWOULDBLOCK ||
errsv == EAGAIN)
#endif
}
{
win32_unset_event_mask (socket, FD_ACCEPT);
if (socket->priv->blocking)
{
if (!g_socket_condition_wait (socket,
G_IO_IN, cancellable, error))
return NULL;
continue;
}
}
g_set_error (error, G_IO_ERROR,
socket_io_error_from_errno (errsv),
@ -2368,6 +2368,8 @@ g_socket_connect (GSocket *socket,
if (errsv == WSAEWOULDBLOCK)
#endif
{
win32_unset_event_mask (socket, FD_CONNECT);
if (socket->priv->blocking)
{
if (g_socket_condition_wait (socket, G_IO_OUT, cancellable, error))
@ -2602,11 +2604,6 @@ g_socket_receive_with_blocking (GSocket *socket,
while (1)
{
if (blocking &&
!g_socket_condition_wait (socket,
G_IO_IN, cancellable, error))
return -1;
if ((ret = recv (socket->priv->fd, buffer, size, 0)) < 0)
{
int errsv = get_socket_errno ();
@ -2614,17 +2611,24 @@ g_socket_receive_with_blocking (GSocket *socket,
if (errsv == EINTR)
continue;
if (blocking)
{
#ifdef WSAEWOULDBLOCK
if (errsv == WSAEWOULDBLOCK)
continue;
if (errsv == WSAEWOULDBLOCK)
#else
if (errsv == EWOULDBLOCK ||
errsv == EAGAIN)
continue;
if (errsv == EWOULDBLOCK ||
errsv == EAGAIN)
#endif
}
{
win32_unset_event_mask (socket, FD_READ);
if (blocking)
{
if (!g_socket_condition_wait (socket,
G_IO_IN, cancellable, error))
return -1;
continue;
}
}
win32_unset_event_mask (socket, FD_READ);
@ -2777,11 +2781,6 @@ g_socket_send_with_blocking (GSocket *socket,
while (1)
{
if (blocking &&
!g_socket_condition_wait (socket,
G_IO_OUT, cancellable, error))
return -1;
if ((ret = send (socket->priv->fd, buffer, size, G_SOCKET_DEFAULT_SEND_FLAGS)) < 0)
{
int errsv = get_socket_errno ();
@ -2790,21 +2789,23 @@ g_socket_send_with_blocking (GSocket *socket,
continue;
#ifdef WSAEWOULDBLOCK
if (errsv == WSAEWOULDBLOCK)
win32_unset_event_mask (socket, FD_WRITE);
#endif
if (blocking)
{
#ifdef WSAEWOULDBLOCK
if (errsv == WSAEWOULDBLOCK)
continue;
if (errsv == WSAEWOULDBLOCK)
#else
if (errsv == EWOULDBLOCK ||
errsv == EAGAIN)
continue;
if (errsv == EWOULDBLOCK ||
errsv == EAGAIN)
#endif
}
{
win32_unset_event_mask (socket, FD_WRITE);
if (blocking)
{
if (!g_socket_condition_wait (socket,
G_IO_OUT, cancellable, error))
return -1;
continue;
}
}
g_set_error (error, G_IO_ERROR,
socket_io_error_from_errno (errsv),
@ -3874,11 +3875,6 @@ g_socket_send_message (GSocket *socket,
while (1)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
G_IO_OUT, cancellable, error))
return -1;
result = sendmsg (socket->priv->fd, &msg, flags | G_SOCKET_DEFAULT_SEND_FLAGS);
if (result < 0)
{
@ -3890,7 +3886,13 @@ g_socket_send_message (GSocket *socket,
if (socket->priv->blocking &&
(errsv == EWOULDBLOCK ||
errsv == EAGAIN))
continue;
{
if (!g_socket_condition_wait (socket,
G_IO_OUT, cancellable, error))
return -1;
continue;
}
g_set_error (error, G_IO_ERROR,
socket_io_error_from_errno (errsv),
@ -3942,11 +3944,6 @@ g_socket_send_message (GSocket *socket,
while (1)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
G_IO_OUT, cancellable, error))
return -1;
if (address)
result = WSASendTo (socket->priv->fd,
bufs, num_vectors,
@ -3967,11 +3964,18 @@ g_socket_send_message (GSocket *socket,
continue;
if (errsv == WSAEWOULDBLOCK)
win32_unset_event_mask (socket, FD_WRITE);
{
win32_unset_event_mask (socket, FD_WRITE);
if (socket->priv->blocking &&
errsv == WSAEWOULDBLOCK)
continue;
if (socket->priv->blocking)
{
if (!g_socket_condition_wait (socket,
G_IO_OUT, cancellable, error))
return -1;
continue;
}
}
g_set_error (error, G_IO_ERROR,
socket_io_error_from_errno (errsv),
@ -4634,11 +4638,6 @@ g_socket_receive_message (GSocket *socket,
/* do it */
while (1)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
G_IO_IN, cancellable, error))
return -1;
addrlen = sizeof addr;
if (address)
result = WSARecvFrom (socket->priv->fd,
@ -4658,11 +4657,19 @@ g_socket_receive_message (GSocket *socket,
if (errsv == WSAEINTR)
continue;
win32_unset_event_mask (socket, FD_READ);
if (errsv == WSAEWOULDBLOCK)
{
win32_unset_event_mask (socket, FD_READ);
if (socket->priv->blocking &&
errsv == WSAEWOULDBLOCK)
continue;
if (socket->priv->blocking)
{
if (!g_socket_condition_wait (socket,
G_IO_IN, cancellable, error))
return -1;
continue;
}
}
g_set_error (error, G_IO_ERROR,
socket_io_error_from_errno (errsv),