From 0db9140747e65ab402768670b130b0ddaa88f883 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 31 Aug 2010 00:08:45 +0200 Subject: multi: make sure the next timeout is used when one expires Each easy handle has a list of timeouts, so as soon as the main timeout for a handle expires, we must make sure to get the next entry from the list and re-add the handle to the splay tree. This was attempted previously but was done poorly in my commit 232ad6549a68450. --- lib/multi.c | 104 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 4bf74a746..5a5470246 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -192,6 +192,9 @@ static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle, struct connectdata *conn); static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); +static CURLMcode add_next_timeout(struct timeval now, + struct Curl_multi *multi, + struct SessionHandle *d); #ifdef DEBUGBUILD static const char * const statename[]={ @@ -1690,37 +1693,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) */ do { multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - struct SessionHandle *d = t->payload; - struct timeval *tv = &d->state.expiretime; - struct curl_llist *list = d->state.timeoutlist; - struct curl_llist_element *e; - - /* move over the timeout list for this specific handle and remove all - timeouts that are now passed tense and store the next pending - timeout in *tv */ - for(e = list->head; e; ) { - struct curl_llist_element *n = e->next; - if(curlx_tvdiff(*(struct timeval *)e->ptr, now) < 0) - /* remove outdated entry */ - Curl_llist_remove(list, e, NULL); - e = n; - } - if(!list->size) { - /* clear the expire times within the handles that we remove from the - splay tree */ - tv->tv_sec = 0; - tv->tv_usec = 0; - } - else { - e = list->head; - /* copy the first entry to 'tv' */ - memcpy(tv, e->ptr, sizeof(*tv)); - - /* remove first entry from list */ - Curl_llist_remove(list, e, NULL); - } - } + if(t) + /* the removed may have another timeout in queue */ + (void)add_next_timeout(now, multi, t->payload); } while(t); @@ -1992,6 +1967,62 @@ static void singlesocket(struct Curl_multi *multi, easy->numsocks = num; } +/* + * add_next_timeout() + * + * Each SessionHandle has a list of timeouts. The add_next_timeout() is called + * when it has just been removed from the splay tree because the timeout has + * expired. This function is then to advance in the list to pick the next + * timeout to use (skip the already expired ones) and add this node back to + * the splay tree again. + * + * The splay tree only has each sessionhandle as a single node and the nearest + * timeout is used to sort it on. + */ +static CURLMcode add_next_timeout(struct timeval now, + struct Curl_multi *multi, + struct SessionHandle *d) +{ + struct timeval *tv = &d->state.expiretime; + struct curl_llist *list = d->state.timeoutlist; + struct curl_llist_element *e; + + /* move over the timeout list for this specific handle and remove all + timeouts that are now passed tense and store the next pending + timeout in *tv */ + for(e = list->head; e; ) { + struct curl_llist_element *n = e->next; + long diff = curlx_tvdiff(*(struct timeval *)e->ptr, now); + if(diff <= 0) + /* remove outdated entry */ + Curl_llist_remove(list, e, NULL); + else + /* the list is sorted so get out on the first mismatch */ + break; + e = n; + } + if(!list->size) { + /* clear the expire times within the handles that we remove from the + splay tree */ + tv->tv_sec = 0; + tv->tv_usec = 0; + } + else { + e = list->head; + /* copy the first entry to 'tv' */ + memcpy(tv, e->ptr, sizeof(*tv)); + + /* remove first entry from list */ + Curl_llist_remove(list, e, NULL); + + /* insert this node again into the splay */ + multi->timetree = Curl_splayinsert(*tv, multi->timetree, + &d->state.timenode); + } + return CURLM_OK; +} + + static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, curl_socket_t s, @@ -2106,15 +2137,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - /* assign 'data' to be the easy handle we just removed from the splay - tree */ - data = t->payload; - /* clear the expire time within the handle we removed from the - splay tree */ - data->state.expiretime.tv_sec = 0; - data->state.expiretime.tv_usec = 0; - } + if(t) + (void)add_next_timeout(now, multi, t->payload); } while(t); -- cgit v1.2.3