milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
if(milli == 0)
milli += 10;
- Curl_expire_latest(conn->data, milli);
+ Curl_expire_latest(conn->data, milli, EXPIRE_ARES);
return max;
}
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * 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
conn->connecttime = Curl_tvnow();
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 */
if(!isconnected && (conn->socktype == SOCK_STREAM)) {
conn->tempaddr[1] = NULL;
conn->tempsock[0] = 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 */
conn->timeoutms_per_addr =
if(!result &&
((newstate&(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;
}
else {
/* Add timeout to multi handle and break out of the loop */
if(!result && *connected == FALSE) {
- if(data->set.accepttimeout > 0)
- Curl_expire(data, data->set.accepttimeout);
- else
- Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
+ Curl_expire(data, data->set.accepttimeout > 0 ?
+ data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, 0);
}
}
/* if we receive data for another handle, wake that up */
if(conn_s->data != data_s)
- Curl_expire(data_s, 0);
+ Curl_expire(data_s, 0, EXPIRE_H2DATA);
}
break;
case NGHTTP2_PUSH_PROMISE:
/* if we receive data for another handle, wake that up */
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 "
"(%zu left in buffer %p, total %zu)\n",
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);
+ Curl_expire(data_s, 0, EXPIRE_H2DATA);
DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
stream->status_code, data_s));
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);
+ Curl_expire(data_s, 0, EXPIRE_H2DATA);
DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
value));
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_ADD_HANDLE);
/* increase the node-counter */
multi->num_easy++;
if(send_timeout_ms <= 0 && recv_timeout_ms <= 0)
multistate(data, CURLM_STATE_PERFORM);
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
- Curl_expire_latest(data, recv_timeout_ms);
+ Curl_expire_latest(data, recv_timeout_ms, EXPIRE_TOOFAST);
}
break;
if(send_timeout_ms > 0 || recv_timeout_ms > 0) {
multistate(data, CURLM_STATE_TOOFAST);
if(send_timeout_ms >= recv_timeout_ms)
- Curl_expire_latest(data, send_timeout_ms);
+ Curl_expire_latest(data, send_timeout_ms, EXPIRE_TOOFAST);
else
- Curl_expire_latest(data, recv_timeout_ms);
+ Curl_expire_latest(data, recv_timeout_ms, EXPIRE_TOOFAST);
break;
}
/* expire the new receiving pipeline 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 */
Curl_multi_process_pending_handles(multi);
struct time_node {
struct curl_llist_element list;
struct timeval time;
+ expire_id id;
};
/*
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()
*
*
*/
static CURLMcode
-multi_addtimeout(struct curl_llist *timeoutlist,
- struct timeval *stamp)
+multi_addtimeout(struct Curl_easy *data,
+ struct timeval *stamp,
+ int id)
{
struct curl_llist_element *e;
struct time_node *node;
struct curl_llist_element *prev = NULL;
+ size_t n;
+ struct curl_llist *timeoutlist = &data->state.timeoutlist;
node = malloc(sizeof(struct time_node));
if(!node)
return CURLM_OUT_OF_MEMORY;
- /* copy the timestamp */
+ /* copy the timestamp and id */
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 */
for(e = timeoutlist->head; e; e = e->next) {
struct time_node *check = (struct time_node *)e->ptr;
*
* 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.
*/
-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;
if(!multi)
return;
+ DEBUGASSERT(id < EXPIRE_LAST);
+
+ infof(data, "EXPIRE in %d, id %d\n", (int)milli, id);
+
set = Curl_tvnow();
set.tv_sec += (long)(milli/1000);
set.tv_usec += (long)(milli%1000)*1000;
Compare if the new time is earlier, and only remove-old/add-new if it
is. */
time_t diff = curlx_tvdiff(set, *nowp);
+
+ /* remove the previous timer first, if there */
+ multi_deltimeout(data, id);
+
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);
+ multi_addtimeout(data, &set, id);
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);
+ multi_addtimeout(data, nowp, id);
/* Since this is an updated time, we must remove the previous entry from
the splay tree first and then re-add the new value */
* 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;
}
/* Just add the timeout like normal */
- Curl_expire(data, milli);
+ Curl_expire(data, milli, id);
}
Curl_llist_remove(&multi->pending, e, NULL);
/* 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 */
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * 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
/*
* 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_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);
void Curl_multi_handlePipeBreak(struct Curl_easy *data);
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);
+ Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_PIPELINE_SEND);
}
#if 0 /* enable for pipeline debugging */
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);
+ Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_PIPELINE_READ);
}
/* The receiver's list is not really interesting here since either this
if(data->set.low_speed_limit)
/* if low speed limit is enabled, set the expire timer to make this
connection's speed get checked again in a second */
- Curl_expire_latest(data, 1000);
+ Curl_expire_latest(data, 1000, EXPIRE_SPEEDCHECK);
return CURLE_OK;
}
*didwhat &= ~KEEP_SEND; /* we didn't write anything actually */
/* 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;
}
Curl_pgrsStartNow(data);
if(data->set.timeout)
- Curl_expire(data, data->set.timeout);
+ Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
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 the session we need to make sure we only use the one(s) we now
/* 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. */
- Curl_expire(data, data->set.expect_100_timeout);
+ Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
}
else {
if(data->state.expect100header)