[glib] gsocket: add g_socket_condition_timed_wait()



commit 726257ab9721acd0699e54194e20e7f53e5a9688
Author: Dan Winship <danw gnome org>
Date:   Mon Feb 13 17:20:04 2012 -0500

    gsocket: add g_socket_condition_timed_wait()
    
    https://bugzilla.gnome.org/show_bug.cgi?id=667755

 docs/reference/gio/gio-sections.txt |    1 +
 gio/gio.symbols                     |    1 +
 gio/gsocket.c                       |   91 +++++++++++++++++++++++++++++-----
 gio/gsocket.h                       |    5 ++
 gio/tests/socket.c                  |   53 ++++++++++++++++++++-
 5 files changed, 136 insertions(+), 15 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 71c597b..c9ed66b 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1852,6 +1852,7 @@ g_socket_is_connected
 g_socket_create_source
 g_socket_condition_check
 g_socket_condition_wait
+g_socket_condition_timed_wait
 g_socket_get_available_bytes
 g_socket_set_listen_backlog
 g_socket_get_listen_backlog
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 140eaab..9b609c1 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -949,6 +949,7 @@ g_socket_close
 g_socket_shutdown
 g_socket_condition_check
 g_socket_condition_wait
+g_socket_condition_timed_wait
 g_socket_connect
 g_socket_create_source
 g_socket_get_available_bytes
diff --git a/gio/gsocket.c b/gio/gsocket.c
index c6a9137..d46d497 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -3404,6 +3404,8 @@ g_socket_condition_check (GSocket      *socket,
  * the appropriate value (%G_IO_ERROR_CANCELLED or
  * %G_IO_ERROR_TIMED_OUT).
  *
+ * See also g_socket_condition_timed_wait().
+ *
  * Returns: %TRUE if the condition was met, %FALSE otherwise
  *
  * Since: 2.22
@@ -3416,17 +3418,69 @@ g_socket_condition_wait (GSocket       *socket,
 {
   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))
     return FALSE;
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     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
   {
     GIOCondition current_condition;
     WSAEVENT events[2];
-    DWORD res, timeout;
+    DWORD res;
     GPollFD cancel_fd;
     int num_events;
 
@@ -3441,16 +3495,14 @@ g_socket_condition_wait (GSocket       *socket,
     if (g_cancellable_make_pollfd (cancellable, &cancel_fd))
       events[num_events++] = (WSAEVENT)cancel_fd.fd;
 
-    if (socket->priv->timeout)
-      timeout = socket->priv->timeout * 1000;
-    else
+    if (timeout == -1)
       timeout = WSA_INFINITE;
 
     current_condition = update_condition (socket);
     while ((condition & current_condition) == 0)
       {
-	res = WSAWaitForMultipleEvents(num_events, events,
-				       FALSE, timeout, FALSE);
+	res = WSAWaitForMultipleEvents (num_events, events,
+					FALSE, timeout, FALSE);
 	if (res == WSA_WAIT_FAILED)
 	  {
 	    int errsv = get_socket_errno ();
@@ -3472,6 +3524,13 @@ g_socket_condition_wait (GSocket       *socket,
 	  break;
 
 	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);
     if (num_events > 1)
@@ -3484,7 +3543,6 @@ g_socket_condition_wait (GSocket       *socket,
     GPollFD poll_fd[2];
     gint result;
     gint num;
-    gint timeout;
 
     poll_fd[0].fd = socket->priv->fd;
     poll_fd[0].events = condition;
@@ -3493,14 +3551,19 @@ g_socket_condition_wait (GSocket       *socket,
     if (g_cancellable_make_pollfd (cancellable, &poll_fd[1]))
       num++;
 
-    if (socket->priv->timeout)
-      timeout = socket->priv->timeout * 1000;
-    else
-      timeout = -1;
+    while (TRUE)
+      {
+	result = g_poll (poll_fd, num, timeout);
+	if (result != -1 || errno != EINTR)
+	  break;
 
-    do
-      result = g_poll (poll_fd, num, timeout);
-    while (result == -1 && get_socket_errno () == EINTR);
+	if (timeout != -1)
+	  {
+	    timeout -= (g_get_monotonic_time () - start_time) * 1000;
+	    if (timeout < 0)
+	      timeout = 0;
+	  }
+      }
     
     if (num > 1)
       g_cancellable_release_fd (cancellable);
diff --git a/gio/gsocket.h b/gio/gsocket.h
index 1ad6a6f..d817328 100644
--- a/gio/gsocket.h
+++ b/gio/gsocket.h
@@ -145,6 +145,11 @@ gboolean               g_socket_condition_wait          (GSocket
 							 GIOCondition             condition,
 							 GCancellable            *cancellable,
 							 GError                 **error);
+gboolean               g_socket_condition_timed_wait    (GSocket                 *socket,
+							 GIOCondition             condition,
+							 gint64                   timeout,
+							 GCancellable            *cancellable,
+							 GError                 **error);
 GSocket *              g_socket_accept                  (GSocket                 *socket,
 							 GCancellable            *cancellable,
 							 GError                 **error);
diff --git a/gio/tests/socket.c b/gio/tests/socket.c
index ecdb3be..7404bba 100644
--- a/gio/tests/socket.c
+++ b/gio/tests/socket.c
@@ -584,6 +584,56 @@ test_ipv6_v4mapped (void)
 #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
 test_sockaddr (void)
 {
   struct sockaddr_in6 sin6, gsin6;
@@ -774,10 +824,11 @@ main (int   argc,
   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_async", test_ipv6_async);
-  g_test_add_func ("/socket/close_graceful", test_close_graceful);
 #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY)
   g_test_add_func ("/socket/ipv6_v4mapped", test_ipv6_v4mapped);
 #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);
 #ifdef G_OS_UNIX
   g_test_add_func ("/socket/unix-from-fd", test_unix_from_fd);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]