aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2010-09-01 16:52:23 +0200
committerDaniel Stenberg <daniel@haxx.se>2010-09-01 16:52:23 +0200
commitca10e28f06f168ec433d376ee8fd64a25b317f0f (patch)
treec2c88dafe0c5ba7aa9a467957bf58539b05905f0
parent5e920157112923417faf901b08098b65d82f7746 (diff)
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.
-rw-r--r--lib/multi.c64
1 files changed, 43 insertions, 21 deletions
diff --git a/lib/multi.c b/lib/multi.c
index 5a5470246..13915c120 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -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,