summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2008-05-28 20:56:19 +0000
committerDaniel Stenberg <daniel@haxx.se>2008-05-28 20:56:19 +0000
commitec4f6e93c27f899c219863fab9b48863fc2d3600 (patch)
tree94ace01c33693215539fce273c94db41bf131fa1
parentb49dcfb52b5910c63b8f8c3e02eb32904d35fb11 (diff)
downloadgnurl-ec4f6e93c27f899c219863fab9b48863fc2d3600.tar.gz
gnurl-ec4f6e93c27f899c219863fab9b48863fc2d3600.tar.bz2
gnurl-ec4f6e93c27f899c219863fab9b48863fc2d3600.zip
- Emil Romanus found a problem and helped me repeat it. It occured when using
the curl_multi_socket() API with HTTP pipelining enabled and could lead to the pipeline basically stalling for a very long period of time until it took off again.
-rw-r--r--CHANGES5
-rw-r--r--RELEASE-NOTES3
-rw-r--r--lib/multi.c77
3 files changed, 65 insertions, 20 deletions
diff --git a/CHANGES b/CHANGES
index 7e7f99727..6ff59e060 100644
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,11 @@
Daniel Stenberg (28 May 2008)
+- Emil Romanus found a problem and helped me repeat it. It occured when using
+ the curl_multi_socket() API with HTTP pipelining enabled and could lead to
+ the pipeline basically stalling for a very long period of time until it took
+ off again.
+
- Jeff Weber reported memory leaks with aborted SCP and SFTP transfers and
provided excellent repeat recipes. I fixed the cases I managed to reproduce
but Jeff still got some (SCP) problems even after these fixes:
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 7beb1d48e..117a6f8c6 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -39,6 +39,7 @@ This release includes the following bugfixes:
o builds fine for Haiku OS
o follow redirect with only a new query string
o SCP and SFTP memory leaks on aborted transfers
+ o curl_multi_socket() and HTTP pipelining transfer stalls
This release includes the following known bugs:
@@ -61,6 +62,6 @@ advice from friends like these:
Rafa Muyo, Andre Guibert de Bruet, Brock Noland, Sandor Feldi, Stefan Krause,
David Shaw, Norbert Frese, Bart Whiteley, Jean-Francois Bertrand, Ben Van Hof,
Yuriy Sosov, Christopher Palow, Yang Tse, Liam Healy, Nikolai Kondrashov,
- David Rosenstrauch, Andreas Faerber, Scott McCreary, Jeff Weber
+ David Rosenstrauch, Andreas Faerber, Scott McCreary, Jeff Weber, Emil Romanus
Thanks! (and sorry if I forgot to mention someone)
diff --git a/lib/multi.c b/lib/multi.c
index 3b6e4aeb2..9349ee6c6 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -189,8 +189,8 @@ static int update_timer(struct Curl_multi *multi);
static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
struct connectdata *conn);
static int checkPendPipeline(struct connectdata *conn);
-static int moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle,
- struct connectdata *conn);
+static void moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle,
+ struct connectdata *conn);
static bool isHandleAtHead(struct SessionHandle *handle,
struct curl_llist *pipeline);
@@ -505,7 +505,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
sockets that time-out or have actions will be dealt with. Since this
handle has no action yet, we make sure it times out to get things to
happen. */
- Curl_expire(easy->easy_handle, 10);
+ Curl_expire(easy->easy_handle, 1);
/* increase the node-counter */
multi->num_easy++;
@@ -1286,13 +1286,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* call this even if the readwrite function returned error */
Curl_posttransfer(easy->easy_handle);
+ /* we're no longer receving */
+ Curl_removeHandleFromPipeline(easy->easy_handle,
+ easy->easy_conn->recv_pipe);
+
+ /* expire the new receiving pipeline head */
+ if(easy->easy_conn->recv_pipe->head)
+ Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1);
+
+ /* Check if we can move pending requests to send pipe */
+ checkPendPipeline(easy->easy_conn);
+
/* When we follow redirects, must to go back to the CONNECT state */
if(easy->easy_handle->req.newurl || retry) {
- Curl_removeHandleFromPipeline(easy->easy_handle,
- easy->easy_conn->recv_pipe);
- /* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
-
if(!retry) {
/* if the URL is a follow-location and not just a retried request
then figure out the URL here */
@@ -1331,10 +1337,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
checkPendPipeline(easy->easy_conn);
if(easy->easy_conn->bits.stream_was_rewound) {
- /* This request read past its response boundary so we quickly
- let the other requests consume those bytes since there is no
- guarantee that the socket will become active again */
- result = CURLM_CALL_MULTI_PERFORM;
+ /* This request read past its response boundary so we quickly let the
+ other requests consume those bytes since there is no guarantee that
+ the socket will become active again */
+ result = CURLM_CALL_MULTI_PERFORM;
}
/* post-transfer command */
@@ -1431,7 +1437,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
get things to happen. Also, this makes it less important for callers of
the curl_multi_* functions to bother about the CURLM_CALL_MULTI_PERFORM
return code, as long as they deal with the timeouts properly. */
- Curl_expire(easy->easy_handle, 10);
+ Curl_expire(easy->easy_handle, 1);
return result;
}
@@ -1785,6 +1791,9 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
extracts a matching node if there is one */
now = Curl_tvnow();
+ now.tv_usec += 1000; /* to compensate for the truncating of 999us to 0ms,
+ we always add time here to make the comparison
+ below better */
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
if(t) {
@@ -1962,6 +1971,7 @@ static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
static int checkPendPipeline(struct connectdata *conn)
{
int result = 0;
+ struct curl_llist_element *sendhead = conn->send_pipe->head;
if (conn->server_supports_pipelining) {
size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
@@ -1979,10 +1989,27 @@ static int checkPendPipeline(struct connectdata *conn)
conn->now = Curl_tvnow();
}
+ if(result) {
+ /* something moved, check for a new send pipeline leader */
+ if(sendhead != conn->send_pipe->head) {
+ /* this is a new one as head, expire it */
+ conn->writechannel_inuse = FALSE; /* not in use yet */
+ infof(conn->data, "%p is at send pipe head!\n",
+ conn->send_pipe->head->ptr);
+ Curl_expire(conn->send_pipe->head->ptr, 1);
+ }
+ }
+
return result;
}
-static int moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
+/* Move this transfer from the sending list to the receiving list.
+
+ Pay special attention to the new sending list "leader" as it needs to get
+ checked to update what sockets it acts on.
+
+ */
+static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
struct connectdata *conn)
{
struct curl_llist_element *curr;
@@ -1992,12 +2019,24 @@ static int moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
if(curr->ptr == handle) {
Curl_llist_move(conn->send_pipe, curr,
conn->recv_pipe, conn->recv_pipe->tail);
- return 1; /* we moved a handle */
+
+ if(conn->send_pipe->head) {
+ /* Since there's a new easy handle at the start of the send pipeline,
+ set its timeout value to 1ms to make it trigger instantly */
+ conn->writechannel_inuse = FALSE; /* not used now */
+ infof(conn->data, "%p is at send pipe head B!\n",
+ conn->send_pipe->head->ptr);
+ Curl_expire(conn->send_pipe->head->ptr, 1);
+ }
+
+ /* The receiver's list is not really interesting here since either this
+ handle is now first in the list and we'll deal with it soon, or
+ another handle is already first and thus is already taken care of */
+
+ break; /* we're done! */
}
curr = curr->next;
}
-
- return 0;
}
static bool isHandleAtHead(struct SessionHandle *handle,
@@ -2073,8 +2112,8 @@ void Curl_expire(struct SessionHandle *data, long milli)
*nowp = set;
#if 0
- infof(data, "Expire at %ld / %ld (%ldms)\n",
- (long)nowp->tv_sec, (long)nowp->tv_usec, milli);
+ 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,