multi: use a fixed array of timers instead of malloc

... since the total amount is low this is faster, easier and reduces
memory overhead.

Also, Curl_expire_done() can now mark an expire timeout as done so that
it never times out.

Closes #1472
This commit is contained in:
Daniel Stenberg 2017-05-09 12:47:49 +02:00
parent e9fd794a61
commit 31b39c40cf
12 changed files with 66 additions and 70 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, EXPIRE_ARES); Curl_expire_latest(conn->data, milli, EXPIRE_ASYNC_NAME);
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
@ -540,7 +540,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
td->poll_interval = 250; td->poll_interval = 250;
td->interval_end = elapsed + td->poll_interval; td->interval_end = elapsed + td->poll_interval;
Curl_expire(conn->data, td->poll_interval); Curl_expire(conn->data, td->poll_interval, EXPIRE_ASYNC_NAME);
} }
return CURLE_OK; return CURLE_OK;

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, EXPIRE_UNPAUSE); /* get this handle going again */ Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */
return result; return result;
} }

View File

@ -2746,6 +2746,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
data->req.upload_done = TRUE; data->req.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we're done writing */ data->req.keepon &= ~KEEP_SEND; /* we're done writing */
data->req.exp100 = EXP100_SEND_DATA; /* already sent */ data->req.exp100 = EXP100_SEND_DATA; /* already sent */
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
} }
} }
@ -3042,6 +3043,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(k->exp100 > EXP100_SEND_DATA) { if(k->exp100 > EXP100_SEND_DATA) {
k->exp100 = EXP100_SEND_DATA; k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_SEND; k->keepon |= KEEP_SEND;
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
} }
break; break;
case 101: case 101:
@ -3168,6 +3170,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
* request body has been sent we stop sending and mark the * request body has been sent we stop sending and mark the
* connection for closure after we've read the entire response. * connection for closure after we've read the entire response.
*/ */
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
if(!k->upload_done) { if(!k->upload_done) {
if(data->set.http_keep_sending_on_error) { if(data->set.http_keep_sending_on_error) {
infof(data, "HTTP error before end of send, keep sending\n"); infof(data, "HTTP error before end of send, keep sending\n");

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, EXPIRE_H2DATA); Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
} }
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, EXPIRE_H2DATA); Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
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, EXPIRE_H2DATA); Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
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, EXPIRE_H2DATA); Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
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

@ -122,7 +122,8 @@ Curl_llist_remove(struct curl_llist *list, struct curl_llist_element *e,
--list->size; --list->size;
/* call the dtor() last for when it actually frees the 'e' memory itself */ /* call the dtor() last for when it actually frees the 'e' memory itself */
list->dtor(user, ptr); if(list->dtor)
list->dtor(user, ptr);
} }
void void

View File

@ -99,8 +99,6 @@ static const char * const statename[]={
}; };
#endif #endif
static void multi_freetimeout(void *a, void *b);
/* function pointer called once when switching TO a state */ /* function pointer called once when switching TO a state */
typedef void (*init_multistate_func)(struct Curl_easy *data); typedef void (*init_multistate_func)(struct Curl_easy *data);
@ -369,7 +367,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
return CURLM_ADDED_ALREADY; return CURLM_ADDED_ALREADY;
/* Initialize timeout list for this handle */ /* Initialize timeout list for this handle */
Curl_llist_init(&data->state.timeoutlist, multi_freetimeout); Curl_llist_init(&data->state.timeoutlist, NULL);
/* /*
* No failure allowed in this function beyond this point. And no * No failure allowed in this function beyond this point. And no
@ -430,7 +428,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, EXPIRE_ADD_HANDLE); Curl_expire(data, 0, EXPIRE_RUN_NOW);
/* increase the node-counter */ /* increase the node-counter */
multi->num_easy++; multi->num_easy++;
@ -1941,7 +1939,7 @@ 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); EXPIRE_RUN_NOW);
/* 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);
@ -2482,12 +2480,6 @@ void Curl_multi_closed(struct connectdata *conn, curl_socket_t s)
} }
} }
struct time_node {
struct curl_llist_element list;
struct timeval time;
expire_id id;
};
/* /*
* add_next_timeout() * add_next_timeout()
* *
@ -2859,35 +2851,20 @@ static int update_timer(struct Curl_multi *multi)
return multi->timer_cb(multi, timeout_ms, multi->timer_userp); return multi->timer_cb(multi, timeout_ms, multi->timer_userp);
} }
/*
* multi_freetimeout()
*
* Callback used by the llist system when a single timeout list entry is
* destroyed.
*/
static void multi_freetimeout(void *user, void *entryptr)
{
(void)user;
/* the entry was plain malloc()'ed */
free(entryptr);
}
/* /*
* multi_deltimeout() * multi_deltimeout()
* *
* Remove a given timestamp from the list of timeouts. * Remove a given timestamp from the list of timeouts.
*/ */
static void static void
multi_deltimeout(struct Curl_easy *data, expire_id id) multi_deltimeout(struct Curl_easy *data, expire_id eid)
{ {
struct curl_llist_element *e; struct curl_llist_element *e;
struct curl_llist *timeoutlist = &data->state.timeoutlist; struct curl_llist *timeoutlist = &data->state.timeoutlist;
/* find and remove the specific node from the list */
/* find and remove the node(s) from the list */
for(e = timeoutlist->head; e; e = e->next) { for(e = timeoutlist->head; e; e = e->next) {
struct time_node *node = (struct time_node *)e->ptr; struct time_node *n = (struct time_node *)e->ptr;
if(node->id == id) { if(n->eid == eid) {
Curl_llist_remove(timeoutlist, e, NULL); Curl_llist_remove(timeoutlist, e, NULL);
return; return;
} }
@ -2904,7 +2881,7 @@ multi_deltimeout(struct Curl_easy *data, expire_id id)
static CURLMcode static CURLMcode
multi_addtimeout(struct Curl_easy *data, multi_addtimeout(struct Curl_easy *data,
struct timeval *stamp, struct timeval *stamp,
int id) expire_id eid)
{ {
struct curl_llist_element *e; struct curl_llist_element *e;
struct time_node *node; struct time_node *node;
@ -2912,13 +2889,11 @@ multi_addtimeout(struct Curl_easy *data,
size_t n; size_t n;
struct curl_llist *timeoutlist = &data->state.timeoutlist; struct curl_llist *timeoutlist = &data->state.timeoutlist;
node = malloc(sizeof(struct time_node)); node = &data->state.expires[eid];
if(!node)
return CURLM_OUT_OF_MEMORY;
/* copy the timestamp and id */ /* copy the timestamp and id */
memcpy(&node->time, stamp, sizeof(*stamp)); memcpy(&node->time, stamp, sizeof(*stamp));
node->id = id; node->eid = eid; /* also marks it as in use */
n = Curl_llist_count(timeoutlist); n = Curl_llist_count(timeoutlist);
infof(data, "TIMEOUTS %zd\n", n); infof(data, "TIMEOUTS %zd\n", n);
@ -2949,9 +2924,7 @@ multi_addtimeout(struct Curl_easy *data,
* 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 * Expire replaces a former timeout using the same id if already set.
* 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, expire_id id) void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id)
{ {
@ -3056,6 +3029,17 @@ void Curl_expire_latest(struct Curl_easy *data, time_t milli, expire_id id)
Curl_expire(data, milli, id); Curl_expire(data, milli, id);
} }
/*
* Curl_expire_done()
*
* Removes the expire timer. Marks it as done.
*
*/
void Curl_expire_done(struct Curl_easy *data, expire_id id)
{
/* remove the timer, if there */
multi_deltimeout(data, id);
}
/* /*
* Curl_expire_clear() * Curl_expire_clear()
@ -3160,7 +3144,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, EXPIRE_MULTI_PENDING); Curl_expire_latest(data, 0, EXPIRE_RUN_NOW);
} }
e = next; /* operate on next handle */ e = next; /* operate on next handle */

View File

@ -26,28 +26,10 @@
* Prototypes for library-wide functions provided by multi.c * Prototypes for library-wide functions provided by multi.c
*/ */
/* 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(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, expire_id); void Curl_expire_latest(struct Curl_easy *data, time_t milli, expire_id);
void Curl_expire_done(struct Curl_easy *data, expire_id 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, EXPIRE_PIPELINE_SEND); Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
} }
#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, EXPIRE_PIPELINE_READ); Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
} }
/* 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

@ -1891,7 +1891,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
/* since we don't really wait for anything at this point, we want the /* since we don't really wait for anything at this point, we want the
state machine to move on as soon as possible so we set a very short state machine to move on as soon as possible so we set a very short
timeout here */ timeout here */
Curl_expire(data, 0); Curl_expire(data, 0, EXPIRE_RUN_NOW);
state(conn, SSH_STOP); state(conn, SSH_STOP);
} }

View File

@ -1139,6 +1139,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
/* we've waited long enough, continue anyway */ /* we've waited long enough, continue anyway */
k->exp100 = EXP100_SEND_DATA; k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_SEND; k->keepon |= KEEP_SEND;
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
infof(data, "Done waiting for 100-continue\n"); infof(data, "Done waiting for 100-continue\n");
} }
} }

View File

@ -1314,6 +1314,30 @@ struct tempbuf {
Curl_client_write() */ Curl_client_write() */
}; };
/* Timers */
typedef enum {
EXPIRE_100_TIMEOUT,
EXPIRE_ASYNC_NAME,
EXPIRE_CONNECTTIMEOUT,
EXPIRE_DNS_PER_NAME,
EXPIRE_HAPPY_EYEBALLS,
EXPIRE_MULTI_PENDING,
EXPIRE_RUN_NOW,
EXPIRE_SPEEDCHECK,
EXPIRE_TIMEOUT,
EXPIRE_TOOFAST,
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;
/*
* One instance for each timeout an easy handle can set.
*/
struct time_node {
struct curl_llist_element list;
struct timeval time;
expire_id eid;
};
struct UrlState { struct UrlState {
/* Points to the connection cache */ /* Points to the connection cache */
@ -1382,6 +1406,7 @@ struct UrlState {
struct timeval expiretime; /* set this with Curl_expire() only */ struct timeval expiretime; /* set this with Curl_expire() only */
struct Curl_tree timenode; /* for the splay stuff */ struct Curl_tree timenode; /* for the splay stuff */
struct curl_llist timeoutlist; /* list of pending timeouts */ struct curl_llist timeoutlist; /* list of pending timeouts */
struct time_node expires[EXPIRE_LAST]; /* nodes for each expire type */
/* a place to store the most recently set FTP entrypath */ /* a place to store the most recently set FTP entrypath */
char *most_recent_ftp_entrypath; char *most_recent_ftp_entrypath;