gsocket: add g_socket_condition_timed_wait()

https://bugzilla.gnome.org/show_bug.cgi?id=667755
This commit is contained in:
Dan Winship 2012-02-13 17:20:04 -05:00
parent 823f553e36
commit 726257ab97
5 changed files with 136 additions and 15 deletions

View File

@ -1852,6 +1852,7 @@ g_socket_is_connected
g_socket_create_source g_socket_create_source
g_socket_condition_check g_socket_condition_check
g_socket_condition_wait g_socket_condition_wait
g_socket_condition_timed_wait
g_socket_get_available_bytes g_socket_get_available_bytes
g_socket_set_listen_backlog g_socket_set_listen_backlog
g_socket_get_listen_backlog g_socket_get_listen_backlog

View File

@ -949,6 +949,7 @@ g_socket_close
g_socket_shutdown g_socket_shutdown
g_socket_condition_check g_socket_condition_check
g_socket_condition_wait g_socket_condition_wait
g_socket_condition_timed_wait
g_socket_connect g_socket_connect
g_socket_create_source g_socket_create_source
g_socket_get_available_bytes g_socket_get_available_bytes

View File

@ -3404,6 +3404,8 @@ g_socket_condition_check (GSocket *socket,
* the appropriate value (%G_IO_ERROR_CANCELLED or * the appropriate value (%G_IO_ERROR_CANCELLED or
* %G_IO_ERROR_TIMED_OUT). * %G_IO_ERROR_TIMED_OUT).
* *
* See also g_socket_condition_timed_wait().
*
* Returns: %TRUE if the condition was met, %FALSE otherwise * Returns: %TRUE if the condition was met, %FALSE otherwise
* *
* Since: 2.22 * Since: 2.22
@ -3416,17 +3418,69 @@ g_socket_condition_wait (GSocket *socket,
{ {
g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
return g_socket_condition_timed_wait (socket, condition, -1,
cancellable, error);
}
/**
* g_socket_condition_timed_wait:
* @socket: a #GSocket
* @condition: a #GIOCondition mask to wait for
* @timeout: the maximum time (in microseconds) to wait, or -1
* @cancellable: (allow-none): a #GCancellable, or %NULL
* @error: a #GError pointer, or %NULL
*
* Waits for up to @timeout microseconds for @condition to become true
* on @socket. If the condition is met, %TRUE is returned.
*
* If @cancellable is cancelled before the condition is met, or if
* @timeout (or the socket's #GSocket:timeout) is reached before the
* condition is met, then %FALSE is returned and @error, if non-%NULL,
* is set to the appropriate value (%G_IO_ERROR_CANCELLED or
* %G_IO_ERROR_TIMED_OUT).
*
* If you don't want a timeout, use g_socket_condition_wait().
* (Alternatively, you can pass -1 for @timeout.)
*
* Note that although @timeout is in microseconds for consistency with
* other GLib APIs, this function actually only has millisecond
* resolution, and the behavior is undefined if @timeout is not an
* exact number of milliseconds.
*
* Returns: %TRUE if the condition was met, %FALSE otherwise
*
* Since: 2.32
*/
gboolean
g_socket_condition_timed_wait (GSocket *socket,
GIOCondition condition,
gint64 timeout,
GCancellable *cancellable,
GError **error)
{
gint64 start_time;
g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
if (!check_socket (socket, error)) if (!check_socket (socket, error))
return FALSE; return FALSE;
if (g_cancellable_set_error_if_cancelled (cancellable, error)) if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE; return FALSE;
if (socket->priv->timeout &&
(timeout < 0 || socket->priv->timeout < timeout / G_USEC_PER_SEC))
timeout = socket->priv->timeout * 1000;
else if (timeout != -1)
timeout = timeout / 1000;
start_time = g_get_monotonic_time ();
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
{ {
GIOCondition current_condition; GIOCondition current_condition;
WSAEVENT events[2]; WSAEVENT events[2];
DWORD res, timeout; DWORD res;
GPollFD cancel_fd; GPollFD cancel_fd;
int num_events; int num_events;
@ -3441,15 +3495,13 @@ g_socket_condition_wait (GSocket *socket,
if (g_cancellable_make_pollfd (cancellable, &cancel_fd)) if (g_cancellable_make_pollfd (cancellable, &cancel_fd))
events[num_events++] = (WSAEVENT)cancel_fd.fd; events[num_events++] = (WSAEVENT)cancel_fd.fd;
if (socket->priv->timeout) if (timeout == -1)
timeout = socket->priv->timeout * 1000;
else
timeout = WSA_INFINITE; timeout = WSA_INFINITE;
current_condition = update_condition (socket); current_condition = update_condition (socket);
while ((condition & current_condition) == 0) while ((condition & current_condition) == 0)
{ {
res = WSAWaitForMultipleEvents(num_events, events, res = WSAWaitForMultipleEvents (num_events, events,
FALSE, timeout, FALSE); FALSE, timeout, FALSE);
if (res == WSA_WAIT_FAILED) if (res == WSA_WAIT_FAILED)
{ {
@ -3472,6 +3524,13 @@ g_socket_condition_wait (GSocket *socket,
break; break;
current_condition = update_condition (socket); current_condition = update_condition (socket);
if (timeout != WSA_INFINITE)
{
timeout -= (g_get_monotonic_time () - start_time) * 1000;
if (timeout < 0)
timeout = 0;
}
} }
remove_condition_watch (socket, &condition); remove_condition_watch (socket, &condition);
if (num_events > 1) if (num_events > 1)
@ -3484,7 +3543,6 @@ g_socket_condition_wait (GSocket *socket,
GPollFD poll_fd[2]; GPollFD poll_fd[2];
gint result; gint result;
gint num; gint num;
gint timeout;
poll_fd[0].fd = socket->priv->fd; poll_fd[0].fd = socket->priv->fd;
poll_fd[0].events = condition; poll_fd[0].events = condition;
@ -3493,14 +3551,19 @@ g_socket_condition_wait (GSocket *socket,
if (g_cancellable_make_pollfd (cancellable, &poll_fd[1])) if (g_cancellable_make_pollfd (cancellable, &poll_fd[1]))
num++; num++;
if (socket->priv->timeout) while (TRUE)
timeout = socket->priv->timeout * 1000; {
else
timeout = -1;
do
result = g_poll (poll_fd, num, timeout); result = g_poll (poll_fd, num, timeout);
while (result == -1 && get_socket_errno () == EINTR); if (result != -1 || errno != EINTR)
break;
if (timeout != -1)
{
timeout -= (g_get_monotonic_time () - start_time) * 1000;
if (timeout < 0)
timeout = 0;
}
}
if (num > 1) if (num > 1)
g_cancellable_release_fd (cancellable); g_cancellable_release_fd (cancellable);

View File

@ -145,6 +145,11 @@ gboolean g_socket_condition_wait (GSocket
GIOCondition condition, GIOCondition condition,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
gboolean g_socket_condition_timed_wait (GSocket *socket,
GIOCondition condition,
gint64 timeout,
GCancellable *cancellable,
GError **error);
GSocket * g_socket_accept (GSocket *socket, GSocket * g_socket_accept (GSocket *socket,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);

View File

@ -583,6 +583,56 @@ test_ipv6_v4mapped (void)
} }
#endif #endif
static void
test_timed_wait (void)
{
IPTestData *data;
GError *error = NULL;
GSocket *client;
GSocketAddress *addr;
gint64 start_time;
gint poll_duration;
data = create_server (G_SOCKET_FAMILY_IPV4, echo_server_thread, FALSE);
addr = g_socket_get_local_address (data->server, &error);
client = g_socket_new (G_SOCKET_FAMILY_IPV4,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
&error);
g_assert_no_error (error);
g_socket_set_blocking (client, TRUE);
g_socket_set_timeout (client, 1);
g_socket_connect (client, addr, NULL, &error);
g_assert_no_error (error);
g_object_unref (addr);
start_time = g_get_monotonic_time ();
g_socket_condition_timed_wait (client, G_IO_IN, 100000 /* 100 ms */,
NULL, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
g_clear_error (&error);
poll_duration = g_get_monotonic_time () - start_time;
g_assert_cmpint (poll_duration, >=, 100000);
g_assert_cmpint (poll_duration, <, 110000);
g_socket_close (client, &error);
g_assert_no_error (error);
g_thread_join (data->thread);
g_socket_close (data->server, &error);
g_assert_no_error (error);
g_object_unref (data->server);
g_object_unref (client);
g_slice_free (IPTestData, data);
}
static void static void
test_sockaddr (void) test_sockaddr (void)
{ {
@ -774,10 +824,11 @@ main (int argc,
g_test_add_func ("/socket/ipv4_async", test_ipv4_async); g_test_add_func ("/socket/ipv4_async", test_ipv4_async);
g_test_add_func ("/socket/ipv6_sync", test_ipv6_sync); g_test_add_func ("/socket/ipv6_sync", test_ipv6_sync);
g_test_add_func ("/socket/ipv6_async", test_ipv6_async); g_test_add_func ("/socket/ipv6_async", test_ipv6_async);
g_test_add_func ("/socket/close_graceful", test_close_graceful);
#if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY)
g_test_add_func ("/socket/ipv6_v4mapped", test_ipv6_v4mapped); g_test_add_func ("/socket/ipv6_v4mapped", test_ipv6_v4mapped);
#endif #endif
g_test_add_func ("/socket/close_graceful", test_close_graceful);
g_test_add_func ("/socket/timed_wait", test_timed_wait);
g_test_add_func ("/socket/address", test_sockaddr); g_test_add_func ("/socket/address", test_sockaddr);
#ifdef G_OS_UNIX #ifdef G_OS_UNIX
g_test_add_func ("/socket/unix-from-fd", test_unix_from_fd); g_test_add_func ("/socket/unix-from-fd", test_unix_from_fd);