mirror of
https://github.com/moparisthebest/curl
synced 2024-11-12 04:25:08 -05:00
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
232ad6549a
.
This commit is contained in:
parent
55c266de6d
commit
0db9140747
104
lib/multi.c
104
lib/multi.c
@ -192,6 +192,9 @@ static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle,
|
|||||||
struct connectdata *conn);
|
struct connectdata *conn);
|
||||||
static bool isHandleAtHead(struct SessionHandle *handle,
|
static bool isHandleAtHead(struct SessionHandle *handle,
|
||||||
struct curl_llist *pipeline);
|
struct curl_llist *pipeline);
|
||||||
|
static CURLMcode add_next_timeout(struct timeval now,
|
||||||
|
struct Curl_multi *multi,
|
||||||
|
struct SessionHandle *d);
|
||||||
|
|
||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
static const char * const statename[]={
|
static const char * const statename[]={
|
||||||
@ -1690,37 +1693,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
|||||||
*/
|
*/
|
||||||
do {
|
do {
|
||||||
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
|
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
|
||||||
if(t) {
|
if(t)
|
||||||
struct SessionHandle *d = t->payload;
|
/* the removed may have another timeout in queue */
|
||||||
struct timeval *tv = &d->state.expiretime;
|
(void)add_next_timeout(now, multi, t->payload);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} while(t);
|
} while(t);
|
||||||
|
|
||||||
@ -1992,6 +1967,62 @@ static void singlesocket(struct Curl_multi *multi,
|
|||||||
easy->numsocks = num;
|
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,
|
static CURLMcode multi_socket(struct Curl_multi *multi,
|
||||||
bool checkall,
|
bool checkall,
|
||||||
curl_socket_t s,
|
curl_socket_t s,
|
||||||
@ -2106,15 +2137,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||||||
}
|
}
|
||||||
|
|
||||||
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
|
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
|
||||||
if(t) {
|
if(t)
|
||||||
/* assign 'data' to be the easy handle we just removed from the splay
|
(void)add_next_timeout(now, multi, t->payload);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} while(t);
|
} while(t);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user