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;
}
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * 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
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;
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;
}
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);
}
}
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:
* 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");
/* 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:
/* 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",
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));
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));
--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
};
#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);
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
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++;
/* 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);
}
}
-struct time_node {
- struct curl_llist_element list;
- struct timeval time;
- expire_id id;
-};
-
/*
* add_next_timeout()
*
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;
}
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;
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);
* 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)
{
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_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 */
* 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);
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 */
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
/* 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);
}
/* 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");
}
}
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 */
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;