cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: CURLM Chain of events

From: Daniel Stenberg <daniel_at_haxx.se>
Date: Fri, 22 Jan 2016 15:28:59 +0100 (CET)

On Fri, 22 Jan 2016, surf via curl-library wrote:

(I try to manually untangle that text blob when I respond now, but please send
plain old ordinary text mails in the future.)

> OK. curl_multi_perform OUT,
> curl_multi_socket_action(curl_m_handle,CURL_SOCKET_TIMEOUT… IN

Yes, and the CURL_SOCKET_TIMEOUT case is then for timeouts only.

> I assume this just causes libcurl to attempt transfer for all added easy
> handles?

Nope. If a socket is ready to get traffic, you'd get that signalled and you'd
tell libcurl about that. When you tell libcurl there's a timeout, libcurl will
only check pending handles for timeout actions. It could be to expire timeouts
or do retries and similar.

> So the general idea is that calling curl_multi_socket_action() for a
> particular socket will cause libcurl to attempt transfer on thatsocket -  if
> it can,

Exactly!

> and if it can’t it will fire socket_callback() telling me to call
> curl_multi_socket_action() again

Not really. It will call the socket_callback() again if there's a change in
what sockets or socket events the application should wait for. If the setup
remains the same, no new calls to the callback will be made.

> This would go back and forth until socket_callback() fires with
> CURL_POLL_REMOVE in which case I would need to loop on
> curl_multi_info_read() to determine the outcome for the transfer and
> optionally remove the easy handle from the collection.

You will find out that libcurl can very well use more than one socket per
transfer, so you most probably get several created and several removed before
the transfer is complete. So you either check the counter the
curl_multi_socket_action() returns or you call curl_multi_info_read() to
figure out if something completed. Trying to figure out when something is
completed based on which sockets you wait for is futile.

> Still seeing those after changing to
> curl_multi_socket_action(curl_m_handle,CURL_SOCKET_TIMEOUT.. (25 calls), but
> I guess this is OK.

Getting many calls to this callback is to be expected, as libcurl will update
the timeout as it progresses.

> I'm still a bit hesitated about how the non-repeating timer supplements the
> transfer process per easy handle.

Quite simply: the timeout is necessary for when libcurl needs to act without
any detected socket acitivity. Like when someone cuts off your internet
connection somewhere in the middle and no more socket activities happen.

> The way I figure, the non-repeating timer callback says it's time's up for
> telling libcurl that "some socket" is ready;

Not at all.

> therefore, I assume I should stop the timer from firing as soonas I
> determine the socket is ready.

No you shouldn't. If libcurl determines that the timeout isn't necessary
anymore (like after socket activity), it will call the timer callback and say
so.

> The thing is that the api does not appear to expose a one-to-one
> relationship between a socketfd and a non-repeating timerinstance.

Exactly. You only need to maintain a single timer. That represents the
shortest timeout of all the internal timers libcurl has. You can add a
thousand handles and you still only get one timeout to handle.

> I mean sure it allows me to pass custom data via *userp but I don't quite
> see how to construct this per socketfd to begin with.

The timeout is for the entire multi handle. You can't and shouldn't try to
attribute it to any particular socket.

> am I expected to just let timer callbacks fire anyway and
> callcurl_multi_socket_action(curl_m_handle, CURL_SOCKET_TIMEOUT regardless
> of what the socket ready state is?

Yes!

> Will that cause libcurl to fire a socket_callback with CURL_POLL_REMOVE for
> a particular timed-out socket

If a transfer is done, successul or not, libcurl will tell you to
CURL_POLL_REMOVE it.

> in which case I would need to also test for CURLE_OPERATION_TIMEDOUT

libcurl will of course provide the CURLcode for the individual transfer and
that can be CURLE_OPERATION_TIMEDOUT as well as one of all the other return
codes. You extract that return code for the invidual transfers with
curl_multi_info_read().

> Obviously I would need to ensure that no call to curl_multi_socket_action()
> will take place for that socket after this point,

libcurl will tell your socket callback to remove sockets it no longer is
interested in, so yes you need to make sure that you handle that. So sockets
that are used by transfers that are completed (and not used by anything else)
will be removed like that.

> I wonder if I'm allowed to prevent the timeout callback from firing to begin
> with just to be more efficient,

I wouldn't recommend it. If libcurl sets too many/wrong callbacks, then that's
a bug we should fix. I don't think trying to optimize that in your application
is a good idea.

> that is when socket readiness is determined before the timeout elapses.

But if libcurl check its sockets now, and we know the timeout expires in 2
milliseconds, it still means that the timeout hasn't expired when the
socket(s) are checked so the timeout handling didn't run - so you still need
to tell libcurl about the expired timeout after 2 milliseconds.

If you skip the timeout call just because you recently called libcurl with
socket activity, you'll only delay some timeout handling. That may be totally
fine, but you may also lose internal timing accuracy.

-- 
  / daniel.haxx.se

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2016-01-22