curl_multi_poll: a sister to curl_multi_wait() that waits more

Repeatedly we see problems where using curl_multi_wait() is difficult or
just awkward because if it has no file descriptor to wait for
internally, it returns immediately and leaves it to the caller to wait
for a small amount of time in order to avoid occasional busy-looping.

This is often missed or misunderstood, leading to underperforming
applications.

This change introduces curl_multi_poll() as a replacement drop-in
function that accepts the exact same set of arguments. This function
works identically to curl_multi_wait() - EXCEPT - for the case when
there's nothing to wait for internally, as then this function will by
itself wait for a "suitable" short time before it returns. This
effectiely avoids all risks of busy-looping and should also make it less
likely that apps "over-wait".

This also changes the curl tool to use this funtion internally when
doing parallel transfers and changes curl_easy_perform() to use it
internally.

Closes #4163
This commit is contained in:
Daniel Stenberg 2019-07-29 13:41:00 +02:00
parent cee7639b79
commit 02346abc32
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
9 changed files with 160 additions and 79 deletions

View file

@ -1904,23 +1904,6 @@ static CURLcode create_transfers(struct GlobalConfig *global,
return result;
}
/* portable millisecond sleep */
static void wait_ms(int ms)
{
#if defined(MSDOS)
delay(ms);
#elif defined(WIN32)
Sleep(ms);
#elif defined(HAVE_USLEEP)
usleep(1000 * ms);
#else
struct timeval pending_tv;
pending_tv.tv_sec = ms / 1000;
pending_tv.tv_usec = (ms % 1000) * 1000;
(void)select(0, NULL, NULL, NULL, &pending_tv);
#endif
}
static long all_added; /* number of easy handles currently added */
static int add_parallel_transfers(struct GlobalConfig *global,
@ -1971,30 +1954,9 @@ static CURLcode parallel_transfers(struct GlobalConfig *global,
return result;
while(!done && !mcode && still_running) {
int numfds;
struct timeval before = tvnow();
long delta;
mcode = curl_multi_wait(multi, NULL, 0, 1000, &numfds);
delta = tvdiff(tvnow(), before);
if(!mcode) {
if(!numfds && (delta < 30)) {
long sleep_ms;
/* If it returns without any file descriptor instantly, we need to
avoid busy-looping during periods where it has nothing particular
to wait for */
curl_multi_timeout(multi, &sleep_ms);
if(sleep_ms) {
if(sleep_ms > 1000)
sleep_ms = 1000;
wait_ms((int)sleep_ms);
}
}
mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL);
if(!mcode)
mcode = curl_multi_perform(multi, &still_running);
}
progress_meter(global, &start, FALSE);