multi: fixes for timing out handles

Add a timeout check for handles in the state machine so that they will
timeout in all states disregarding what actions that may or may not
happen.

Fixed a bug in socket_action introduced recently when looping over timed
out handles: it wouldn't assign the 'data' variable and thus it wouldn't
properly take care of handles.

In the update_timer function, the code now checks if the timeout has
been removed and then it tells the application. Previously it would
always let the remaining timeout(s) just linger to expire later on.
This commit is contained in:
Daniel Stenberg 2010-09-01 16:52:23 +02:00
parent 5e92015711
commit ca10e28f06

View File

@ -929,6 +929,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
}
static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct timeval now,
struct Curl_one_easy *easy)
{
struct Curl_message *msg = NULL;
@ -940,6 +941,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
CURLMcode result = CURLM_OK;
struct SingleRequest *k;
struct SessionHandle *data;
long timeout_ms;
if(!GOOD_EASY_HANDLE(easy->easy_handle))
return CURLM_BAD_EASY_HANDLE;
@ -974,6 +976,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Make sure we set the connection's current owner */
easy->easy_conn->data = data;
if(easy->easy_conn && (easy->state >= CURLM_STATE_CONNECT)) {
/* we need to wait for the connect state as only then is the
start time stored */
timeout_ms = Curl_timeleft(easy->easy_conn, &now,
easy->state <= CURLM_STATE_WAITDO);
if(timeout_ms < 0) {
/* Handle timed out */
easy->result = CURLE_OPERATION_TIMEDOUT;
multistate(easy, CURLM_STATE_COMPLETED);
break;
}
}
switch(easy->state) {
case CURLM_STATE_INIT:
/* init this transfer. */
@ -1385,7 +1402,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if( (data->set.max_send_speed > 0) &&
(data->progress.ulspeed > data->set.max_send_speed) ) {
int buffersize;
long timeout_ms;
multistate(easy, CURLM_STATE_TOOFAST);
@ -1402,7 +1418,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if( (data->set.max_recv_speed > 0) &&
(data->progress.dlspeed > data->set.max_recv_speed) ) {
int buffersize;
long timeout_ms;
multistate(easy, CURLM_STATE_TOOFAST);
@ -1666,7 +1681,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
}
do
result = multi_runsingle(multi, easy);
result = multi_runsingle(multi, now, easy);
while (CURLM_CALL_MULTI_PERFORM == result);
if(easy->easy_handle->set.wildcardmatch) {
@ -2032,6 +2047,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
CURLMcode result = CURLM_OK;
struct SessionHandle *data = NULL;
struct Curl_tree *t;
struct timeval now = Curl_tvnow();
if(checkall) {
struct Curl_one_easy *easyp;
@ -2086,7 +2102,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
data->set.one_easy->easy_conn->cselect_bits = ev_bitmask;
do
result = multi_runsingle(multi, data->set.one_easy);
result = multi_runsingle(multi, now, data->set.one_easy);
while (CURLM_CALL_MULTI_PERFORM == result);
if(data->set.one_easy->easy_conn)
@ -2106,18 +2122,23 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
}
}
now.tv_usec += 40000; /* compensate for bad precision timers that might've
triggered too early */
if(now.tv_usec > 1000000) {
now.tv_sec++;
now.tv_usec -= 1000000;
}
/*
* The loop following here will go on as long as there are expire-times left
* to process in the splay and 'data' will be re-assigned for every expired
* handle we deal with.
*/
do {
struct timeval now;
/* the first loop lap 'data' can be NULL */
if(data) {
do
result = multi_runsingle(multi, data->set.one_easy);
result = multi_runsingle(multi, now, data->set.one_easy);
while (CURLM_CALL_MULTI_PERFORM == result);
if(CURLM_OK >= result)
@ -2129,16 +2150,11 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
/* Check if there's one (more) expired timer to deal with! This function
extracts a matching node if there is one */
now = Curl_tvnow();
now.tv_usec += 40000; /* compensate for bad precision timers */
if(now.tv_usec > 1000000) {
now.tv_sec++;
now.tv_usec -= 1000000;
}
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
if(t)
if(t) {
data = t->payload; /* assign this for next loop */
(void)add_next_timeout(now, multi, t->payload);
}
} while(t);
@ -2273,12 +2289,22 @@ CURLMcode curl_multi_timeout(CURLM *multi_handle,
static int update_timer(struct Curl_multi *multi)
{
long timeout_ms;
if(!multi->timer_cb)
return 0;
if( multi_timeout(multi, &timeout_ms) != CURLM_OK )
if(multi_timeout(multi, &timeout_ms)) {
return -1;
if( timeout_ms < 0 )
}
if( timeout_ms < 0 ) {
static const struct timeval none={0,0};
if(Curl_splaycomparekeys(none, multi->timer_lastcall)) {
multi->timer_lastcall = none;
/* there's no timeout now but there was one previously, tell the app to
disable it */
return multi->timer_cb((CURLM*)multi, -1, multi->timer_userp);
}
return 0;
}
/* When multi_timeout() is done, multi->timetree points to the node with the
* timeout we got the (relative) time-out time for. We can thus easily check
@ -2564,10 +2590,6 @@ void Curl_expire(struct SessionHandle *data, long milli)
}
*nowp = set;
#if 0
infof(data, "Expire at %ld / %ld (%ldms) %p\n",
(long)nowp->tv_sec, (long)nowp->tv_usec, milli, data);
#endif
data->state.timenode.payload = data;
multi->timetree = Curl_splayinsert(*nowp,
multi->timetree,