curl / Mailing Lists / curl-library / Single Mail


Re: curl_multi_add_handle and subsequent timer

From: Maksim Dmitrichenko via curl-library <>
Date: Mon, 13 Nov 2017 12:41:14 +0300

Hi Daniel!

2017-11-13 10:26 GMT+03:00 Daniel Stenberg <>:

> On Mon, 13 Nov 2017, Maksim Dmitrichenko via curl-library wrote:
> I've noticed that call to curl_multi_add_handle() triggers appropriate
>> timer function callback with timeout of 1 ms.
> The timeout is actually at 0ms these days.

Maybe I'm running a bit outdated version 7.38 - it is on Debian Jessie. It
request 1 ms.

> Only after that timeout added easy handle starts to perform.
> That's not really how libcurl works. The timeout is the maximum time you
> should wait until you call a libcurl perform function to avoid that it
> delays some operation. You can always - at your choice - decide to call
> libcurl earlier or later than the timeout suggests.

Well, it is not that simple. Timer callback is called for various purposes
- sometimes it is called to setup connection timeout, sometimes (like the
above) to start execution of new handle. I never know for sure when I could
skip waiting for it. For example: let's have a working HTTP request that
waits for response from server. We have a socket that polls for incoming
data and a request timeout of e.g. 5000 ms. Ignoring that timeout and
calling libcurl instead will lead to another timeout being requested by
libcurl (e.g. for 4999 ms) and so on. So ignoring timeouts generally will
lead to high CPU consumption without doing any useful work. So, generally
choosing to ignore timeouts is a bad idea.

> The question is: why we need this timeout at all and if there is any way
>> to skip this step and execute the handle immediately?
> Sure!
> The timeout that is set when you add a handle is there to make sure users
> don't do go waiting around for something they call the perform function -
> because libcurl is ready and has work already at that point. You can thus
> skip the waiting and call libcurl right away if you want to, and libcurl
> will then update the timeout value according to what it was able to do in
> this call.
> libcurl has to set the timeout since you might already be running
> transfers with N other handles so adding another handle to the mix needs to
> make sure that the timeout expires "now" so that the new handle's transfer
> gets kicked off properly.

Let's do it on the bare metal. I have epoll() which polls curl's sockets,
timerfd descriptor and eventfd descriptor. I have curl with an active HTTP
connection (from previous request) and no active request.

Following sequence happens.

1) Thread 1: curl_multi_add_handle() called
2) Thread 1: timer callback is called from within libcurl and asks for
timer for 1 ms. Timer loaded.
3) Thread 1: upon return from curl_multi_add_handle() my code signals to
eventfd to interrupt epoll() running in thread 2.
4) Thread 2: epoll() exists with signal in eventfd().
5) Thread 2: my code makes a call curl_multi_socket_action(handle_multi,
CURL_SOCKET_TIMEOUT, 0, &running_handles);
6) Thread 2: timer callback is called from within libcurl and asks for
timer for 1 ms. Timer reloaded. (AGAIN!!!)
7) Thread 2: nothing happens, go into epoll() again!

There is no sleeps between steps 1 to 7!!! Only one context switch to
thread 2.

8) Thread 2 (AFTER 1ms): epoll() finished on timeout, call
curl_multi_socket_action(handle_multi, CURL_SOCKET_TIMEOUT, 0,
9) Thread 2: curl begins to do something - debug callback called with
text: Found bundle for host 0x7f69440101b0

So, I tried to hack this timeout but this doesn't work. Something inside
libcurl actually waits for 1 ms to pass.

Have I done something wrong, or you were incorrect saying that this timeout
can be ignored?

With best regards
  Maksim Dmitrichenko

Received on 2017-11-13