G_socket_set_timeout () clarification

Hi,

Is the "timeout" arg to g_socket_set_timeout() a request-timeout or idle-timeout ?

Request timeout:

E.g. HTTP client sends request ( at time T1 ) to webserver. With g_socket_set_timeout (client_socket, 10), if the client doesn’t receive a response before T1 + 10 seconds, the client GSocket will be marked as timed-out.

Idle timeout:

E.g. HTTP client sends request ( at time T1 ) to webserver and receives a response ( at time T2 ). With g_socket_set_timeout(client_socket, 10), the client GSocket will be marked as timed-out at T2 + 10 seconds,

I looked into the code, and it appears we’re doing the later, but the gtk-doc suggests the former.

Did I misread the code / documentation ?

So I haven’t taken the time to look at the code, but I would have assumed that it is an I/O timeout that applies to particular syscalls (read, write, close).

i.e. you finish a write() at time T1 and start a read(), then it should time out at T1 + 10 seconds. If the read() finishes at time T2 and you start a new read(), then it would time out at T2 + 10 seconds, but only if you start the read(). Whether you do that or not is up to the application using GSocket.

Again, those are just my assumptions based on reading the documentation and not looking at the code. Do you think any of that is incorrect?

@mcatanzaro: We actually don’t do a read() explicitly ( as you’ve mentioned ), but perform read() on the socket when data becomes available on the socket ( via a GSource->dispatch() attached to the GSocket ). Even blocking calls use the same technique with a huge timeout value ( -1 ).

I think the following happens in code:

Considering an example HTTP client.

  1. Client creates a GSocket connection with timeout of 10 seconds.
  2. Client creates a GSocketInputstream and attaches a GSource (G_IO_IN) to the GSocket.
  3. Client sends a HTTP request ( R1 ) to the web server.
  4. GSocket receives a G_IO_IN read event and GSocket->GSource->dispatch() is called.
  5. GSource->dispatch() invokes the HTTP client’s callback fn to read the data. ( refer code below )
  6. GSource->dispatch() updates the timeout for GSource after [5] completes. ( refer code below )

I think [6] is the issue here.

Here is the relevant code in gsocket.c:

  if (timeout >= 0 && timeout < g_source_get_time (source) &&
      !g_socket_is_closed (socket_source->socket))
    {
      socket->priv->timed_out = TRUE;
      events |= (G_IO_IN | G_IO_OUT);
    }

  ret = (*func) (socket, events & socket_source->condition, user_data);

  if (socket->priv->timeout && !g_socket_is_closed (socket_source->socket))
    g_source_set_ready_time (source, g_get_monotonic_time () + socket->priv->timeout * 1000000);
  else
    g_source_set_ready_time (source, -1);

Here, consider we have received the full HTTP response in [5], and still the timer is pushed to current + 10 seconds. So, if we want to use the same GSocket to send request ( R2 ) after current + 10 seconds, the send will fail as the GSource->dispatch() which was triggered at current + 10 seconds, has already marked the GSocket as timed out ( as shown in above code )

So, the following function will return false for HTTP request R2 on the GSocket:

static gboolean
check_timeout (GSocket *socket,
	       GError **error)
{
  if (socket->priv->timed_out)
    {
      socket->priv->timed_out = FALSE;
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
			   _("Socket I/O timed out"));
      return FALSE;
    }

  return TRUE;
}

Filed https://gitlab.gnome.org/GNOME/glib/-/issues/3079

2 Likes

Thanks for following up on this problem by creating an issue report. Let’s continue to discuss there.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.