diff options
Diffstat (limited to 'libs/libcurl/src/conncache.c')
-rw-r--r-- | libs/libcurl/src/conncache.c | 1026 |
1 files changed, 272 insertions, 754 deletions
diff --git a/libs/libcurl/src/conncache.c b/libs/libcurl/src/conncache.c index 17431a181c..1de4a86b7d 100644 --- a/libs/libcurl/src/conncache.c +++ b/libs/libcurl/src/conncache.c @@ -32,7 +32,9 @@ #include "cfilters.h"
#include "progress.h"
#include "multiif.h"
+#include "multi_ev.h"
#include "sendf.h"
+#include "cshutdn.h"
#include "conncache.h"
#include "http_negotiate.h"
#include "http_ntlm.h"
@@ -41,6 +43,8 @@ #include "connect.h"
#include "select.h"
#include "strcase.h"
+#include "curlx/strparse.h"
+#include "uint-table.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -50,24 +54,24 @@ #define CPOOL_IS_LOCKED(c) ((c) && (c)->locked)
-#define CPOOL_LOCK(c) \
+#define CPOOL_LOCK(c,d) \
do { \
if((c)) { \
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
- Curl_share_lock(((c)->idata), CURL_LOCK_DATA_CONNECT, \
+ Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \
CURL_LOCK_ACCESS_SINGLE); \
DEBUGASSERT(!(c)->locked); \
(c)->locked = TRUE; \
} \
} while(0)
-#define CPOOL_UNLOCK(c) \
+#define CPOOL_UNLOCK(c,d) \
do { \
if((c)) { \
DEBUGASSERT((c)->locked); \
(c)->locked = FALSE; \
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
- Curl_share_unlock((c)->idata, CURL_LOCK_DATA_CONNECT); \
+ Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT); \
} \
} while(0)
@@ -84,33 +88,18 @@ static void cpool_discard_conn(struct cpool *cpool, struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
-static void cpool_close_and_destroy(struct cpool *cpool,
- struct connectdata *conn,
- struct Curl_easy *data,
- bool do_shutdown);
-static void cpool_run_conn_shutdown(struct Curl_easy *data,
- struct connectdata *conn,
- bool *done);
-static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
- struct connectdata *conn);
-static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
- struct Curl_easy *data,
- struct connectdata *conn);
-static void cpool_shutdown_all(struct cpool *cpool,
- struct Curl_easy *data, int timeout_ms);
-static void cpool_close_and_destroy_all(struct cpool *cpool);
-static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool);
-
-static struct cpool_bundle *cpool_bundle_create(const char *dest,
- size_t dest_len)
+
+static struct cpool_bundle *cpool_bundle_create(const char *dest)
{
struct cpool_bundle *bundle;
+ size_t dest_len = strlen(dest);
+
bundle = calloc(1, sizeof(*bundle) + dest_len);
if(!bundle)
return NULL;
Curl_llist_init(&bundle->conns, NULL);
- bundle->dest_len = dest_len;
- memcpy(bundle->dest, dest, dest_len);
+ bundle->dest_len = dest_len + 1;
+ memcpy(bundle->dest, dest, bundle->dest_len);
return bundle;
}
@@ -144,57 +133,102 @@ static void cpool_bundle_free_entry(void *freethis) cpool_bundle_destroy((struct cpool_bundle *)freethis);
}
-int Curl_cpool_init(struct cpool *cpool,
- Curl_cpool_disconnect_cb *disconnect_cb,
- struct Curl_multi *multi,
- struct Curl_share *share,
- size_t size)
+void Curl_cpool_init(struct cpool *cpool,
+ struct Curl_easy *idata,
+ struct Curl_share *share,
+ size_t size)
{
- DEBUGASSERT(!!multi != !!share); /* either one */
Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
- Curl_str_key_compare, cpool_bundle_free_entry);
- Curl_llist_init(&cpool->shutdowns, NULL);
+ curlx_str_key_compare, cpool_bundle_free_entry);
- DEBUGASSERT(disconnect_cb);
- if(!disconnect_cb)
- return 1;
+ DEBUGASSERT(idata);
- /* allocate a new easy handle to use when closing cached connections */
- cpool->idata = curl_easy_init();
- if(!cpool->idata)
- return 1; /* bad */
- cpool->idata->state.internal = TRUE;
- /* TODO: this is quirky. We need an internal handle for certain
- * operations, but we do not add it to the multi (if there is one).
- * But we give it the multi so that socket event operations can work.
- * Probably better to have an internal handle owned by the multi that
- * can be used for cpool operations. */
- cpool->idata->multi = multi;
-#ifdef DEBUGBUILD
- if(getenv("CURL_DEBUG"))
- cpool->idata->set.verbose = TRUE;
-#endif
+ cpool->idata = idata;
+ cpool->share = share;
+ cpool->initialised = TRUE;
+}
+
+/* Return the "first" connection in the pool or NULL. */
+static struct connectdata *cpool_get_first(struct cpool *cpool)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+ struct cpool_bundle *bundle;
+ struct Curl_llist_node *conn_node;
+
+ Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
+ bundle = he->ptr;
+ conn_node = Curl_llist_head(&bundle->conns);
+ if(conn_node)
+ return Curl_node_elem(conn_node);
+ }
+ return NULL;
+}
+
+
+static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
+ struct connectdata *conn)
+{
+ return Curl_hash_pick(&cpool->dest2bundle,
+ conn->destination, strlen(conn->destination) + 1);
+}
- cpool->disconnect_cb = disconnect_cb;
- cpool->idata->multi = cpool->multi = multi;
- cpool->idata->share = cpool->share = share;
- return 0; /* good */
+static void cpool_remove_bundle(struct cpool *cpool,
+ struct cpool_bundle *bundle)
+{
+ if(!cpool)
+ return;
+ Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
+}
+
+
+static void cpool_remove_conn(struct cpool *cpool,
+ struct connectdata *conn)
+{
+ struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
+ DEBUGASSERT(cpool);
+ if(list) {
+ /* The connection is certainly in the pool, but where? */
+ struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
+ if(bundle && (list == &bundle->conns)) {
+ cpool_bundle_remove(bundle, conn);
+ if(!Curl_llist_count(&bundle->conns))
+ cpool_remove_bundle(cpool, bundle);
+ conn->bits.in_cpool = FALSE;
+ cpool->num_conn--;
+ }
+ else {
+ /* Should have been in the bundle list */
+ DEBUGASSERT(NULL);
+ }
+ }
}
void Curl_cpool_destroy(struct cpool *cpool)
{
- if(cpool) {
- if(cpool->idata) {
- cpool_close_and_destroy_all(cpool);
- /* The internal closure handle is special and we need to
- * disconnect it from multi/share before closing it down. */
- cpool->idata->multi = NULL;
- cpool->idata->share = NULL;
- Curl_close(&cpool->idata);
+ if(cpool && cpool->initialised && cpool->idata) {
+ struct connectdata *conn;
+ SIGPIPE_VARIABLE(pipe_st);
+
+ CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
+ cpool->share ? "[SHARE] " : "", cpool->num_conn);
+ /* Move all connections to the shutdown list */
+ sigpipe_init(&pipe_st);
+ CPOOL_LOCK(cpool, cpool->idata);
+ conn = cpool_get_first(cpool);
+ while(conn) {
+ cpool_remove_conn(cpool, conn);
+ sigpipe_apply(cpool->idata, &pipe_st);
+ connclose(conn, "kill all");
+ cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
+ conn = cpool_get_first(cpool);
}
+ CPOOL_UNLOCK(cpool, cpool->idata);
+ sigpipe_restore(&pipe_st);
Curl_hash_destroy(&cpool->dest2bundle);
- cpool->multi = NULL;
}
}
@@ -217,23 +251,14 @@ void Curl_cpool_xfer_init(struct Curl_easy *data) DEBUGASSERT(cpool);
if(cpool) {
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
/* the identifier inside the connection cache */
data->id = cpool->next_easy_id++;
if(cpool->next_easy_id <= 0)
cpool->next_easy_id = 0;
data->state.lastconnect_id = -1;
- /* The closure handle only ever has default timeouts set. To improve the
- state somewhat we clone the timeouts from each added handle so that the
- closure handle always has the same timeouts as the most recently added
- easy handle. */
- cpool->idata->set.timeout = data->set.timeout;
- cpool->idata->set.server_response_timeout =
- data->set.server_response_timeout;
- cpool->idata->set.no_signal = data->set.no_signal;
-
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
else {
/* We should not get here, but in a non-debug build, do something */
@@ -242,19 +267,12 @@ void Curl_cpool_xfer_init(struct Curl_easy *data) }
}
-static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
- struct connectdata *conn)
-{
- return Curl_hash_pick(&cpool->dest2bundle,
- conn->destination, conn->destination_len);
-}
-
static struct cpool_bundle *
cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
{
struct cpool_bundle *bundle;
- bundle = cpool_bundle_create(conn->destination, conn->destination_len);
+ bundle = cpool_bundle_create(conn->destination);
if(!bundle)
return NULL;
@@ -266,17 +284,70 @@ cpool_add_bundle(struct cpool *cpool, struct connectdata *conn) return bundle;
}
-static void cpool_remove_bundle(struct cpool *cpool,
- struct cpool_bundle *bundle)
+static struct connectdata *
+cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
{
- if(!cpool)
- return;
+ struct Curl_llist_node *curr;
+ timediff_t highscore = -1;
+ timediff_t score;
+ struct curltime now;
+ struct connectdata *oldest_idle = NULL;
+ struct connectdata *conn;
- Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
+ now = curlx_now();
+ curr = Curl_llist_head(&bundle->conns);
+ while(curr) {
+ conn = Curl_node_elem(curr);
+
+ if(!CONN_INUSE(conn)) {
+ /* Set higher score for the age passed since the connection was used */
+ score = curlx_timediff(now, conn->lastused);
+
+ if(score > highscore) {
+ highscore = score;
+ oldest_idle = conn;
+ }
+ }
+ curr = Curl_node_next(curr);
+ }
+ return oldest_idle;
+}
+
+static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_llist_node *curr;
+ struct Curl_hash_element *he;
+ struct connectdata *oldest_idle = NULL;
+ struct cpool_bundle *bundle;
+ struct curltime now;
+ timediff_t highscore =- 1;
+ timediff_t score;
+
+ now = curlx_now();
+ Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
+ struct connectdata *conn;
+ bundle = he->ptr;
+
+ for(curr = Curl_llist_head(&bundle->conns); curr;
+ curr = Curl_node_next(curr)) {
+ conn = Curl_node_elem(curr);
+ if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
+ continue;
+ /* Set higher score for the age passed since the connection was used */
+ score = curlx_timediff(now, conn->lastused);
+ if(score > highscore) {
+ highscore = score;
+ oldest_idle = conn;
+ }
+ }
+ }
+ return oldest_idle;
}
-static struct connectdata *
-cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle);
int Curl_cpool_check_limits(struct Curl_easy *data,
struct connectdata *conn)
@@ -285,70 +356,95 @@ int Curl_cpool_check_limits(struct Curl_easy *data, struct cpool_bundle *bundle;
size_t dest_limit = 0;
size_t total_limit = 0;
+ size_t shutdowns;
int result = CPOOL_LIMIT_OK;
if(!cpool)
return CPOOL_LIMIT_OK;
- if(data && data->multi) {
- dest_limit = data->multi->max_host_connections;
- total_limit = data->multi->max_total_connections;
+ if(cpool->idata->multi) {
+ dest_limit = cpool->idata->multi->max_host_connections;
+ total_limit = cpool->idata->multi->max_total_connections;
}
if(!dest_limit && !total_limit)
return CPOOL_LIMIT_OK;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, cpool->idata);
if(dest_limit) {
+ size_t live;
+
bundle = cpool_find_bundle(cpool, conn);
- while(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
- struct connectdata *oldest_idle = NULL;
- /* The bundle is full. Extract the oldest connection that may
- * be removed now, if there is one. */
- oldest_idle = cpool_bundle_get_oldest_idle(bundle);
- if(!oldest_idle)
+ live = bundle ? Curl_llist_count(&bundle->conns) : 0;
+ shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
+ while((live + shutdowns) >= dest_limit) {
+ if(shutdowns) {
+ /* close one connection in shutdown right away, if we can */
+ if(!Curl_cshutdn_close_oldest(data, conn->destination))
+ break;
+ }
+ else if(!bundle)
break;
- /* disconnect the old conn and continue */
- DEBUGF(infof(data, "Discarding connection #%"
- FMT_OFF_T " from %zu to reach destination "
- "limit of %zu", oldest_idle->connection_id,
- Curl_llist_count(&bundle->conns), dest_limit));
- Curl_cpool_disconnect(data, oldest_idle, FALSE);
-
- /* in case the bundle was destroyed in disconnect, look it up again */
- bundle = cpool_find_bundle(cpool, conn);
+ else {
+ struct connectdata *oldest_idle = NULL;
+ /* The bundle is full. Extract the oldest connection that may
+ * be removed now, if there is one. */
+ oldest_idle = cpool_bundle_get_oldest_idle(bundle);
+ if(!oldest_idle)
+ break;
+ /* disconnect the old conn and continue */
+ CURL_TRC_M(data, "Discarding connection #%"
+ FMT_OFF_T " from %zu to reach destination "
+ "limit of %zu", oldest_idle->connection_id,
+ Curl_llist_count(&bundle->conns), dest_limit);
+ Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
+
+ /* in case the bundle was destroyed in disconnect, look it up again */
+ bundle = cpool_find_bundle(cpool, conn);
+ live = bundle ? Curl_llist_count(&bundle->conns) : 0;
+ }
+ shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination);
}
- if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
+ if((live + shutdowns) >= dest_limit) {
result = CPOOL_LIMIT_DEST;
goto out;
}
}
if(total_limit) {
- while(cpool->num_conn >= total_limit) {
- struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
- if(!oldest_idle)
- break;
- /* disconnect the old conn and continue */
- DEBUGF(infof(data, "Discarding connection #%"
+ shutdowns = Curl_cshutdn_count(cpool->idata);
+ while((cpool->num_conn + shutdowns) >= total_limit) {
+ if(shutdowns) {
+ /* close one connection in shutdown right away, if we can */
+ if(!Curl_cshutdn_close_oldest(data, NULL))
+ break;
+ }
+ else {
+ struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
+ if(!oldest_idle)
+ break;
+ /* disconnect the old conn and continue */
+ CURL_TRC_M(data, "Discarding connection #%"
FMT_OFF_T " from %zu to reach total "
"limit of %zu",
- oldest_idle->connection_id, cpool->num_conn, total_limit));
- Curl_cpool_disconnect(data, oldest_idle, FALSE);
+ oldest_idle->connection_id, cpool->num_conn, total_limit);
+ Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
+ }
+ shutdowns = Curl_cshutdn_count(cpool->idata);
}
- if(cpool->num_conn >= total_limit) {
+ if((cpool->num_conn + shutdowns) >= total_limit) {
result = CPOOL_LIMIT_TOTAL;
goto out;
}
}
out:
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, cpool->idata);
return result;
}
-CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
- struct connectdata *conn)
+CURLcode Curl_cpool_add(struct Curl_easy *data,
+ struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct cpool_bundle *bundle = NULL;
@@ -359,7 +455,7 @@ CURLcode Curl_cpool_add_conn(struct Curl_easy *data, if(!cpool)
return CURLE_FAILED_INIT;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
bundle = cpool_find_bundle(cpool, conn);
if(!bundle) {
bundle = cpool_add_bundle(cpool, conn);
@@ -372,37 +468,15 @@ CURLcode Curl_cpool_add_conn(struct Curl_easy *data, cpool_bundle_add(bundle, conn);
conn->connection_id = cpool->next_connection_id++;
cpool->num_conn++;
- DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". "
- "The cache now contains %zu members",
- conn->connection_id, cpool->num_conn));
+ CURL_TRC_M(data, "[CPOOL] added connection %" FMT_OFF_T ". "
+ "The cache now contains %zu members",
+ conn->connection_id, cpool->num_conn);
out:
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
return result;
}
-static void cpool_remove_conn(struct cpool *cpool,
- struct connectdata *conn)
-{
- struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
- DEBUGASSERT(cpool);
- if(list) {
- /* The connection is certainly in the pool, but where? */
- struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
- if(bundle && (list == &bundle->conns)) {
- cpool_bundle_remove(bundle, conn);
- if(!Curl_llist_count(&bundle->conns))
- cpool_remove_bundle(cpool, bundle);
- conn->bits.in_cpool = FALSE;
- cpool->num_conn--;
- }
- else {
- /* Not in a bundle, already in the shutdown list? */
- DEBUGASSERT(list == &cpool->shutdowns);
- }
- }
-}
-
/* This function iterates the entire connection pool and calls the function
func() with the connection pointer as the first argument and the supplied
'param' argument as the other.
@@ -450,25 +524,6 @@ static bool cpool_foreach(struct Curl_easy *data, return FALSE;
}
-/* Return a live connection in the pool or NULL. */
-static struct connectdata *cpool_get_live_conn(struct cpool *cpool)
-{
- struct Curl_hash_iterator iter;
- struct Curl_hash_element *he;
- struct cpool_bundle *bundle;
- struct Curl_llist_node *conn_node;
-
- Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
- for(he = Curl_hash_next_element(&iter); he;
- he = Curl_hash_next_element(&iter)) {
- bundle = he->ptr;
- conn_node = Curl_llist_head(&bundle->conns);
- if(conn_node)
- return Curl_node_elem(conn_node);
- }
- return NULL;
-}
-
/*
* A connection (already in the pool) has become idle. Do any
* cleanups in regard to the pool's limits.
@@ -479,103 +534,36 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, struct connectdata *conn)
{
unsigned int maxconnects = !data->multi->maxconnects ?
- data->multi->num_easy * 4 : data->multi->maxconnects;
+ (Curl_multi_xfers_running(data->multi) * 4) : data->multi->maxconnects;
struct connectdata *oldest_idle = NULL;
struct cpool *cpool = cpool_get_instance(data);
bool kept = TRUE;
- conn->lastused = Curl_now(); /* it was used up until now */
+ conn->lastused = curlx_now(); /* it was used up until now */
if(cpool && maxconnects) {
/* may be called form a callback already under lock */
bool do_lock = !CPOOL_IS_LOCKED(cpool);
if(do_lock)
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
if(cpool->num_conn > maxconnects) {
- infof(data, "Connection pool is full, closing the oldest one");
+ infof(data, "Connection pool is full, closing the oldest of %zu/%u",
+ cpool->num_conn, maxconnects);
oldest_idle = cpool_get_oldest_idle(cpool);
kept = (oldest_idle != conn);
if(oldest_idle) {
- Curl_cpool_disconnect(cpool->idata, oldest_idle, FALSE);
+ Curl_conn_terminate(data, oldest_idle, FALSE);
}
}
if(do_lock)
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
return kept;
}
-/*
- * This function finds the connection in the connection bundle that has been
- * unused for the longest time.
- */
-static struct connectdata *
-cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
-{
- struct Curl_llist_node *curr;
- timediff_t highscore = -1;
- timediff_t score;
- struct curltime now;
- struct connectdata *oldest_idle = NULL;
- struct connectdata *conn;
-
- now = Curl_now();
- curr = Curl_llist_head(&bundle->conns);
- while(curr) {
- conn = Curl_node_elem(curr);
-
- if(!CONN_INUSE(conn)) {
- /* Set higher score for the age passed since the connection was used */
- score = Curl_timediff(now, conn->lastused);
-
- if(score > highscore) {
- highscore = score;
- oldest_idle = conn;
- }
- }
- curr = Curl_node_next(curr);
- }
- return oldest_idle;
-}
-
-static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
-{
- struct Curl_hash_iterator iter;
- struct Curl_llist_node *curr;
- struct Curl_hash_element *he;
- struct connectdata *oldest_idle = NULL;
- struct cpool_bundle *bundle;
- struct curltime now;
- timediff_t highscore =- 1;
- timediff_t score;
-
- now = Curl_now();
- Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
-
- for(he = Curl_hash_next_element(&iter); he;
- he = Curl_hash_next_element(&iter)) {
- struct connectdata *conn;
- bundle = he->ptr;
-
- for(curr = Curl_llist_head(&bundle->conns); curr;
- curr = Curl_node_next(curr)) {
- conn = Curl_node_elem(curr);
- if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
- continue;
- /* Set higher score for the age passed since the connection was used */
- score = Curl_timediff(now, conn->lastused);
- if(score > highscore) {
- highscore = score;
- oldest_idle = conn;
- }
- }
- }
- return oldest_idle;
-}
-
bool Curl_cpool_find(struct Curl_easy *data,
- const char *destination, size_t dest_len,
+ const char *destination,
Curl_cpool_conn_match_cb *conn_cb,
Curl_cpool_done_match_cb *done_cb,
void *userdata)
@@ -589,8 +577,10 @@ bool Curl_cpool_find(struct Curl_easy *data, if(!cpool)
return FALSE;
- CPOOL_LOCK(cpool);
- bundle = Curl_hash_pick(&cpool->dest2bundle, (void *)destination, dest_len);
+ CPOOL_LOCK(cpool, data);
+ bundle = Curl_hash_pick(&cpool->dest2bundle,
+ CURL_UNCONST(destination),
+ strlen(destination) + 1);
if(bundle) {
struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
while(curr) {
@@ -608,89 +598,10 @@ bool Curl_cpool_find(struct Curl_easy *data, if(done_cb) {
result = done_cb(result, userdata);
}
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
return result;
}
-static void cpool_shutdown_discard_all(struct cpool *cpool)
-{
- struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
- struct connectdata *conn;
-
- if(!e)
- return;
-
- DEBUGF(infof(cpool->idata, "cpool_shutdown_discard_all"));
- while(e) {
- conn = Curl_node_elem(e);
- Curl_node_remove(e);
- DEBUGF(infof(cpool->idata, "discard connection #%" FMT_OFF_T,
- conn->connection_id));
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- e = Curl_llist_head(&cpool->shutdowns);
- }
-}
-
-static void cpool_close_and_destroy_all(struct cpool *cpool)
-{
- struct connectdata *conn;
- int timeout_ms = 0;
- SIGPIPE_VARIABLE(pipe_st);
-
- DEBUGASSERT(cpool);
- /* Move all connections to the shutdown list */
- sigpipe_init(&pipe_st);
- CPOOL_LOCK(cpool);
- conn = cpool_get_live_conn(cpool);
- while(conn) {
- cpool_remove_conn(cpool, conn);
- sigpipe_apply(cpool->idata, &pipe_st);
- connclose(conn, "kill all");
- cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
-
- conn = cpool_get_live_conn(cpool);
- }
- CPOOL_UNLOCK(cpool);
-
- /* Just for testing, run graceful shutdown */
-#ifdef DEBUGBUILD
- {
- char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
- if(p) {
- long l = strtol(p, NULL, 10);
- if(l > 0 && l < INT_MAX)
- timeout_ms = (int)l;
- }
- }
-#endif
- sigpipe_apply(cpool->idata, &pipe_st);
- cpool_shutdown_all(cpool, cpool->idata, timeout_ms);
-
- /* discard all connections in the shutdown list */
- cpool_shutdown_discard_all(cpool);
-
- Curl_hostcache_clean(cpool->idata, cpool->idata->dns.hostcache);
- sigpipe_restore(&pipe_st);
-}
-
-
-static void cpool_shutdown_destroy_oldest(struct cpool *cpool)
-{
- struct Curl_llist_node *e;
- struct connectdata *conn;
-
- e = Curl_llist_head(&cpool->shutdowns);
- if(e) {
- SIGPIPE_VARIABLE(pipe_st);
- conn = Curl_node_elem(e);
- Curl_node_remove(e);
- sigpipe_init(&pipe_st);
- sigpipe_apply(cpool->idata, &pipe_st);
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- sigpipe_restore(&pipe_st);
- }
-}
-
static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
@@ -699,6 +610,7 @@ static void cpool_discard_conn(struct cpool *cpool, bool done = FALSE;
DEBUGASSERT(data);
+ DEBUGASSERT(!data->conn);
DEBUGASSERT(cpool);
DEBUGASSERT(!conn->bits.in_cpool);
@@ -707,9 +619,9 @@ static void cpool_discard_conn(struct cpool *cpool, * are other users of it
*/
if(CONN_INUSE(conn) && !aborted) {
- DEBUGF(infof(data, "[CCACHE] not discarding #%" FMT_OFF_T
- " still in use by %zu transfers", conn->connection_id,
- CONN_INUSE(conn)));
+ CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
+ " still in use by %u transfers", conn->connection_id,
+ CONN_ATTACHED(conn));
return;
}
@@ -728,52 +640,18 @@ static void cpool_discard_conn(struct cpool *cpool, done = TRUE;
if(!done) {
/* Attempt to shutdown the connection right away. */
- Curl_attach_connection(data, conn);
- cpool_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
- conn->connection_id, done));
- Curl_detach_connection(data);
- }
-
- if(done) {
- cpool_close_and_destroy(cpool, conn, data, FALSE);
- return;
+ Curl_cshutdn_run_once(cpool->idata, conn, &done);
}
- /* Add the connection to our shutdown list for non-blocking shutdown
- * during multi processing. */
- if(data->multi && data->multi->max_shutdown_connections > 0 &&
- (data->multi->max_shutdown_connections >=
- (long)Curl_llist_count(&cpool->shutdowns))) {
- DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection "
- "due to limit of %ld",
- data->multi->max_shutdown_connections));
- cpool_shutdown_destroy_oldest(cpool);
- }
-
- if(data->multi && data->multi->socket_cb) {
- DEBUGASSERT(cpool == &data->multi->cpool);
- /* Start with an empty shutdown pollset, so out internal closure handle
- * is added to the sockets. */
- memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll));
- if(cpool_update_shutdown_ev(data->multi, cpool->idata, conn)) {
- DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
- "discarding #%" FMT_OFF_T,
- conn->connection_id));
- cpool_close_and_destroy(cpool, conn, data, FALSE);
- return;
- }
- }
-
- Curl_llist_append(&cpool->shutdowns, conn, &conn->cpool_node);
- DEBUGF(infof(data, "[CCACHE] added #%" FMT_OFF_T
- " to shutdown list of length %zu", conn->connection_id,
- Curl_llist_count(&cpool->shutdowns)));
+ if(done || !data->multi)
+ Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
+ else
+ Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
}
-void Curl_cpool_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool aborted)
+void Curl_conn_terminate(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool aborted)
{
struct cpool *cpool = cpool_get_instance(data);
bool do_lock;
@@ -787,7 +665,7 @@ void Curl_cpool_disconnect(struct Curl_easy *data, * are other users of it */
if(CONN_INUSE(conn) && !aborted) {
DEBUGASSERT(0); /* does this ever happen? */
- DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
+ DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn)));
return;
}
@@ -795,15 +673,17 @@ void Curl_cpool_disconnect(struct Curl_easy *data, * user callback in find. */
do_lock = !CPOOL_IS_LOCKED(cpool);
if(do_lock)
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
if(conn->bits.in_cpool) {
cpool_remove_conn(cpool, conn);
DEBUGASSERT(!conn->bits.in_cpool);
}
- /* Run the callback to let it clean up anything it wants to. */
- aborted = cpool->disconnect_cb(data, conn, aborted);
+ /* treat the connection as aborted in CONNECT_ONLY situations,
+ * so no graceful shutdown is attempted. */
+ if(conn->connect_only)
+ aborted = TRUE;
if(data->multi) {
/* Add it to the multi's cpool for shutdown handling */
@@ -812,376 +692,15 @@ void Curl_cpool_disconnect(struct Curl_easy *data, cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
}
else {
- /* No multi available. Make a best-effort shutdown + close */
+ /* No multi available, terminate */
infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
- cpool_close_and_destroy(NULL, conn, data, !aborted);
+ Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
}
if(do_lock)
- CPOOL_UNLOCK(cpool);
-}
-
-static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
- struct connectdata *conn)
-{
- if(!conn->bits.shutdown_handler) {
- if(conn->dns_entry)
- Curl_resolv_unlink(data, &conn->dns_entry);
-
- /* Cleanup NTLM connection-related data */
- Curl_http_auth_cleanup_ntlm(conn);
-
- /* Cleanup NEGOTIATE connection-related data */
- Curl_http_auth_cleanup_negotiate(conn);
-
- if(conn->handler && conn->handler->disconnect) {
- /* This is set if protocol-specific cleanups should be made */
- DEBUGF(infof(data, "connection #%" FMT_OFF_T
- ", shutdown protocol handler (aborted=%d)",
- conn->connection_id, conn->bits.aborted));
-
- conn->handler->disconnect(data, conn, conn->bits.aborted);
- }
-
- /* possible left-overs from the async name resolvers */
- Curl_resolver_cancel(data);
-
- conn->bits.shutdown_handler = TRUE;
- }
-}
-
-static void cpool_run_conn_shutdown(struct Curl_easy *data,
- struct connectdata *conn,
- bool *done)
-{
- CURLcode r1, r2;
- bool done1, done2;
-
- /* We expect to be attached when called */
- DEBUGASSERT(data->conn == conn);
-
- cpool_run_conn_shutdown_handler(data, conn);
-
- if(conn->bits.shutdown_filters) {
- *done = TRUE;
- return;
- }
-
- if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
- r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
- else {
- r1 = CURLE_OK;
- done1 = TRUE;
- }
-
- if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
- r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
- else {
- r2 = CURLE_OK;
- done2 = TRUE;
- }
-
- /* we are done when any failed or both report success */
- *done = (r1 || r2 || (done1 && done2));
- if(*done)
- conn->bits.shutdown_filters = TRUE;
+ CPOOL_UNLOCK(cpool, data);
}
-static CURLcode cpool_add_pollfds(struct cpool *cpool,
- struct curl_pollfds *cpfds)
-{
- CURLcode result = CURLE_OK;
-
- if(Curl_llist_head(&cpool->shutdowns)) {
- struct Curl_llist_node *e;
- struct easy_pollset ps;
- struct connectdata *conn;
-
- for(e = Curl_llist_head(&cpool->shutdowns); e;
- e = Curl_node_next(e)) {
- conn = Curl_node_elem(e);
- memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(cpool->idata, conn);
- Curl_conn_adjust_pollset(cpool->idata, &ps);
- Curl_detach_connection(cpool->idata);
-
- result = Curl_pollfds_add_ps(cpfds, &ps);
- if(result) {
- Curl_pollfds_cleanup(cpfds);
- goto out;
- }
- }
- }
-out:
- return result;
-}
-
-CURLcode Curl_cpool_add_pollfds(struct cpool *cpool,
- struct curl_pollfds *cpfds)
-{
- CURLcode result;
- CPOOL_LOCK(cpool);
- result = cpool_add_pollfds(cpool, cpfds);
- CPOOL_UNLOCK(cpool);
- return result;
-}
-
-CURLcode Curl_cpool_add_waitfds(struct cpool *cpool,
- struct curl_waitfds *cwfds)
-{
- CURLcode result = CURLE_OK;
-
- CPOOL_LOCK(cpool);
- if(Curl_llist_head(&cpool->shutdowns)) {
- struct Curl_llist_node *e;
- struct easy_pollset ps;
- struct connectdata *conn;
-
- for(e = Curl_llist_head(&cpool->shutdowns); e;
- e = Curl_node_next(e)) {
- conn = Curl_node_elem(e);
- memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(cpool->idata, conn);
- Curl_conn_adjust_pollset(cpool->idata, &ps);
- Curl_detach_connection(cpool->idata);
-
- result = Curl_waitfds_add_ps(cwfds, &ps);
- if(result)
- goto out;
- }
- }
-out:
- CPOOL_UNLOCK(cpool);
- return result;
-}
-
-static void cpool_perform(struct cpool *cpool)
-{
- struct Curl_easy *data = cpool->idata;
- struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
- struct Curl_llist_node *enext;
- struct connectdata *conn;
- struct curltime *nowp = NULL;
- struct curltime now;
- timediff_t next_from_now_ms = 0, ms;
- bool done;
-
- if(!e)
- return;
-
- DEBUGASSERT(data);
- DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown",
- Curl_llist_count(&cpool->shutdowns)));
- while(e) {
- enext = Curl_node_next(e);
- conn = Curl_node_elem(e);
- Curl_attach_connection(data, conn);
- cpool_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
- conn->connection_id, done));
- Curl_detach_connection(data);
- if(done) {
- Curl_node_remove(e);
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- }
- else {
- /* Not done, when does this connection time out? */
- if(!nowp) {
- now = Curl_now();
- nowp = &now;
- }
- ms = Curl_conn_shutdown_timeleft(conn, nowp);
- if(ms && ms < next_from_now_ms)
- next_from_now_ms = ms;
- }
- e = enext;
- }
-
- if(next_from_now_ms)
- Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW);
-}
-
-void Curl_cpool_multi_perform(struct Curl_multi *multi)
-{
- CPOOL_LOCK(&multi->cpool);
- cpool_perform(&multi->cpool);
- CPOOL_UNLOCK(&multi->cpool);
-}
-
-
-/*
- * Close and destroy the connection. Run the shutdown sequence once,
- * of so requested.
- */
-static void cpool_close_and_destroy(struct cpool *cpool,
- struct connectdata *conn,
- struct Curl_easy *data,
- bool do_shutdown)
-{
- bool done;
-
- /* there must be a connection to close */
- DEBUGASSERT(conn);
- /* it must be removed from the connection pool */
- DEBUGASSERT(!conn->bits.in_cpool);
- /* there must be an associated transfer */
- DEBUGASSERT(data || cpool);
- if(!data)
- data = cpool->idata;
-
- /* the transfer must be detached from the connection */
- DEBUGASSERT(data && !data->conn);
-
- Curl_attach_connection(data, conn);
-
- cpool_run_conn_shutdown_handler(data, conn);
- if(do_shutdown) {
- /* Make a last attempt to shutdown handlers and filters, if
- * not done so already. */
- cpool_run_conn_shutdown(data, conn, &done);
- }
-
- if(cpool)
- DEBUGF(infof(data, "[CCACHE] closing #%" FMT_OFF_T,
- conn->connection_id));
- else
- DEBUGF(infof(data, "closing connection #%" FMT_OFF_T,
- conn->connection_id));
- Curl_conn_close(data, SECONDARYSOCKET);
- Curl_conn_close(data, FIRSTSOCKET);
- Curl_detach_connection(data);
-
- Curl_conn_free(data, conn);
-}
-
-
-static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- struct easy_pollset ps;
- CURLMcode mresult;
-
- DEBUGASSERT(data);
- DEBUGASSERT(multi);
- DEBUGASSERT(multi->socket_cb);
-
- memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(data, conn);
- Curl_conn_adjust_pollset(data, &ps);
- Curl_detach_connection(data);
-
- mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll);
-
- if(!mresult) /* Remember for next time */
- memcpy(&conn->shutdown_poll, &ps, sizeof(ps));
- return mresult;
-}
-
-void Curl_cpool_multi_socket(struct Curl_multi *multi,
- curl_socket_t s, int ev_bitmask)
-{
- struct cpool *cpool = &multi->cpool;
- struct Curl_easy *data = cpool->idata;
- struct Curl_llist_node *e;
- struct connectdata *conn;
- bool done;
-
- (void)ev_bitmask;
- DEBUGASSERT(multi->socket_cb);
- CPOOL_LOCK(cpool);
- e = Curl_llist_head(&cpool->shutdowns);
- while(e) {
- conn = Curl_node_elem(e);
- if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
- Curl_attach_connection(data, conn);
- cpool_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
- conn->connection_id, done));
- Curl_detach_connection(data);
- if(done || cpool_update_shutdown_ev(multi, data, conn)) {
- Curl_node_remove(e);
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- }
- break;
- }
- e = Curl_node_next(e);
- }
- CPOOL_UNLOCK(cpool);
-}
-
-#define NUM_POLLS_ON_STACK 10
-
-static CURLcode cpool_shutdown_wait(struct cpool *cpool, int timeout_ms)
-{
- struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
- struct curl_pollfds cpfds;
- CURLcode result;
-
- Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
-
- result = cpool_add_pollfds(cpool, &cpfds);
- if(result)
- goto out;
-
- Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
-
-out:
- Curl_pollfds_cleanup(&cpfds);
- return result;
-}
-
-static void cpool_shutdown_all(struct cpool *cpool,
- struct Curl_easy *data, int timeout_ms)
-{
- struct connectdata *conn;
- struct curltime started = Curl_now();
-
- if(!data)
- return;
- (void)data;
-
- DEBUGF(infof(data, "cpool shutdown all"));
-
- /* Move all connections into the shutdown queue */
- for(conn = cpool_get_live_conn(cpool); conn;
- conn = cpool_get_live_conn(cpool)) {
- /* Move conn from live set to shutdown or destroy right away */
- DEBUGF(infof(data, "moving connection #%" FMT_OFF_T
- " to shutdown queue", conn->connection_id));
- cpool_remove_conn(cpool, conn);
- cpool_discard_conn(cpool, data, conn, FALSE);
- }
-
- while(Curl_llist_head(&cpool->shutdowns)) {
- timediff_t timespent;
- int remain_ms;
-
- cpool_perform(cpool);
-
- if(!Curl_llist_head(&cpool->shutdowns)) {
- DEBUGF(infof(data, "cpool shutdown ok"));
- break;
- }
-
- /* wait for activity, timeout or "nothing" */
- timespent = Curl_timediff(Curl_now(), started);
- if(timespent >= (timediff_t)timeout_ms) {
- DEBUGF(infof(data, "cpool shutdown %s",
- (timeout_ms > 0) ? "timeout" : "best effort done"));
- break;
- }
-
- remain_ms = timeout_ms - (int)timespent;
- if(cpool_shutdown_wait(cpool, remain_ms)) {
- DEBUGF(infof(data, "cpool shutdown all, abort"));
- break;
- }
- }
-
- /* Due to errors/timeout, we might come here without being done. */
- cpool_shutdown_discard_all(cpool);
-}
struct cpool_reaper_ctx {
struct curltime now;
@@ -1193,7 +712,7 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, struct cpool_reaper_ctx *rctx = param;
if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
/* stop the iteration here, pass back the connection that was pruned */
- Curl_cpool_disconnect(data, conn, FALSE);
+ Curl_conn_terminate(data, conn, FALSE);
return 1;
}
return 0; /* continue iteration */
@@ -1215,16 +734,16 @@ void Curl_cpool_prune_dead(struct Curl_easy *data) if(!cpool)
return;
- rctx.now = Curl_now();
- CPOOL_LOCK(cpool);
- elapsed = Curl_timediff(rctx.now, cpool->last_cleanup);
+ rctx.now = curlx_now();
+ CPOOL_LOCK(cpool, data);
+ elapsed = curlx_timediff(rctx.now, cpool->last_cleanup);
if(elapsed >= 1000L) {
while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
;
cpool->last_cleanup = rctx.now;
}
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
static int conn_upkeep(struct Curl_easy *data,
@@ -1232,7 +751,6 @@ static int conn_upkeep(struct Curl_easy *data, void *param)
{
struct curltime *now = param;
- /* TODO, shall we reap connections that return an error here? */
Curl_conn_upkeep(data, conn, now);
return 0; /* continue iteration */
}
@@ -1240,14 +758,14 @@ static int conn_upkeep(struct Curl_easy *data, CURLcode Curl_cpool_upkeep(void *data)
{
struct cpool *cpool = cpool_get_instance(data);
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
if(!cpool)
return CURLE_OK;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &now, conn_upkeep);
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
return CURLE_OK;
}
@@ -1278,9 +796,9 @@ struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data, return NULL;
fctx.id = conn_id;
fctx.conn = NULL;
- CPOOL_LOCK(cpool);
- cpool_foreach(cpool->idata, cpool, &fctx, cpool_find_conn);
- CPOOL_UNLOCK(cpool);
+ CPOOL_LOCK(cpool, data);
+ cpool_foreach(data, cpool, &fctx, cpool_find_conn);
+ CPOOL_UNLOCK(cpool, data);
return fctx.conn;
}
@@ -1313,9 +831,9 @@ void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id, dctx.id = conn_id;
dctx.cb = cb;
dctx.cbdata = cbdata;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &dctx, cpool_do_conn);
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
void Curl_cpool_do_locked(struct Curl_easy *data,
@@ -1324,9 +842,9 @@ void Curl_cpool_do_locked(struct Curl_easy *data, {
struct cpool *cpool = cpool_get_instance(data);
if(cpool) {
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
cb(conn, data, cbdata);
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
else
cb(conn, data, cbdata);
|