Loading CHANGES +5 −0 Original line number Diff line number Diff line Loading @@ -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: Loading RELEASE-NOTES +2 −1 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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) lib/multi.c +58 −19 Original line number Diff line number Diff line Loading @@ -189,7 +189,7 @@ 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, static void moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle, struct connectdata *conn); static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); Loading Loading @@ -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++; Loading Loading @@ -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); /* When we follow redirects, must to go back to the CONNECT state */ if(easy->easy_handle->req.newurl || retry) { /* 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) { if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ Loading Loading @@ -1331,9 +1337,9 @@ 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 */ /* 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; } Loading Loading @@ -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; } Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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; Loading @@ -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, Loading Loading @@ -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, Loading Loading
CHANGES +5 −0 Original line number Diff line number Diff line Loading @@ -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: Loading
RELEASE-NOTES +2 −1 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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)
lib/multi.c +58 −19 Original line number Diff line number Diff line Loading @@ -189,7 +189,7 @@ 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, static void moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle, struct connectdata *conn); static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); Loading Loading @@ -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++; Loading Loading @@ -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); /* When we follow redirects, must to go back to the CONNECT state */ if(easy->easy_handle->req.newurl || retry) { /* 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) { if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ Loading Loading @@ -1331,9 +1337,9 @@ 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 */ /* 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; } Loading Loading @@ -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; } Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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; Loading @@ -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, Loading Loading @@ -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, Loading