Update libcurl from 7.54.1 to 7.55.1.
Bug: http://b/64610131
Test: builds, boots, `vendor/google/tools/fake-ota on streaming` works
Change-Id: I7ecaf9c83e9496ac4a379507791bec637deaa4cb
diff --git a/lib/multi.c b/lib/multi.c
index 950b600..d5bc532 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -44,6 +44,7 @@
#include "sigpipe.h"
#include "vtls/vtls.h"
#include "connect.h"
+#include "http_proxy.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -69,7 +70,7 @@
struct Curl_easy *data);
static int update_timer(struct Curl_multi *multi);
-static CURLMcode add_next_timeout(struct timeval now,
+static CURLMcode add_next_timeout(struct curltime now,
struct Curl_multi *multi,
struct Curl_easy *d);
static CURLMcode multi_timeout(struct Curl_multi *multi,
@@ -99,8 +100,6 @@
};
#endif
-static void multi_freetimeout(void *a, void *b);
-
/* function pointer called once when switching TO a state */
typedef void (*init_multistate_func)(struct Curl_easy *data);
@@ -116,6 +115,13 @@
NULL,
NULL,
Curl_init_CONNECT, /* CONNECT */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ Curl_connect_free /* DO */
/* the rest is NULL too */
};
@@ -280,9 +286,8 @@
static CURLMcode multi_addmsg(struct Curl_multi *multi,
struct Curl_message *msg)
{
- if(!Curl_llist_insert_next(multi->msglist, multi->msglist->tail, msg))
- return CURLM_OUT_OF_MEMORY;
-
+ Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg,
+ &msg->list);
return CURLM_OK;
}
@@ -316,13 +321,8 @@
if(Curl_conncache_init(&multi->conn_cache, chashsize))
goto error;
- multi->msglist = Curl_llist_alloc(multi_freeamsg);
- if(!multi->msglist)
- goto error;
-
- multi->pending = Curl_llist_alloc(multi_freeamsg);
- if(!multi->pending)
- goto error;
+ Curl_llist_init(&multi->msglist, multi_freeamsg);
+ Curl_llist_init(&multi->pending, multi_freeamsg);
/* allocate a new easy handle to use when closing cached connections */
multi->closure_handle = curl_easy_init();
@@ -345,8 +345,8 @@
Curl_conncache_destroy(&multi->conn_cache);
Curl_close(multi->closure_handle);
multi->closure_handle = NULL;
- Curl_llist_destroy(multi->msglist, NULL);
- Curl_llist_destroy(multi->pending, NULL);
+ Curl_llist_destroy(&multi->msglist, NULL);
+ Curl_llist_destroy(&multi->pending, NULL);
free(multi);
return NULL;
@@ -361,8 +361,6 @@
CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
struct Curl_easy *data)
{
- struct curl_llist *timeoutlist;
-
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -376,10 +374,8 @@
if(data->multi)
return CURLM_ADDED_ALREADY;
- /* Allocate and initialize timeout list for easy handle */
- timeoutlist = Curl_llist_alloc(multi_freetimeout);
- if(!timeoutlist)
- return CURLM_OUT_OF_MEMORY;
+ /* Initialize timeout list for this handle */
+ Curl_llist_init(&data->state.timeoutlist, NULL);
/*
* No failure allowed in this function beyond this point. And no
@@ -388,10 +384,6 @@
* function no matter what.
*/
- /* Make easy handle use timeout list initialized above */
- data->state.timeoutlist = timeoutlist;
- timeoutlist = NULL;
-
/* set the easy handle */
multistate(data, CURLM_STATE_INIT);
@@ -444,7 +436,7 @@
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(data, 0);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
/* increase the node-counter */
multi->num_easy++;
@@ -532,6 +524,7 @@
CURLcode result;
struct connectdata *conn;
struct Curl_easy *data;
+ unsigned int i;
DEBUGASSERT(*connp);
@@ -579,7 +572,7 @@
result = CURLE_ABORTED_BY_CALLBACK;
}
- if(conn->send_pipe->size + conn->recv_pipe->size != 0 &&
+ if(conn->send_pipe.size + conn->recv_pipe.size != 0 &&
!data->set.reuse_forbid &&
!conn->bits.close) {
/* Stop if pipeline is not empty and we do not have to close
@@ -598,9 +591,11 @@
}
/* if the transfer was completed in a paused state there can be buffered
- data left to write and then kill */
- free(data->state.tempwrite);
- data->state.tempwrite = NULL;
+ data left to free */
+ for(i=0; i < data->state.tempcount; i++) {
+ free(data->state.tempwrite[i].buf);
+ }
+ data->state.tempcount = 0;
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this connection. This is ignored for requests taking
@@ -638,7 +633,10 @@
infof(data, "Connection #%ld to host %s left intact\n",
conn->connection_id,
- conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
+ conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
+ conn->bits.httpproxy ? conn->http_proxy.host.dispname :
+ conn->bits.conn_to_host ? conn->conn_to_host.dispname :
+ conn->host.dispname);
}
else
data->state.lastconnect = NULL;
@@ -692,13 +690,13 @@
if(data->easy_conn &&
data->mstate > CURLM_STATE_DO &&
data->mstate < CURLM_STATE_COMPLETED) {
+ /* Set connection owner so that the DONE function closes it. We can
+ safely do this here since connection is killed. */
+ data->easy_conn->data = easy;
/* If the handle is in a pipeline and has started sending off its
request but not received its response yet, we need to close
connection. */
streamclose(data->easy_conn, "Removed with partial response");
- /* Set connection owner so that the DONE function closes it. We can
- safely do this here since connection is killed. */
- data->easy_conn->data = easy;
easy_owns_conn = TRUE;
}
@@ -735,10 +733,7 @@
/* destroy the timeout list that is held in the easy handle, do this *after*
multi_done() as that may actually call Curl_expire that uses this */
- if(data->state.timeoutlist) {
- Curl_llist_destroy(data->state.timeoutlist, NULL);
- data->state.timeoutlist = NULL;
- }
+ Curl_llist_destroy(&data->state.timeoutlist, NULL);
/* as this was using a shared connection cache we clear the pointer to that
since we're not part of that multi handle anymore */
@@ -761,11 +756,11 @@
/* make sure there's no pending message in the queue sent from this easy
handle */
- for(e = multi->msglist->head; e; e = e->next) {
+ for(e = multi->msglist.head; e; e = e->next) {
struct Curl_message *msg = e->ptr;
if(msg->extmsg.easy_handle == easy) {
- Curl_llist_remove(multi->msglist, e, NULL);
+ Curl_llist_remove(&multi->msglist, e, NULL);
/* there can only be one from this specific handle */
break;
}
@@ -839,7 +834,7 @@
/* when we've sent a CONNECT to a proxy, we should rather wait for the
socket to become readable to be able to get the response headers */
- if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+ if(conn->connect_state)
return GETSOCK_READSOCK(0);
return GETSOCK_WRITESOCK(0);
@@ -956,10 +951,8 @@
if(s == CURL_SOCKET_BAD)
/* this socket is unused, break out of loop */
break;
- else {
- if((int)s > this_max_fd)
- this_max_fd = (int)s;
- }
+ if((int)s > this_max_fd)
+ this_max_fd = (int)s;
}
data = data->next; /* check next handle */
@@ -970,6 +963,8 @@
return CURLM_OK;
}
+#define NUM_POLLS_ON_STACK 10
+
CURLMcode curl_multi_wait(struct Curl_multi *multi,
struct curl_waitfd extra_fds[],
unsigned int extra_nfds,
@@ -983,8 +978,10 @@
unsigned int nfds = 0;
unsigned int curlfds;
struct pollfd *ufds = NULL;
+ bool ufds_malloc = FALSE;
long timeout_internal;
int retcode = 0;
+ struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -1023,10 +1020,19 @@
curlfds = nfds; /* number of internal file descriptors */
nfds += extra_nfds; /* add the externally provided ones */
- if(nfds || extra_nfds) {
- ufds = malloc(nfds * sizeof(struct pollfd));
- if(!ufds)
- return CURLM_OUT_OF_MEMORY;
+ if(nfds) {
+ if(nfds > NUM_POLLS_ON_STACK) {
+ /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
+ big, so at 2^29 sockets this value might wrap. When a process gets
+ the capability to actually handle over 500 million sockets this
+ calculation needs a integer overflow check. */
+ ufds = malloc(nfds * sizeof(struct pollfd));
+ if(!ufds)
+ return CURLM_OUT_OF_MEMORY;
+ ufds_malloc = TRUE;
+ }
+ else
+ ufds = &a_few_on_stack[0];
}
nfds = 0;
@@ -1104,7 +1110,8 @@
}
}
- free(ufds);
+ if(ufds_malloc)
+ free(ufds);
if(ret)
*ret = retcode;
return CURLM_OK;
@@ -1294,7 +1301,7 @@
}
static CURLMcode multi_runsingle(struct Curl_multi *multi,
- struct timeval now,
+ struct curltime now,
struct Curl_easy *data)
{
struct Curl_message *msg = NULL;
@@ -1307,6 +1314,8 @@
CURLcode result = CURLE_OK;
struct SingleRequest *k;
time_t timeout_ms;
+ time_t recv_timeout_ms;
+ time_t send_timeout_ms;
int control;
if(!GOOD_EASY_HANDLE(data))
@@ -1432,10 +1441,9 @@
multistate(data, CURLM_STATE_CONNECT_PEND);
/* add this handle to the list of connect-pending handles */
- if(!Curl_llist_insert_next(multi->pending, multi->pending->tail, data))
- result = CURLE_OUT_OF_MEMORY;
- else
- result = CURLE_OK;
+ Curl_llist_insert_next(&multi->pending, multi->pending.tail, data,
+ &data->connect_queue);
+ result = CURLE_OK;
break;
}
@@ -1459,7 +1467,7 @@
CURLM_STATE_WAITDO:CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
- if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+ if(Curl_connect_ongoing(data->easy_conn))
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
@@ -1477,8 +1485,8 @@
struct connectdata *conn = data->easy_conn;
const char *hostname;
- if(conn->bits.proxy)
- hostname = conn->proxy.name;
+ if(conn->bits.httpproxy)
+ hostname = conn->http_proxy.host.name;
else if(conn->bits.conn_to_host)
hostname = conn->conn_to_host.name;
else
@@ -1524,7 +1532,7 @@
CURLM_STATE_WAITDO:CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
- if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+ if(Curl_connect_ongoing(data->easy_conn))
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
@@ -1556,7 +1564,7 @@
else if(!result) {
if((data->easy_conn->http_proxy.proxytype != CURLPROXY_HTTPS ||
data->easy_conn->bits.proxy_ssl_connected[FIRSTSOCKET]) &&
- (data->easy_conn->tunnel_state[FIRSTSOCKET] != TUNNEL_CONNECT)) {
+ Curl_connect_complete(data->easy_conn)) {
rc = CURLM_CALL_MULTI_PERFORM;
/* initiate protocol connect phase */
multistate(data, CURLM_STATE_SENDPROTOCONNECT);
@@ -1572,7 +1580,7 @@
#ifndef CURL_DISABLE_HTTP
if((data->easy_conn->http_proxy.proxytype == CURLPROXY_HTTPS &&
!data->easy_conn->bits.proxy_ssl_connected[FIRSTSOCKET]) ||
- (data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)) {
+ Curl_connect_ongoing(data->easy_conn)) {
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
break;
}
@@ -1719,20 +1727,18 @@
else {
/* Follow failed */
result = drc;
- free(newurl);
}
}
else {
/* done didn't return OK or SEND_ERROR */
result = drc;
- free(newurl);
}
}
else {
/* Have error handler disconnect conn if we can't retry */
stream_error = TRUE;
- free(newurl);
}
+ free(newurl);
}
else {
/* failure detected */
@@ -1826,19 +1832,30 @@
else
result = Curl_speedcheck(data, now);
- if(( (data->set.max_send_speed == 0) ||
- (Curl_pgrsLimitWaitTime(data->progress.uploaded,
- data->progress.ul_limit_size,
- data->set.max_send_speed,
- data->progress.ul_limit_start,
- now) <= 0)) &&
- ( (data->set.max_recv_speed == 0) ||
- (Curl_pgrsLimitWaitTime(data->progress.downloaded,
- data->progress.dl_limit_size,
- data->set.max_recv_speed,
- data->progress.dl_limit_start,
- now) <= 0)))
- multistate(data, CURLM_STATE_PERFORM);
+ if(!result) {
+ send_timeout_ms = 0;
+ if(data->set.max_send_speed > 0)
+ send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ now);
+
+ recv_timeout_ms = 0;
+ if(data->set.max_recv_speed > 0)
+ recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ now);
+
+ if(send_timeout_ms <= 0 && recv_timeout_ms <= 0)
+ multistate(data, CURLM_STATE_PERFORM);
+ else if(send_timeout_ms >= recv_timeout_ms)
+ Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST);
+ else
+ Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST);
+ }
break;
case CURLM_STATE_PERFORM:
@@ -1848,31 +1865,30 @@
bool comeback = FALSE;
/* check if over send speed */
- if(data->set.max_send_speed > 0) {
- timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded,
- data->progress.ul_limit_size,
- data->set.max_send_speed,
- data->progress.ul_limit_start,
- now);
- if(timeout_ms > 0) {
- multistate(data, CURLM_STATE_TOOFAST);
- Curl_expire_latest(data, timeout_ms);
- break;
- }
- }
+ send_timeout_ms = 0;
+ if(data->set.max_send_speed > 0)
+ send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ now);
/* check if over recv speed */
- if(data->set.max_recv_speed > 0) {
- timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded,
- data->progress.dl_limit_size,
- data->set.max_recv_speed,
- data->progress.dl_limit_start,
- now);
- if(timeout_ms > 0) {
- multistate(data, CURLM_STATE_TOOFAST);
- Curl_expire_latest(data, timeout_ms);
- break;
- }
+ recv_timeout_ms = 0;
+ if(data->set.max_recv_speed > 0)
+ recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ now);
+
+ if(send_timeout_ms > 0 || recv_timeout_ms > 0) {
+ multistate(data, CURLM_STATE_TOOFAST);
+ if(send_timeout_ms >= recv_timeout_ms)
+ Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST);
+ else
+ Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST);
+ break;
}
/* read/write data if it is ready to do so */
@@ -1928,11 +1944,11 @@
Curl_posttransfer(data);
/* we're no longer receiving */
- Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
+ Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe);
/* expire the new receiving pipeline head */
- if(data->easy_conn->recv_pipe->head)
- Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 0);
+ if(data->easy_conn->recv_pipe.head)
+ Curl_expire(data->easy_conn->recv_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
/* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi);
@@ -1956,9 +1972,6 @@
if(!result) {
multistate(data, CURLM_STATE_CONNECT);
rc = CURLM_CALL_MULTI_PERFORM;
- newurl = NULL; /* handed over the memory ownership to
- Curl_follow(), make sure we don't free() it
- here */
}
}
}
@@ -1972,9 +1985,7 @@
newurl = data->req.location;
data->req.location = NULL;
result = Curl_follow(data, newurl, FOLLOW_FAKE);
- if(!result)
- newurl = NULL; /* allocation was handed over Curl_follow() */
- else
+ if(result)
stream_error = TRUE;
}
@@ -1997,7 +2008,7 @@
CURLcode res;
/* Remove ourselves from the receive pipeline, if we are there. */
- Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
+ Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe);
/* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi);
@@ -2073,8 +2084,8 @@
/* if this has a connection, unsubscribe from the pipelines */
Curl_pipeline_leave_write(data->easy_conn);
Curl_pipeline_leave_read(data->easy_conn);
- Curl_removeHandleFromPipeline(data, data->easy_conn->send_pipe);
- Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
+ Curl_removeHandleFromPipeline(data, &data->easy_conn->send_pipe);
+ Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe);
if(stream_error) {
/* Don't attempt to send data over a connection that timed out */
@@ -2135,7 +2146,7 @@
struct Curl_easy *data;
CURLMcode returncode=CURLM_OK;
struct Curl_tree *t;
- struct timeval now = Curl_tvnow();
+ struct curltime now = Curl_tvnow();
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -2229,8 +2240,8 @@
Curl_hash_destroy(&multi->sockhash);
Curl_conncache_destroy(&multi->conn_cache);
- Curl_llist_destroy(multi->msglist, NULL);
- Curl_llist_destroy(multi->pending, NULL);
+ Curl_llist_destroy(&multi->msglist, NULL);
+ Curl_llist_destroy(&multi->pending, NULL);
/* remove all easy handles */
data = multi->easyp;
@@ -2262,8 +2273,7 @@
return CURLM_OK;
}
- else
- return CURLM_BAD_HANDLE;
+ return CURLM_BAD_HANDLE;
}
/*
@@ -2282,24 +2292,23 @@
*msgs_in_queue = 0; /* default to none */
- if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(multi->msglist)) {
+ if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(&multi->msglist)) {
/* there is one or more messages in the list */
struct curl_llist_element *e;
/* extract the head of the list to return */
- e = multi->msglist->head;
+ e = multi->msglist.head;
msg = e->ptr;
/* remove the extracted entry */
- Curl_llist_remove(multi->msglist, e, NULL);
+ Curl_llist_remove(&multi->msglist, e, NULL);
- *msgs_in_queue = curlx_uztosi(Curl_llist_count(multi->msglist));
+ *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist));
return &msg->extmsg;
}
- else
- return NULL;
+ return NULL;
}
/*
@@ -2394,7 +2403,7 @@
removed. */
struct connectdata *easy_conn = data->easy_conn;
if(easy_conn) {
- if(easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) {
+ if(easy_conn->recv_pipe.size > 1) {
/* the handle should not be removed from the pipe yet */
remove_sock_from_hash = FALSE;
@@ -2403,12 +2412,12 @@
isn't already) */
if(entry->easy == data) {
if(Curl_recvpipe_head(data, easy_conn))
- entry->easy = easy_conn->recv_pipe->head->next->ptr;
+ entry->easy = easy_conn->recv_pipe.head->next->ptr;
else
- entry->easy = easy_conn->recv_pipe->head->ptr;
+ entry->easy = easy_conn->recv_pipe.head->ptr;
}
}
- if(easy_conn->send_pipe && easy_conn->send_pipe->size > 1) {
+ if(easy_conn->send_pipe.size > 1) {
/* the handle should not be removed from the pipe yet */
remove_sock_from_hash = FALSE;
@@ -2417,9 +2426,9 @@
isn't already) */
if(entry->easy == data) {
if(Curl_sendpipe_head(data, easy_conn))
- entry->easy = easy_conn->send_pipe->head->next->ptr;
+ entry->easy = easy_conn->send_pipe.head->next->ptr;
else
- entry->easy = easy_conn->send_pipe->head->ptr;
+ entry->easy = easy_conn->send_pipe.head->ptr;
}
}
/* Don't worry about overwriting recv_pipe head with send_pipe_head,
@@ -2475,8 +2484,6 @@
}
}
-
-
/*
* add_next_timeout()
*
@@ -2489,20 +2496,23 @@
* 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,
+static CURLMcode add_next_timeout(struct curltime now,
struct Curl_multi *multi,
struct Curl_easy *d)
{
- struct timeval *tv = &d->state.expiretime;
- struct curl_llist *list = d->state.timeoutlist;
+ struct curltime *tv = &d->state.expiretime;
+ struct curl_llist *list = &d->state.timeoutlist;
struct curl_llist_element *e;
+ struct time_node *node = NULL;
/* 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;
- time_t diff = curlx_tvdiff(*(struct timeval *)e->ptr, now);
+ time_t diff;
+ node = (struct time_node *)e->ptr;
+ diff = curlx_tvdiff(node->time, now);
if(diff <= 0)
/* remove outdated entry */
Curl_llist_remove(list, e, NULL);
@@ -2520,12 +2530,10 @@
}
else {
/* copy the first entry to 'tv' */
- memcpy(tv, e->ptr, sizeof(*tv));
+ memcpy(tv, &node->time, sizeof(*tv));
- /* remove first entry from list */
- Curl_llist_remove(list, e, NULL);
-
- /* insert this node again into the splay */
+ /* Insert this node again into the splay. Keep the timer in the list in
+ case we need to recompute future timers. */
multi->timetree = Curl_splayinsert(*tv, multi->timetree,
&d->state.timenode);
}
@@ -2541,7 +2549,7 @@
CURLMcode result = CURLM_OK;
struct Curl_easy *data = NULL;
struct Curl_tree *t;
- struct timeval now = Curl_tvnow();
+ struct curltime now = Curl_tvnow();
if(checkall) {
/* *perform() deals with running_handles on its own */
@@ -2560,7 +2568,7 @@
/* or should we fall-through and do the timer-based stuff? */
return result;
}
- else if(s != CURL_SOCKET_TIMEOUT) {
+ if(s != CURL_SOCKET_TIMEOUT) {
struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
@@ -2585,13 +2593,11 @@
head. If we should read from the socket, take the recv_pipe head. */
if(data->easy_conn) {
if((ev_bitmask & CURL_POLL_OUT) &&
- data->easy_conn->send_pipe &&
- data->easy_conn->send_pipe->head)
- data = data->easy_conn->send_pipe->head->ptr;
+ data->easy_conn->send_pipe.head)
+ data = data->easy_conn->send_pipe.head->ptr;
else if((ev_bitmask & CURL_POLL_IN) &&
- data->easy_conn->recv_pipe &&
- data->easy_conn->recv_pipe->head)
- data = data->easy_conn->recv_pipe->head->ptr;
+ data->easy_conn->recv_pipe.head)
+ data = data->easy_conn->recv_pipe.head->ptr;
}
if(data->easy_conn &&
@@ -2769,11 +2775,11 @@
static CURLMcode multi_timeout(struct Curl_multi *multi,
long *timeout_ms)
{
- static struct timeval tv_zero = {0, 0};
+ static struct curltime tv_zero = {0, 0};
if(multi->timetree) {
/* we have a tree of expire times */
- struct timeval now = Curl_tvnow();
+ struct curltime now = Curl_tvnow();
/* splay the lowest to the bottom */
multi->timetree = Curl_splay(tv_zero, multi->timetree);
@@ -2825,7 +2831,7 @@
return -1;
}
if(timeout_ms < 0) {
- static const struct timeval none={0, 0};
+ static const struct curltime 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
@@ -2848,17 +2854,23 @@
}
/*
- * multi_freetimeout()
+ * multi_deltimeout()
*
- * Callback used by the llist system when a single timeout list entry is
- * destroyed.
+ * Remove a given timestamp from the list of timeouts.
*/
-static void multi_freetimeout(void *user, void *entryptr)
+static void
+multi_deltimeout(struct Curl_easy *data, expire_id eid)
{
- (void)user;
-
- /* the entry was plain malloc()'ed */
- free(entryptr);
+ struct curl_llist_element *e;
+ struct curl_llist *timeoutlist = &data->state.timeoutlist;
+ /* find and remove the specific node from the list */
+ for(e = timeoutlist->head; e; e = e->next) {
+ struct time_node *n = (struct time_node *)e->ptr;
+ if(n->eid == eid) {
+ Curl_llist_remove(timeoutlist, e, NULL);
+ return;
+ }
+ }
}
/*
@@ -2869,25 +2881,28 @@
*
*/
static CURLMcode
-multi_addtimeout(struct curl_llist *timeoutlist,
- struct timeval *stamp)
+multi_addtimeout(struct Curl_easy *data,
+ struct curltime *stamp,
+ expire_id eid)
{
struct curl_llist_element *e;
- struct timeval *timedup;
+ struct time_node *node;
struct curl_llist_element *prev = NULL;
+ size_t n;
+ struct curl_llist *timeoutlist = &data->state.timeoutlist;
- timedup = malloc(sizeof(*timedup));
- if(!timedup)
- return CURLM_OUT_OF_MEMORY;
+ node = &data->state.expires[eid];
- /* copy the timestamp */
- memcpy(timedup, stamp, sizeof(*timedup));
+ /* copy the timestamp and id */
+ memcpy(&node->time, stamp, sizeof(*stamp));
+ node->eid = eid; /* also marks it as in use */
- if(Curl_llist_count(timeoutlist)) {
+ n = Curl_llist_count(timeoutlist);
+ if(n) {
/* find the correct spot in the list */
for(e = timeoutlist->head; e; e = e->next) {
- struct timeval *checktime = e->ptr;
- time_t diff = curlx_tvdiff(*checktime, *timedup);
+ struct time_node *check = (struct time_node *)e->ptr;
+ time_t diff = curlx_tvdiff(check->time, node->time);
if(diff > 0)
break;
prev = e;
@@ -2897,11 +2912,7 @@
/* else
this is the first timeout on the list */
- if(!Curl_llist_insert_next(timeoutlist, prev, timedup)) {
- free(timedup);
- return CURLM_OUT_OF_MEMORY;
- }
-
+ Curl_llist_insert_next(timeoutlist, prev, node, &node->list);
return CURLM_OK;
}
@@ -2913,44 +2924,51 @@
*
* The timeout will be added to a queue of timeouts if it defines a moment in
* time that is later than the current head of queue.
+ *
+ * Expire replaces a former timeout using the same id if already set.
*/
-void Curl_expire(struct Curl_easy *data, time_t milli)
+void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id)
{
struct Curl_multi *multi = data->multi;
- struct timeval *nowp = &data->state.expiretime;
+ struct curltime *nowp = &data->state.expiretime;
int rc;
- struct timeval set;
+ struct curltime set;
/* this is only interesting while there is still an associated multi struct
remaining! */
if(!multi)
return;
+ DEBUGASSERT(id < EXPIRE_LAST);
+
set = Curl_tvnow();
- set.tv_sec += (long)(milli/1000);
- set.tv_usec += (milli%1000)*1000;
+ set.tv_sec += milli/1000;
+ set.tv_usec += (unsigned int)(milli%1000)*1000;
if(set.tv_usec >= 1000000) {
set.tv_sec++;
set.tv_usec -= 1000000;
}
+ /* Remove any timer with the same id just in case. */
+ multi_deltimeout(data, id);
+
+ /* Add it to the timer list. It must stay in the list until it has expired
+ in case we need to recompute the minimum timer later. */
+ multi_addtimeout(data, &set, id);
+
if(nowp->tv_sec || nowp->tv_usec) {
/* This means that the struct is added as a node in the splay tree.
Compare if the new time is earlier, and only remove-old/add-new if it
is. */
time_t diff = curlx_tvdiff(set, *nowp);
+
if(diff > 0) {
- /* the new expire time was later so just add it to the queue
- and get out */
- multi_addtimeout(data->state.timeoutlist, &set);
+ /* The current splay tree entry is sooner than this new expiry time.
+ We don't need to update our splay tree entry. */
return;
}
- /* the new time is newer than the presently set one, so add the current
- to the queue and update the head */
- multi_addtimeout(data->state.timeoutlist, nowp);
-
/* Since this is an updated time, we must remove the previous entry from
the splay tree first and then re-add the new value */
rc = Curl_splayremovebyaddr(multi->timetree,
@@ -2960,6 +2978,8 @@
infof(data, "Internal error removing splay node = %d\n", rc);
}
+ /* Indicate that we are in the splay tree and insert the new timer expiry
+ value since it is our local minimum. */
*nowp = set;
data->state.timenode.payload = data;
multi->timetree = Curl_splayinsert(*nowp, multi->timetree,
@@ -2967,46 +2987,17 @@
}
/*
- * Curl_expire_latest()
+ * Curl_expire_done()
*
- * This is like Curl_expire() but will only add a timeout node to the list of
- * timers if there is no timeout that will expire before the given time.
- *
- * Use this function if the code logic risks calling this function many times
- * or if there's no particular conditional wait in the code for this specific
- * time-out period to expire.
+ * Removes the expire timer. Marks it as done.
*
*/
-void Curl_expire_latest(struct Curl_easy *data, time_t milli)
+void Curl_expire_done(struct Curl_easy *data, expire_id id)
{
- struct timeval *expire = &data->state.expiretime;
-
- struct timeval set;
-
- set = Curl_tvnow();
- set.tv_sec += (long)(milli / 1000);
- set.tv_usec += (milli % 1000) * 1000;
-
- if(set.tv_usec >= 1000000) {
- set.tv_sec++;
- set.tv_usec -= 1000000;
- }
-
- if(expire->tv_sec || expire->tv_usec) {
- /* This means that the struct is added as a node in the splay tree.
- Compare if the new time is earlier, and only remove-old/add-new if it
- is. */
- time_t diff = curlx_tvdiff(set, *expire);
- if(diff > 0)
- /* the new expire time was later than the top time, so just skip this */
- return;
- }
-
- /* Just add the timeout like normal */
- Curl_expire(data, milli);
+ /* remove the timer, if there */
+ multi_deltimeout(data, id);
}
-
/*
* Curl_expire_clear()
*
@@ -3015,7 +3006,7 @@
void Curl_expire_clear(struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
- struct timeval *nowp = &data->state.expiretime;
+ struct curltime *nowp = &data->state.expiretime;
int rc;
/* this is only interesting while there is still an associated multi struct
@@ -3026,7 +3017,7 @@
if(nowp->tv_sec || nowp->tv_usec) {
/* Since this is an cleared time, we must remove the previous entry from
the splay tree */
- struct curl_llist *list = data->state.timeoutlist;
+ struct curl_llist *list = &data->state.timeoutlist;
rc = Curl_splayremovebyaddr(multi->timetree,
&data->state.timenode,
@@ -3035,8 +3026,9 @@
infof(data, "Internal error clearing splay node = %d\n", rc);
/* flush the timeout list too */
- while(list->size > 0)
+ while(list->size > 0) {
Curl_llist_remove(list, list->tail, NULL);
+ }
#ifdef DEBUGBUILD
infof(data, "Expire cleared\n");
@@ -3086,17 +3078,17 @@
struct curl_llist *Curl_multi_pipelining_site_bl(struct Curl_multi *multi)
{
- return multi->pipelining_site_bl;
+ return &multi->pipelining_site_bl;
}
struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi)
{
- return multi->pipelining_server_bl;
+ return &multi->pipelining_server_bl;
}
void Curl_multi_process_pending_handles(struct Curl_multi *multi)
{
- struct curl_llist_element *e = multi->pending->head;
+ struct curl_llist_element *e = multi->pending.head;
while(e) {
struct Curl_easy *data = e->ptr;
@@ -3106,10 +3098,10 @@
multistate(data, CURLM_STATE_CONNECT);
/* Remove this node from the list */
- Curl_llist_remove(multi->pending, e, NULL);
+ Curl_llist_remove(&multi->pending, e, NULL);
/* Make sure that the handle will be processed soonish. */
- Curl_expire_latest(data, 0);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
e = next; /* operate on next handle */