1
0
mirror of https://github.com/moparisthebest/curl synced 2025-03-11 07:39:50 -04:00

multi: assign IDs to all timers and make each timer singleton

A) reduces the timeout lists drastically

 B) prevents a lot of superfluous loops for timers that expires "in vain"
    when it has actually already been extended to fire later on
This commit is contained in:
Daniel Stenberg 2017-05-09 12:47:49 +02:00
parent 8dfa378e52
commit e9fd794a61
10 changed files with 98 additions and 39 deletions

View File

@ -232,7 +232,7 @@ int Curl_resolver_getsock(struct connectdata *conn,
milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000); milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
if(milli == 0) if(milli == 0)
milli += 10; milli += 10;
Curl_expire_latest(conn->data, milli); Curl_expire_latest(conn->data, milli, EXPIRE_ARES);
return max; return max;
} }

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -1070,7 +1070,7 @@ static CURLcode singleipconnect(struct connectdata *conn,
conn->connecttime = Curl_tvnow(); conn->connecttime = Curl_tvnow();
if(conn->num_addr > 1) if(conn->num_addr > 1)
Curl_expire_latest(data, conn->timeoutms_per_addr); Curl_expire_latest(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME);
/* Connect TCP sockets, bind UDP */ /* Connect TCP sockets, bind UDP */
if(!isconnected && (conn->socktype == SOCK_STREAM)) { if(!isconnected && (conn->socktype == SOCK_STREAM)) {
@ -1169,7 +1169,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
conn->tempaddr[1] = NULL; conn->tempaddr[1] = NULL;
conn->tempsock[0] = CURL_SOCKET_BAD; conn->tempsock[0] = CURL_SOCKET_BAD;
conn->tempsock[1] = CURL_SOCKET_BAD; conn->tempsock[1] = CURL_SOCKET_BAD;
Curl_expire(conn->data, HAPPY_EYEBALLS_TIMEOUT); Curl_expire(conn->data, HAPPY_EYEBALLS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS);
/* Max time for the next connection attempt */ /* Max time for the next connection attempt */
conn->timeoutms_per_addr = conn->timeoutms_per_addr =

View File

@ -1044,7 +1044,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
if(!result && if(!result &&
((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) ) (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) )
Curl_expire(data, 0); /* get this handle going again */ Curl_expire(data, 0, EXPIRE_UNPAUSE); /* get this handle going again */
return result; return result;
} }

View File

@ -580,10 +580,8 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
else { else {
/* Add timeout to multi handle and break out of the loop */ /* Add timeout to multi handle and break out of the loop */
if(!result && *connected == FALSE) { if(!result && *connected == FALSE) {
if(data->set.accepttimeout > 0) Curl_expire(data, data->set.accepttimeout > 0 ?
Curl_expire(data, data->set.accepttimeout); data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, 0);
else
Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
} }
} }

View File

@ -569,7 +569,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
/* if we receive data for another handle, wake that up */ /* if we receive data for another handle, wake that up */
if(conn_s->data != data_s) if(conn_s->data != data_s)
Curl_expire(data_s, 0); Curl_expire(data_s, 0, EXPIRE_H2DATA);
} }
break; break;
case NGHTTP2_PUSH_PROMISE: case NGHTTP2_PUSH_PROMISE:
@ -646,7 +646,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
/* if we receive data for another handle, wake that up */ /* if we receive data for another handle, wake that up */
if(conn->data != data_s) if(conn->data != data_s)
Curl_expire(data_s, 0); Curl_expire(data_s, 0, EXPIRE_H2DATA);
DEBUGF(infof(data_s, "%zu data received for stream %u " DEBUGF(infof(data_s, "%zu data received for stream %u "
"(%zu left in buffer %p, total %zu)\n", "(%zu left in buffer %p, total %zu)\n",
@ -909,7 +909,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
Curl_add_buffer(stream->header_recvbuf, " \r\n", 3); Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
/* if we receive data for another handle, wake that up */ /* if we receive data for another handle, wake that up */
if(conn->data != data_s) if(conn->data != data_s)
Curl_expire(data_s, 0); Curl_expire(data_s, 0, EXPIRE_H2DATA);
DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n", DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
stream->status_code, data_s)); stream->status_code, data_s));
@ -925,7 +925,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
/* if we receive data for another handle, wake that up */ /* if we receive data for another handle, wake that up */
if(conn->data != data_s) if(conn->data != data_s)
Curl_expire(data_s, 0); Curl_expire(data_s, 0, EXPIRE_H2DATA);
DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen, DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
value)); value));

View File

@ -430,7 +430,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
sockets that time-out or have actions will be dealt with. Since this 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 handle has no action yet, we make sure it times out to get things to
happen. */ happen. */
Curl_expire(data, 0); Curl_expire(data, 0, EXPIRE_ADD_HANDLE);
/* increase the node-counter */ /* increase the node-counter */
multi->num_easy++; multi->num_easy++;
@ -1844,9 +1844,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(send_timeout_ms <= 0 && recv_timeout_ms <= 0) if(send_timeout_ms <= 0 && recv_timeout_ms <= 0)
multistate(data, CURLM_STATE_PERFORM); multistate(data, CURLM_STATE_PERFORM);
else if(send_timeout_ms >= recv_timeout_ms) else if(send_timeout_ms >= recv_timeout_ms)
Curl_expire_latest(data, send_timeout_ms); Curl_expire_latest(data, send_timeout_ms, EXPIRE_TOOFAST);
else else
Curl_expire_latest(data, recv_timeout_ms); Curl_expire_latest(data, recv_timeout_ms, EXPIRE_TOOFAST);
} }
break; break;
@ -1877,9 +1877,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(send_timeout_ms > 0 || recv_timeout_ms > 0) { if(send_timeout_ms > 0 || recv_timeout_ms > 0) {
multistate(data, CURLM_STATE_TOOFAST); multistate(data, CURLM_STATE_TOOFAST);
if(send_timeout_ms >= recv_timeout_ms) if(send_timeout_ms >= recv_timeout_ms)
Curl_expire_latest(data, send_timeout_ms); Curl_expire_latest(data, send_timeout_ms, EXPIRE_TOOFAST);
else else
Curl_expire_latest(data, recv_timeout_ms); Curl_expire_latest(data, recv_timeout_ms, EXPIRE_TOOFAST);
break; break;
} }
@ -1940,7 +1940,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* expire the new receiving pipeline head */ /* expire the new receiving pipeline head */
if(data->easy_conn->recv_pipe.head) if(data->easy_conn->recv_pipe.head)
Curl_expire_latest(data->easy_conn->recv_pipe.head->ptr, 0); Curl_expire_latest(data->easy_conn->recv_pipe.head->ptr, 0,
EXPIRE_PIPELINE_READ);
/* Check if we can move pending requests to send pipe */ /* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi); Curl_multi_process_pending_handles(multi);
@ -2484,6 +2485,7 @@ void Curl_multi_closed(struct connectdata *conn, curl_socket_t s)
struct time_node { struct time_node {
struct curl_llist_element list; struct curl_llist_element list;
struct timeval time; struct timeval time;
expire_id id;
}; };
/* /*
@ -2871,6 +2873,27 @@ static void multi_freetimeout(void *user, void *entryptr)
free(entryptr); free(entryptr);
} }
/*
* multi_deltimeout()
*
* Remove a given timestamp from the list of timeouts.
*/
static void
multi_deltimeout(struct Curl_easy *data, expire_id id)
{
struct curl_llist_element *e;
struct curl_llist *timeoutlist = &data->state.timeoutlist;
/* find and remove the node(s) from the list */
for(e = timeoutlist->head; e; e = e->next) {
struct time_node *node = (struct time_node *)e->ptr;
if(node->id == id) {
Curl_llist_remove(timeoutlist, e, NULL);
return;
}
}
}
/* /*
* multi_addtimeout() * multi_addtimeout()
* *
@ -2879,21 +2902,27 @@ static void multi_freetimeout(void *user, void *entryptr)
* *
*/ */
static CURLMcode static CURLMcode
multi_addtimeout(struct curl_llist *timeoutlist, multi_addtimeout(struct Curl_easy *data,
struct timeval *stamp) struct timeval *stamp,
int id)
{ {
struct curl_llist_element *e; struct curl_llist_element *e;
struct time_node *node; struct time_node *node;
struct curl_llist_element *prev = NULL; struct curl_llist_element *prev = NULL;
size_t n;
struct curl_llist *timeoutlist = &data->state.timeoutlist;
node = malloc(sizeof(struct time_node)); node = malloc(sizeof(struct time_node));
if(!node) if(!node)
return CURLM_OUT_OF_MEMORY; return CURLM_OUT_OF_MEMORY;
/* copy the timestamp */ /* copy the timestamp and id */
memcpy(&node->time, stamp, sizeof(*stamp)); memcpy(&node->time, stamp, sizeof(*stamp));
node->id = id;
if(Curl_llist_count(timeoutlist)) { n = Curl_llist_count(timeoutlist);
infof(data, "TIMEOUTS %zd\n", n);
if(n) {
/* find the correct spot in the list */ /* find the correct spot in the list */
for(e = timeoutlist->head; e; e = e->next) { for(e = timeoutlist->head; e; e = e->next) {
struct time_node *check = (struct time_node *)e->ptr; struct time_node *check = (struct time_node *)e->ptr;
@ -2919,8 +2948,12 @@ multi_addtimeout(struct curl_llist *timeoutlist,
* *
* The timeout will be added to a queue of timeouts if it defines a moment in * 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. * time that is later than the current head of queue.
*
* If 'id' is given (non-zero), expire will replace a former timeout using the
* same id. id is also a good way to keep track of the purpose of each
* timeout.
*/ */
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 Curl_multi *multi = data->multi;
struct timeval *nowp = &data->state.expiretime; struct timeval *nowp = &data->state.expiretime;
@ -2932,6 +2965,10 @@ void Curl_expire(struct Curl_easy *data, time_t milli)
if(!multi) if(!multi)
return; return;
DEBUGASSERT(id < EXPIRE_LAST);
infof(data, "EXPIRE in %d, id %d\n", (int)milli, id);
set = Curl_tvnow(); set = Curl_tvnow();
set.tv_sec += (long)(milli/1000); set.tv_sec += (long)(milli/1000);
set.tv_usec += (long)(milli%1000)*1000; set.tv_usec += (long)(milli%1000)*1000;
@ -2946,16 +2983,20 @@ void Curl_expire(struct Curl_easy *data, time_t milli)
Compare if the new time is earlier, and only remove-old/add-new if it Compare if the new time is earlier, and only remove-old/add-new if it
is. */ is. */
time_t diff = curlx_tvdiff(set, *nowp); time_t diff = curlx_tvdiff(set, *nowp);
/* remove the previous timer first, if there */
multi_deltimeout(data, id);
if(diff > 0) { if(diff > 0) {
/* the new expire time was later so just add it to the queue /* the new expire time was later so just add it to the queue
and get out */ and get out */
multi_addtimeout(&data->state.timeoutlist, &set); multi_addtimeout(data, &set, id);
return; return;
} }
/* the new time is newer than the presently set one, so add the current /* the new time is newer than the presently set one, so add the current
to the queue and update the head */ to the queue and update the head */
multi_addtimeout(&data->state.timeoutlist, nowp); multi_addtimeout(data, nowp, id);
/* Since this is an updated time, we must remove the previous entry from /* Since this is an updated time, we must remove the previous entry from
the splay tree first and then re-add the new value */ the splay tree first and then re-add the new value */
@ -2983,7 +3024,7 @@ void Curl_expire(struct Curl_easy *data, time_t milli)
* time-out period to expire. * time-out period to expire.
* *
*/ */
void Curl_expire_latest(struct Curl_easy *data, time_t milli) void Curl_expire_latest(struct Curl_easy *data, time_t milli, expire_id id)
{ {
struct timeval *expire = &data->state.expiretime; struct timeval *expire = &data->state.expiretime;
@ -3012,7 +3053,7 @@ void Curl_expire_latest(struct Curl_easy *data, time_t milli)
} }
/* Just add the timeout like normal */ /* Just add the timeout like normal */
Curl_expire(data, milli); Curl_expire(data, milli, id);
} }
@ -3119,7 +3160,7 @@ void Curl_multi_process_pending_handles(struct Curl_multi *multi)
Curl_llist_remove(&multi->pending, e, NULL); Curl_llist_remove(&multi->pending, e, NULL);
/* Make sure that the handle will be processed soonish. */ /* Make sure that the handle will be processed soonish. */
Curl_expire_latest(data, 0); Curl_expire_latest(data, 0, EXPIRE_MULTI_PENDING);
} }
e = next; /* operate on next handle */ e = next; /* operate on next handle */

View File

@ -7,7 +7,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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -25,9 +25,29 @@
/* /*
* Prototypes for library-wide functions provided by multi.c * Prototypes for library-wide functions provided by multi.c
*/ */
void Curl_expire(struct Curl_easy *data, time_t milli);
/* Timers */
typedef enum {
EXPIRE_SPEEDCHECK,
EXPIRE_H2DATA,
EXPIRE_PIPELINE_SEND,
EXPIRE_PIPELINE_READ,
EXPIRE_ADD_HANDLE,
EXPIRE_TOOFAST,
EXPIRE_UNPAUSE,
EXPIRE_ARES,
EXPIRE_MULTI_PENDING,
EXPIRE_DNS_PER_NAME,
EXPIRE_HAPPY_EYEBALLS,
EXPIRE_100_TIMEOUT,
EXPIRE_TIMEOUT,
EXPIRE_CONNECTTIMEOUT,
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;
void Curl_expire(struct Curl_easy *data, time_t milli, expire_id);
void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_clear(struct Curl_easy *data);
void Curl_expire_latest(struct Curl_easy *data, time_t milli); void Curl_expire_latest(struct Curl_easy *data, time_t milli, expire_id);
bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits); bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits);
void Curl_multi_handlePipeBreak(struct Curl_easy *data); void Curl_multi_handlePipeBreak(struct Curl_easy *data);

View File

@ -113,7 +113,7 @@ CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
if(pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head) { if(pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head) {
/* this is a new one as head, expire it */ /* this is a new one as head, expire it */
Curl_pipeline_leave_write(conn); /* not in use yet */ Curl_pipeline_leave_write(conn); /* not in use yet */
Curl_expire(conn->send_pipe.head->ptr, 0); Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_PIPELINE_SEND);
} }
#if 0 /* enable for pipeline debugging */ #if 0 /* enable for pipeline debugging */
@ -148,7 +148,7 @@ void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
infof(conn->data, "%p is at send pipe head B!\n", infof(conn->data, "%p is at send pipe head B!\n",
(void *)conn->send_pipe.head->ptr); (void *)conn->send_pipe.head->ptr);
#endif #endif
Curl_expire(conn->send_pipe.head->ptr, 0); Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_PIPELINE_READ);
} }
/* The receiver's list is not really interesting here since either this /* The receiver's list is not really interesting here since either this

View File

@ -67,7 +67,7 @@ CURLcode Curl_speedcheck(struct Curl_easy *data,
if(data->set.low_speed_limit) if(data->set.low_speed_limit)
/* if low speed limit is enabled, set the expire timer to make this /* if low speed limit is enabled, set the expire timer to make this
connection's speed get checked again in a second */ connection's speed get checked again in a second */
Curl_expire_latest(data, 1000); Curl_expire_latest(data, 1000, EXPIRE_SPEEDCHECK);
return CURLE_OK; return CURLE_OK;
} }

View File

@ -889,7 +889,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
*didwhat &= ~KEEP_SEND; /* we didn't write anything actually */ *didwhat &= ~KEEP_SEND; /* we didn't write anything actually */
/* set a timeout for the multi interface */ /* set a timeout for the multi interface */
Curl_expire(data, data->set.expect_100_timeout); Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
break; break;
} }
@ -1338,10 +1338,10 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
Curl_pgrsStartNow(data); Curl_pgrsStartNow(data);
if(data->set.timeout) if(data->set.timeout)
Curl_expire(data, data->set.timeout); Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
if(data->set.connecttimeout) if(data->set.connecttimeout)
Curl_expire(data, data->set.connecttimeout); Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
/* In case the handle is re-used and an authentication method was picked /* In case the handle is re-used and an authentication method was picked
in the session we need to make sure we only use the one(s) we now in the session we need to make sure we only use the one(s) we now
@ -1942,7 +1942,7 @@ Curl_setup_transfer(
/* Set a timeout for the multi interface. Add the inaccuracy margin so /* Set a timeout for the multi interface. Add the inaccuracy margin so
that we don't fire slightly too early and get denied to run. */ that we don't fire slightly too early and get denied to run. */
Curl_expire(data, data->set.expect_100_timeout); Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
} }
else { else {
if(data->state.expect100header) if(data->state.expect100header)