From 31b39c40cf909d34f27dc655755f346482f57089 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 May 2017 12:47:49 +0200 Subject: 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 --- lib/asyn-ares.c | 2 +- lib/asyn-thread.c | 4 ++-- lib/easy.c | 2 +- lib/http.c | 3 +++ lib/http2.c | 8 +++---- lib/llist.c | 3 ++- lib/multi.c | 62 +++++++++++++++++++++---------------------------------- lib/multiif.h | 20 +----------------- lib/pipeline.c | 4 ++-- lib/ssh.c | 2 +- lib/transfer.c | 1 + lib/urldata.h | 25 ++++++++++++++++++++++ 12 files changed, 66 insertions(+), 70 deletions(-) diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index a8396ea52..12f35e412 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -232,7 +232,7 @@ int Curl_resolver_getsock(struct connectdata *conn, milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000); if(milli == 0) milli += 10; - Curl_expire_latest(conn->data, milli, EXPIRE_ARES); + Curl_expire_latest(conn->data, milli, EXPIRE_ASYNC_NAME); return max; } diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c index 2138c78b8..65fa6c5be 100644 --- a/lib/asyn-thread.c +++ b/lib/asyn-thread.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * 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->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; diff --git a/lib/easy.c b/lib/easy.c index d4add46cc..46c0a9911 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -1044,7 +1044,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) if(!result && ((newstate&(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; } diff --git a/lib/http.c b/lib/http.c index 93aac201c..8e7fb0fa9 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2746,6 +2746,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) data->req.upload_done = TRUE; data->req.keepon &= ~KEEP_SEND; /* we're done writing */ 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) { k->exp100 = EXP100_SEND_DATA; k->keepon |= KEEP_SEND; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); } break; 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 * connection for closure after we've read the entire response. */ + Curl_expire_done(data, EXPIRE_100_TIMEOUT); if(!k->upload_done) { if(data->set.http_keep_sending_on_error) { infof(data, "HTTP error before end of send, keep sending\n"); diff --git a/lib/http2.c b/lib/http2.c index 994e53deb..c65305029 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -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(conn_s->data != data_s) - Curl_expire(data_s, 0, EXPIRE_H2DATA); + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); } break; 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(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 " "(%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); /* if we receive data for another handle, wake that up */ 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", 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); /* if we receive data for another handle, wake that up */ 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, value)); diff --git a/lib/llist.c b/lib/llist.c index 06cebb971..4bb0a51b8 100644 --- a/lib/llist.c +++ b/lib/llist.c @@ -122,7 +122,8 @@ Curl_llist_remove(struct curl_llist *list, struct curl_llist_element *e, --list->size; /* 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 diff --git a/lib/multi.c b/lib/multi.c index cfcc8d6c5..252cd2ae4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -99,8 +99,6 @@ static const char * const statename[]={ }; #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); @@ -369,7 +367,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, return CURLM_ADDED_ALREADY; /* 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 @@ -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 handle has no action yet, we make sure it times out to get things to happen. */ - Curl_expire(data, 0, EXPIRE_ADD_HANDLE); + Curl_expire(data, 0, EXPIRE_RUN_NOW); /* increase the node-counter */ multi->num_easy++; @@ -1941,7 +1939,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* expire the new receiving pipeline head */ if(data->easy_conn->recv_pipe.head) 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 */ 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() * @@ -2859,35 +2851,20 @@ static int update_timer(struct Curl_multi *multi) 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() * * Remove a given timestamp from the list of timeouts. */ 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 *timeoutlist = &data->state.timeoutlist; - - /* find and remove the node(s) from the list */ + /* find and remove the specific node from the list */ for(e = timeoutlist->head; e; e = e->next) { - struct time_node *node = (struct time_node *)e->ptr; - if(node->id == id) { + struct time_node *n = (struct time_node *)e->ptr; + if(n->eid == eid) { Curl_llist_remove(timeoutlist, e, NULL); return; } @@ -2904,7 +2881,7 @@ multi_deltimeout(struct Curl_easy *data, expire_id id) static CURLMcode multi_addtimeout(struct Curl_easy *data, struct timeval *stamp, - int id) + expire_id eid) { struct curl_llist_element *e; struct time_node *node; @@ -2912,13 +2889,11 @@ multi_addtimeout(struct Curl_easy *data, size_t n; struct curl_llist *timeoutlist = &data->state.timeoutlist; - node = malloc(sizeof(struct time_node)); - if(!node) - return CURLM_OUT_OF_MEMORY; + node = &data->state.expires[eid]; /* copy the timestamp and id */ memcpy(&node->time, stamp, sizeof(*stamp)); - node->id = id; + node->eid = eid; /* also marks it as in use */ n = Curl_llist_count(timeoutlist); 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 * 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. + * Expire replaces a former timeout using the same id if already set. */ 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_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() @@ -3160,7 +3144,7 @@ void Curl_multi_process_pending_handles(struct Curl_multi *multi) Curl_llist_remove(&multi->pending, e, NULL); /* 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 */ diff --git a/lib/multiif.h b/lib/multiif.h index a9a7a5577..a833e23e0 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -26,28 +26,10 @@ * 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_clear(struct Curl_easy *data); 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); void Curl_multi_handlePipeBreak(struct Curl_easy *data); diff --git a/lib/pipeline.c b/lib/pipeline.c index 729e69c27..b8d203745 100644 --- a/lib/pipeline.c +++ b/lib/pipeline.c @@ -113,7 +113,7 @@ CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle, if(pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head) { /* this is a new one as head, expire it */ 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 */ @@ -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", (void *)conn->send_pipe.head->ptr); #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 diff --git a/lib/ssh.c b/lib/ssh.c index 50f1dcb4e..863d7fd54 100644 --- a/lib/ssh.c +++ b/lib/ssh.c @@ -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 state machine to move on as soon as possible so we set a very short timeout here */ - Curl_expire(data, 0); + Curl_expire(data, 0, EXPIRE_RUN_NOW); state(conn, SSH_STOP); } diff --git a/lib/transfer.c b/lib/transfer.c index b7435f9ae..73497f79f 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1139,6 +1139,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* we've waited long enough, continue anyway */ k->exp100 = EXP100_SEND_DATA; k->keepon |= KEEP_SEND; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); infof(data, "Done waiting for 100-continue\n"); } } diff --git a/lib/urldata.h b/lib/urldata.h index 84124b658..d4a4a98dc 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1314,6 +1314,30 @@ struct tempbuf { 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 { /* Points to the connection cache */ @@ -1382,6 +1406,7 @@ struct UrlState { struct timeval expiretime; /* set this with Curl_expire() only */ struct Curl_tree timenode; /* for the splay stuff */ 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 */ char *most_recent_ftp_entrypath; -- cgit v1.2.3