cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: is curl_multi_socket_all() deprecated?

From: Josef Drexler <josef_at_ttdpatch.net>
Date: Thu, 23 Oct 2008 01:33:45 +0200

On Oct 23, 2008, at 12:07 AM, Ragnar Lonn wrote:
> I may be intellectually challenged, or something, but it is still a
> bit unclear to me what actually happens initially. How can I be
> sure that I will receive a call to the timeout callback before I
> have to use the timeout value in e.g. my epoll_wait() call? Isn't
> it better to call curl_multi_timeout() to retrieve the timeout
> value every time just before I wait on the sockets?

The timeout won't change that often, hence polling would be a waste
of time, especially if you're going to do it for every socket event
that triggers.

The timeout is basically like an alarm clock. You don't need to check
the alarm every second to see if it rang, or if the wake-up time
changes. You just set it when libcurl tells you the time it needs to
wake up, and then forget about it until it rings or libcurl tells you
a new time to set.

> Also, Josef Drexler wrote that adding an easy handle to a multi
> handle will cause "the callbacks to be called" (timeout callback?).
> I thought that CURL would only actually do stuff, call callbacks
> etc. when you invoked one of its perform-functions?

Well, that may be true in the "old" API, but when using the socket
API it isn't the case. For the socket API to function, libcurl
obviously must trigger the callbacks BEFORE it can do any work,
because it's the events you receive for the sockets that will make
libcurl do anything. Same with the timeout. This means that at least
one callback MUST be triggered by adding a new transfer, or nothing
would ever get done.

> From what you tell me it seems what I should do is:
>
> 1) create a multi handle
> 2) set a timeout callback

and obviously a socket callback too

> 3) add easy handles, which will cause my timeout callback to get
> called (will it?), letting me know what timeout to use
> 4) immediately call curl_multi_socket_action() with sockfd argument
> set to CURL_SOCKET_TIMEOUT (because I don't yet know about any
> sockets, and curl has not performed any work yet, which should mean
> there are no active sockets, so waiting for nothing
> seems rather pointless, or...?)

Well... you seem to be trying to optimize away the timeout handling.
I guess that means you don't really have a proper event loop in your
program which would already handle the timeouts "for free". But if
you do have a proper event loop, just have your timeout callback
function add a timeout event when libcurl instructs you. And then
have that timeout event do the above call as and when needed, instead
of "manually" trying to call it at the right time.

> 5) enter main loop, properly wait on sockets, using the timeout
> value, and call curl_multi_socket_action() when things happen (or
> when I get a timeout)
>
> The above would basically mean that the encouraged way to get
> things started is to trigger a timeout - call
> curl_multi_socket_action() with sockfd set to CURL_SOCKET_TIMEOUT.
> This is something I never understood when I read the docs.

You don't need to do this manually. It will happen automatically when
you set a timeout callback and have that timeout callback add a
timeout event to your event loop. You sort of seem to be second-
guessing libcurl here, saying that "because the first thing it will
do is trigger a timeout, I should maybe do that without explicitly
being told to".

It's probably not a good idea to second-guess the library like that,
if the implementation changes in a later version it's likely to break
your program. There's nothing that stops libcurl from calling the
socket callback right away without ever waiting for a timeout, for
instance when the address does not need to be resolved and it can
open a connection immediately. Then it will only need to wait for the
connect to succeed, which will be a socket event, not a timeout.

> Or am I completely out in the blue here? If I get the hang of this
> I could perhaps write a code example for the next person who has
> trouble with this - an updated hiperfifo.c maybe.

Basically, you need to do this:

1. Create a multi handle.
2. Set the timeout callback for it. Set the socket callback for it.
3. Add one or more easy handles to it. Process whatever callbacks
this triggers.
4. Run your event loop. Call libcurl for the events (sockets or
timeouts) that trigger and process its callbacks.

The timeout callback would be called by libcurl as needed to add a
timeout event to your event loop.
And the socket callback would add/update/remove watchers for its
socket(s) in your event loop.

Then when the timeout event triggers in your event loop, you call
libcurl with CURL_SOCKET_TIMEOUT.
And when a socket event is triggered in your event loop, you call
libcurl with the socket and event type.

If you don't have support for timeout events yet, you will need to
add that. If libcurl is the only thing that needs timeouts in your
app, the simplest implementation would be to just remember the time
when libcurl wants to be notified, and in your event loop compute the
timeout argument to epoll_wait() (or select() or whatever you wish to
use) accordingly. And when it's time for the timeout event, call
libcurl with CURL_SOCKET_TIMEOUT. Then trust it to set a new timeout
event as needed. If other things will need timeouts too, you'll have
to implement a priority queue or similar data structure to sort them.

But if you're rolling your own event loop, you might instead want to
look into using one of the many event libraries, which have already
solved all the problems you're going to run into. Like libev, or
libevent, for instance.

-- 
Josef Drexler
josef_at_ttdpatch.net
Received on 2008-10-23