diff --git a/lib/conncache.c b/lib/conncache.c index d4a71c8a97..21b2e61e41 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -42,7 +42,7 @@ do { \ if(c) { \ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ - Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \ + Curl_share_lock_share((c)->share, (d), CURL_LOCK_DATA_CONNECT, \ CURL_LOCK_ACCESS_SINGLE); \ DEBUGASSERT(!(c)->locked); \ (c)->locked = TRUE; \ @@ -55,7 +55,7 @@ DEBUGASSERT((c)->locked); \ (c)->locked = FALSE; \ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ - Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT); \ + Curl_share_unlock_share((c)->share, (d), CURL_LOCK_DATA_CONNECT); \ } \ } while(0) @@ -111,16 +111,12 @@ static void cpool_bundle_free_entry(void *freethis) } void Curl_cpool_init(struct cpool *cpool, - struct Curl_easy *idata, struct Curl_share *share, size_t size) { Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str, curlx_str_key_compare, cpool_bundle_free_entry); - DEBUGASSERT(idata); - - cpool->idata = idata; cpool->share = share; cpool->initialized = TRUE; } @@ -186,6 +182,8 @@ static void cpool_discard_conn(struct cpool *cpool, struct connectdata *conn, bool aborted) { + struct cshutdn *cshutdn; + struct Curl_easy *admin; bool done = FALSE; DEBUGASSERT(data); @@ -193,12 +191,13 @@ static void cpool_discard_conn(struct cpool *cpool, DEBUGASSERT(cpool); DEBUGASSERT(!conn->bits.in_cpool); + admin = Curl_get_admin(data); /* * If this connection is not marked to force-close, leave it open if there * are other users of it */ if(CONN_INUSE(conn) && !aborted) { - CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T + CURL_TRC_M(admin, "[CPOOL] not discarding #%" FMT_OFF_T " still in use by %u transfers", conn->connection_id, conn->attached_xfers); return; @@ -219,35 +218,36 @@ static void cpool_discard_conn(struct cpool *cpool, done = TRUE; if(!done) { /* Attempt to shutdown the connection right away. */ - Curl_cshutdn_run_once(cpool->idata, conn, &done); + Curl_conn_shutdown_once(admin, conn, &done); } - if(done || !data->multi) - Curl_cshutdn_terminate(cpool->idata, conn, FALSE); + cshutdn = Curl_cshutdn_get(data); + if(done || !cshutdn) + Curl_conn_terminate(admin, conn, FALSE); else - Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn); + Curl_cshutdn_add(cshutdn, conn, cpool->num_conn); } -void Curl_cpool_destroy(struct cpool *cpool) +void Curl_cpool_destroy(struct cpool *cpool, struct Curl_easy *admin) { - if(cpool && cpool->initialized && cpool->idata) { + if(cpool && cpool->initialized && admin) { struct connectdata *conn; struct Curl_sigpipe_ctx pipe_ctx; - CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections", + CURL_TRC_M(admin, "%s[CPOOL] destroy, %zu connections", cpool->share ? "[SHARE] " : "", cpool->num_conn); /* Move all connections to the shutdown list */ sigpipe_init(&pipe_ctx); - CPOOL_LOCK(cpool, cpool->idata); + CPOOL_LOCK(cpool, admin); conn = cpool_get_first(cpool); if(conn) - sigpipe_apply(cpool->idata, &pipe_ctx); + sigpipe_apply(admin, &pipe_ctx); while(conn) { cpool_remove_conn(cpool, conn); - cpool_discard_conn(cpool, cpool->idata, conn, FALSE); + cpool_discard_conn(cpool, admin, conn, FALSE); conn = cpool_get_first(cpool); } - CPOOL_UNLOCK(cpool, cpool->idata); + CPOOL_UNLOCK(cpool, admin); sigpipe_restore(&pipe_ctx); Curl_hash_destroy(&cpool->dest2bundle); } @@ -255,17 +255,22 @@ void Curl_cpool_destroy(struct cpool *cpool) static struct cpool *cpool_get_instance(struct Curl_easy *data) { - if(data) { - if(CURL_SHARE_KEEP_CONNECT(data->share)) - return &data->share->cpool; - else if(data->multi_easy) - return &data->multi_easy->cpool; - else if(data->multi) - return &data->multi->cpool; - } + /* admin handles do not necessarily find the correct pool */ + DEBUGASSERT(data->mid); + if(CURL_SHARE_KEEP_CONNECT(data->share)) + return &data->share->cpool; + else if(data->multi_easy) + return &data->multi_easy->cpool; + else if(data->multi) + return &data->multi->cpool; return NULL; } +struct cpool *Curl_cpool_get_instance(struct Curl_easy *data) +{ + return cpool_get_instance(data); +} + void Curl_cpool_xfer_init(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); @@ -367,26 +372,90 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool, return oldest_idle; } +static void cpool_conn_close(struct cpool *cpool, + struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + struct Curl_easy *admin; + bool do_lock; + + DEBUGASSERT(cpool); + DEBUGASSERT(data && !data->conn); + if(!cpool) + return; + + /* If this connection is not marked to force-close, leave it open if there + * are other users of it */ + if(CONN_INUSE(conn) && !aborted) { + DEBUGASSERT(0); /* does this ever happen? */ + DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers)); + return; + } + + /* This method may be called while we are under lock, e.g. from a + * user callback in find. */ + admin = Curl_get_admin(data); + do_lock = !CPOOL_IS_LOCKED(cpool); + if(do_lock) + CPOOL_LOCK(cpool, admin); + + if(conn->bits.in_cpool) { + cpool_remove_conn(cpool, conn); + DEBUGASSERT(!conn->bits.in_cpool); + } + + /* treat the connection as aborted in CONNECT_ONLY situations, + * so no graceful shutdown is attempted. */ + if(conn->bits.connect_only) + aborted = TRUE; + + if(data->multi) { + /* Add it to the multi's cpool for shutdown handling */ + infof(data, "%s connection #%" FMT_OFF_T, + aborted ? "closing" : "shutting down", conn->connection_id); + cpool_discard_conn(&data->multi->cpool, data, conn, aborted); + } + else { + /* No multi available, terminate */ + infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id); + Curl_conn_terminate(admin, conn, !aborted); + } + + if(do_lock) + CPOOL_UNLOCK(cpool, admin); +} + +void Curl_conn_close(struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + struct cpool *cpool = cpool_get_instance(data); + cpool_conn_close(cpool, data, conn, aborted); +} + /* Evict an idle connection to make room in the pool. A pool owned by * a share has no multi that could perform a controlled shutdown of the * connection; terminate it right away. Otherwise, hand it to the * transfer's multi for shutdown. Expects the pool to be locked. */ static void cpool_evict_conn(struct cpool *cpool, - struct Curl_easy *data, + struct Curl_easy *admin, struct connectdata *conn) { if(cpool->share) { cpool_remove_conn(cpool, conn); - Curl_cshutdn_terminate(cpool->idata, conn, TRUE); + Curl_conn_terminate(admin, conn, TRUE); } else - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); } int Curl_cpool_check_limits(struct Curl_easy *data, struct connectdata *conn) { struct cpool *cpool = cpool_get_instance(data); + struct cshutdn *cshutdn = Curl_cshutdn_get(data); + struct Curl_easy *admin; struct cpool_bundle *bundle; size_t dest_limit = 0; size_t total_limit = 0; @@ -405,17 +474,18 @@ int Curl_cpool_check_limits(struct Curl_easy *data, if(!dest_limit && !total_limit) return CPOOL_LIMIT_OK; - CPOOL_LOCK(cpool, cpool->idata); + admin = Curl_get_admin(data); + CPOOL_LOCK(cpool, admin); if(dest_limit) { size_t live; bundle = cpool_find_bundle(cpool, conn); live = bundle ? Curl_llist_count(&bundle->conns) : 0; - shutdowns = Curl_cshutdn_dest_count(data, conn->destination); + shutdowns = Curl_cshutdn_dest_count(cshutdn, 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)) + if(!Curl_cshutdn_close_oldest(cshutdn, conn->destination)) break; } else if(!bundle) @@ -429,17 +499,17 @@ int Curl_cpool_check_limits(struct Curl_easy *data, if(!oldest_idle) break; /* disconnect the old conn and continue */ - CURL_TRC_M(data, "Discarding connection #%" FMT_OFF_T + CURL_TRC_M(admin, "Discarding connection #%" FMT_OFF_T " from %zu to reach destination limit of %zu", oldest_idle->connection_id, Curl_llist_count(&bundle->conns), dest_limit); - cpool_evict_conn(cpool, data, oldest_idle); + cpool_evict_conn(cpool, admin, oldest_idle); /* 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(data, conn->destination); + shutdowns = Curl_cshutdn_dest_count(cshutdn, conn->destination); } if((live + shutdowns) >= dest_limit) { res = CPOOL_LIMIT_DEST; @@ -448,11 +518,11 @@ int Curl_cpool_check_limits(struct Curl_easy *data, } if(total_limit) { - shutdowns = Curl_cshutdn_count(data); + shutdowns = Curl_cshutdn_count(cshutdn); 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)) + if(!Curl_cshutdn_close_oldest(cshutdn, NULL)) break; } else { @@ -461,13 +531,13 @@ int Curl_cpool_check_limits(struct Curl_easy *data, if(!oldest_idle) break; /* disconnect the old conn and continue */ - CURL_TRC_M(data, "Discarding connection #%" + CURL_TRC_M(admin, "Discarding connection #%" FMT_OFF_T " from %zu to reach total " "limit of %zu", oldest_idle->connection_id, cpool->num_conn, total_limit); - cpool_evict_conn(cpool, data, oldest_idle); + cpool_evict_conn(cpool, admin, oldest_idle); } - shutdowns = Curl_cshutdn_count(data); + shutdowns = Curl_cshutdn_count(cshutdn); } if((cpool->num_conn + shutdowns) >= total_limit) { res = CPOOL_LIMIT_TOTAL; @@ -476,7 +546,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, } out: - CPOOL_UNLOCK(cpool, cpool->idata); + CPOOL_UNLOCK(cpool, admin); return res; } @@ -529,7 +599,8 @@ out: static bool cpool_foreach(struct Curl_easy *data, struct cpool *cpool, void *param, - int (*func)(struct Curl_easy *data, + int (*func)(struct cpool *cpool, + struct Curl_easy *data, struct connectdata *conn, void *param)) { struct Curl_hash_iterator iter; @@ -553,7 +624,7 @@ static bool cpool_foreach(struct Curl_easy *data, struct connectdata *conn = Curl_node_elem(curr); curr = Curl_node_next(curr); - if(func(data, conn, param) == 1) { + if(func(cpool, data, conn, param) == 1) { return TRUE; } } @@ -573,6 +644,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, unsigned int maxconnects; struct connectdata *oldest_idle = NULL; struct cpool *cpool = cpool_get_instance(data); + struct Curl_easy *admin; bool kept = TRUE; if(!data || !data->multi) @@ -590,8 +662,10 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, if(cpool && maxconnects) { /* may be called form a callback already under lock */ bool do_lock = !CPOOL_IS_LOCKED(cpool); + + admin = Curl_get_admin(data); if(do_lock) - CPOOL_LOCK(cpool, data); + CPOOL_LOCK(cpool, admin); if(cpool->num_conn > maxconnects) { infof(data, "Connection pool is full, closing the oldest of %zu/%u", cpool->num_conn, maxconnects); @@ -599,11 +673,11 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data)); kept = (oldest_idle != conn); if(oldest_idle) { - cpool_evict_conn(cpool, data, oldest_idle); + cpool_evict_conn(cpool, admin, oldest_idle); } } if(do_lock) - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); } return kept; @@ -649,74 +723,23 @@ bool Curl_cpool_find(struct Curl_easy *data, return found; } -void Curl_conn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool aborted) -{ - struct cpool *cpool = cpool_get_instance(data); - bool do_lock; - - DEBUGASSERT(cpool); - DEBUGASSERT(data && !data->conn); - if(!cpool) - return; - - /* If this connection is not marked to force-close, leave it open if there - * are other users of it */ - if(CONN_INUSE(conn) && !aborted) { - DEBUGASSERT(0); /* does this ever happen? */ - DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers)); - return; - } - - /* This method may be called while we are under lock, e.g. from a - * user callback in find. */ - do_lock = !CPOOL_IS_LOCKED(cpool); - if(do_lock) - CPOOL_LOCK(cpool, data); - - if(conn->bits.in_cpool) { - cpool_remove_conn(cpool, conn); - DEBUGASSERT(!conn->bits.in_cpool); - } - - /* treat the connection as aborted in CONNECT_ONLY situations, - * so no graceful shutdown is attempted. */ - if(conn->bits.connect_only) - aborted = TRUE; - - if(data->multi) { - /* Add it to the multi's cpool for shutdown handling */ - infof(data, "%s connection #%" FMT_OFF_T, - aborted ? "closing" : "shutting down", conn->connection_id); - cpool_discard_conn(&data->multi->cpool, data, conn, aborted); - } - else { - /* No multi available, terminate */ - infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id); - Curl_cshutdn_terminate(cpool->idata, conn, !aborted); - } - - if(do_lock) - CPOOL_UNLOCK(cpool, data); -} - struct cpool_reaper_ctx { size_t reaped; struct curltime now; }; -static int cpool_reap_dead_cb(struct Curl_easy *data, +static int cpool_reap_dead_cb(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { struct cpool_reaper_ctx *reaper = param; if(!CONN_INUSE(conn)) { if(conn->bits.no_reuse || conn->bits.close || - !Curl_cpool_conn_seems_healthy(conn, data, &reaper->now)) { + !Curl_cpool_conn_seems_healthy(conn, admin, &reaper->now)) { /* terminate conn and stop the iteration */ reaper->reaped++; - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); return 1; } } @@ -730,46 +753,49 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, * * When called, this transfer has no connection attached. */ -void Curl_cpool_prune_dead(struct Curl_easy *data) +void Curl_cpool_prune_dead(struct cpool *cpool, + struct Curl_easy *data) { - struct cpool *cpool = cpool_get_instance(data); + struct Curl_easy *admin; timediff_t elapsed; if(!cpool) return; - CPOOL_LOCK(cpool, data); - elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), &cpool->last_cleanup); + admin = Curl_get_admin(data); + CPOOL_LOCK(cpool, admin); + elapsed = curlx_ptimediff_ms(Curl_pgrs_now(admin), &cpool->last_cleanup); if(elapsed >= 1000L) { struct cpool_reaper_ctx reaper; memset(&reaper, 0, sizeof(reaper)); - reaper.now = *Curl_pgrs_now(data); - while(cpool_foreach(data, cpool, &reaper, cpool_reap_dead_cb)) + reaper.now = *Curl_pgrs_now(admin); + while(cpool_foreach(admin, cpool, &reaper, cpool_reap_dead_cb)) ; - cpool->last_cleanup = *Curl_pgrs_now(data); + cpool->last_cleanup = *Curl_pgrs_now(admin); } - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); } -static int conn_upkeep(struct Curl_easy *data, +static int conn_upkeep(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { (void)param; - if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) >= - data->set.upkeep_interval_ms) { + if(curlx_ptimediff_ms(Curl_pgrs_now(admin), &conn->keepalive) >= + admin->set.upkeep_interval_ms) { CURLcode result; /* briefly attach for action */ - Curl_attach_connection(data, conn); - result = Curl_conn_keep_alive(data, conn); - conn->keepalive = *Curl_pgrs_now(data); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_keep_alive(admin, conn); + conn->keepalive = *Curl_pgrs_now(admin); + Curl_detach_connection(admin); if(result && !CONN_INUSE(conn)) { - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); return 1; } } @@ -779,14 +805,15 @@ static int conn_upkeep(struct Curl_easy *data, CURLcode Curl_cpool_upkeep(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); + struct Curl_easy *admin = Curl_get_admin(data); if(!cpool) return CURLE_OK; - CPOOL_LOCK(cpool, data); - while(cpool_foreach(data, cpool, NULL, conn_upkeep)) + CPOOL_LOCK(cpool, admin); + while(cpool_foreach(admin, cpool, NULL, conn_upkeep)) ; - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); return CURLE_OK; } @@ -795,10 +822,12 @@ struct cpool_find_ctx { struct connectdata *conn; }; -static int cpool_find_conn(struct Curl_easy *data, +static int cpool_find_conn(struct cpool *cpool, + struct Curl_easy *data, struct connectdata *conn, void *param) { struct cpool_find_ctx *fctx = param; + (void)cpool; (void)data; if(conn->connection_id == fctx->id) { fctx->conn = conn; @@ -837,36 +866,37 @@ void Curl_cpool_do_locked(struct Curl_easy *data, cb(conn, data, cbdata); } -static int cpool_mark_stale(struct Curl_easy *data, +static int cpool_mark_stale(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { - (void)data; + (void)cpool; + (void)admin; (void)param; conn->bits.no_reuse = TRUE; return 0; } -static int cpool_reap_no_reuse(struct Curl_easy *data, +static int cpool_reap_no_reuse(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { (void)param; if(!CONN_INUSE(conn) && conn->bits.no_reuse) { - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); return 1; } return 0; /* continue iteration */ } -void Curl_cpool_nw_changed(struct Curl_easy *data) +void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin) { - struct cpool *cpool = cpool_get_instance(data); - - if(cpool) { - CPOOL_LOCK(cpool, data); - cpool_foreach(data, cpool, NULL, cpool_mark_stale); - while(cpool_foreach(data, cpool, NULL, cpool_reap_no_reuse)) + if(cpool && admin) { + CPOOL_LOCK(cpool, admin); + cpool_foreach(admin, cpool, NULL, cpool_mark_stale); + while(cpool_foreach(admin, cpool, NULL, cpool_reap_no_reuse)) ; - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); } } @@ -907,24 +937,27 @@ bool Curl_cpool_conn_seems_healthy(struct connectdata *conn, struct Curl_easy *data, const struct curltime *pnow) { + struct Curl_easy *admin; bool healthy = TRUE; DEBUGASSERT(!data->conn); if(!CONN_INUSE(conn) && cpool_conn_maxage(data, conn, pnow)) /* too old? */ return FALSE; - else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) + if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) return TRUE; - else if(conn->scheme->run->connection_is_dead) { - Curl_attach_connection(data, conn); - healthy = !conn->scheme->run->connection_is_dead(data, conn); - Curl_detach_connection(data); + + admin = Curl_get_admin(data); + if(conn->scheme->run->connection_is_dead) { + Curl_attach_connection(admin, conn, FALSE); + healthy = !conn->scheme->run->connection_is_dead(admin, conn); + Curl_detach_connection(admin); } else { bool input_pending = FALSE; - Curl_attach_connection(data, conn); - healthy = Curl_conn_is_alive(data, conn, &input_pending); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + healthy = Curl_conn_is_alive(admin, conn, &input_pending); + Curl_detach_connection(admin); if(healthy && input_pending && !CONN_INUSE(conn) && !Curl_conn_is_multiplex(conn, FIRSTSOCKET)) { /* Non-multiplexed connections without attached transfers should diff --git a/lib/conncache.h b/lib/conncache.h index 4924c038b3..f31b5fcf80 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -34,7 +34,7 @@ struct Curl_multi; struct Curl_share; /** - * Terminate the connection, e.g. close and destroy. + * Close and destroy the connection. * If the connection is in a cpool, remove it. * If a `cshutdn` is available (e.g. data has a multi handle), * pass the connection to that for controlled shutdown. @@ -42,9 +42,9 @@ struct Curl_share; * Takes ownership of `conn`. * `data` should not be attached to a connection. */ -void Curl_conn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool aborted); +void Curl_conn_close(struct Curl_easy *data, + struct connectdata *conn, + bool aborted); struct cpool { /* the pooled connections, bundled per destination */ @@ -53,22 +53,24 @@ struct cpool { curl_off_t next_connection_id; curl_off_t next_easy_id; struct curltime last_cleanup; - struct Curl_easy *idata; /* internal handle for maintenance */ struct Curl_share *share; /* != NULL if pool belongs to share */ BIT(locked); BIT(initialized); }; +/* Get connection pool instance for data or NULL if none exists */ +struct cpool *Curl_cpool_get_instance(struct Curl_easy *data); + /* Init the pool, pass multi only if pool is owned by it. * Cannot fail. */ void Curl_cpool_init(struct cpool *cpool, - struct Curl_easy *idata, struct Curl_share *share, size_t size); /* Destroy all connections and free all members */ -void Curl_cpool_destroy(struct cpool *cpool); +void Curl_cpool_destroy(struct cpool *cpool, + struct Curl_easy *admin); /* Init the transfer to be used within its connection pool. * Assigns `data->id`. */ @@ -126,13 +128,12 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, struct connectdata *conn); /** - * This function scans the data's connection pool for half-open/dead + * Scans the connection pool for half-open/dead * connections, closes and removes them. * The cleanup is done at most once per second. - * - * When called, this transfer has no connection attached. */ -void Curl_cpool_prune_dead(struct Curl_easy *data); +void Curl_cpool_prune_dead(struct cpool *cpool, + struct Curl_easy *data); /** * Perform upkeep actions on connections in the transfer's pool. @@ -154,7 +155,7 @@ void Curl_cpool_do_locked(struct Curl_easy *data, Curl_cpool_conn_do_cb *cb, void *cbdata); /* Close all unused connections, prevent reuse of existing ones. */ -void Curl_cpool_nw_changed(struct Curl_easy *data); +void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin); /* Return TRUE iff the given connection is considered healthy, e.g. * usable for more transfers. */ diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 30cbaa552a..6ad0f88013 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -38,6 +38,13 @@ #include "curlx/strparse.h" +struct cshutdn *Curl_cshutdn_get(struct Curl_easy *data) +{ + if(data && data->multi) + return &data->multi->cshutdn; + return NULL; +} + static void cshutdn_run_conn_handler(struct Curl_easy *data, struct connectdata *conn) { @@ -107,22 +114,22 @@ static void cshutdn_run_once(struct Curl_easy *data, conn->bits.shutdown_filters = TRUE; } -void Curl_cshutdn_run_once(struct Curl_easy *data, +void Curl_conn_shutdown_once(struct Curl_easy *admin, struct connectdata *conn, bool *done) { - DEBUGASSERT(!data->conn); - Curl_attach_connection(data, conn); - cshutdn_run_once(data, conn, done); - CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done); - Curl_detach_connection(data); + DEBUGASSERT(!admin->conn); + DEBUGASSERT(!admin->mid); + Curl_attach_connection(admin, conn, FALSE); + cshutdn_run_once(admin, conn, done); + CURL_TRC_M(admin, "[SHUTDOWN] shutdown, done=%d", *done); + Curl_detach_connection(admin); } -void Curl_cshutdn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool do_shutdown) +void Curl_conn_terminate(struct Curl_easy *admin, + struct connectdata *conn, + bool do_shutdown) { - struct Curl_easy *admin = data; bool done; /* there must be a connection to close */ @@ -130,16 +137,10 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, /* it must be removed from the connection pool */ DEBUGASSERT(!conn->bits.in_cpool); /* the transfer must be detached from the connection */ - DEBUGASSERT(data && !data->conn); + DEBUGASSERT(admin && !admin->conn); + DEBUGASSERT(!admin->mid); - /* If we can obtain an internal admin handle, use that to attach - * and terminate the connection. Some protocol will try to mess with - * `data` during shutdown and we do not want that with a `data` from - * the application. */ - if(data->multi && data->multi->admin) - admin = data->multi->admin; - - Curl_attach_connection(admin, conn); + Curl_attach_connection(admin, conn, FALSE); cshutdn_run_conn_handler(admin, conn); if(do_shutdown) { @@ -154,18 +155,17 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET); Curl_detach_connection(admin); - if(data->multi) - Curl_multi_ev_conn_done(data->multi, data, conn); + if(admin->multi) + Curl_multi_ev_conn_done(admin->multi, admin, conn); Curl_conn_free(admin, conn); - if(data->multi) { - CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged"); - Curl_multi_connchanged(data->multi); + if(admin->multi) { + CURL_TRC_M(admin, "[SHUTDOWN] trigger multi connchanged"); + Curl_multi_connchanged(admin->multi); } } static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn, - struct Curl_easy *data, const char *destination) { struct Curl_llist_node *e; @@ -184,20 +184,19 @@ static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn, conn = Curl_node_elem(e); Curl_node_remove(e); sigpipe_init(&sigpipe_ctx); - sigpipe_apply(data, &sigpipe_ctx); - Curl_cshutdn_terminate(data, conn, FALSE); + sigpipe_apply(cshutdn->multi->admin, &sigpipe_ctx); + Curl_conn_terminate(cshutdn->multi->admin, conn, FALSE); sigpipe_restore(&sigpipe_ctx); return TRUE; } return FALSE; } -bool Curl_cshutdn_close_oldest(struct Curl_easy *data, +bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, const char *destination) { - if(data && data->multi) { - struct cshutdn *csd = &data->multi->cshutdn; - return cshutdn_destroy_oldest(csd, data, destination); + if(cshutdn) { + return cshutdn_destroy_oldest(cshutdn, destination); } return FALSE; } @@ -205,7 +204,6 @@ bool Curl_cshutdn_close_oldest(struct Curl_easy *data, #define NUM_POLLS_ON_STACK 10 static CURLcode cshutdn_wait(struct cshutdn *cshutdn, - struct Curl_easy *data, int timeout_ms) { struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; @@ -214,7 +212,7 @@ static CURLcode cshutdn_wait(struct cshutdn *cshutdn, Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); - result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds); + result = Curl_cshutdn_add_pollfds(cshutdn, &cpfds); if(result) goto out; @@ -226,11 +224,11 @@ out: } static void cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx) { struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list); struct Curl_llist_node *enext; + struct Curl_easy *admin = cshutdn->multi->admin; struct connectdata *conn; timediff_t next_expire_ms = 0, ms; bool done; @@ -238,21 +236,21 @@ static void cshutdn_perform(struct cshutdn *cshutdn, if(!e) return; - CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections", + CURL_TRC_M(admin, "[SHUTDOWN] perform on %zu connections", Curl_llist_count(&cshutdn->list)); - sigpipe_apply(data, sigpipe_ctx); + sigpipe_apply(admin, sigpipe_ctx); while(e) { enext = Curl_node_next(e); conn = Curl_node_elem(e); - Curl_cshutdn_run_once(data, conn, &done); + Curl_conn_shutdown_once(admin, conn, &done); if(done) { Curl_node_remove(e); - Curl_cshutdn_terminate(data, conn, FALSE); + Curl_conn_terminate(admin, conn, FALSE); } else { /* idata has one timer list, but maybe more than one connection. * Set EXPIRE_SHUTDOWN to the smallest time left for all. */ - ms = Curl_conn_shutdown_timeleft(data, conn); + ms = Curl_conn_shutdown_timeleft(admin, conn); if(ms && (!next_expire_ms || (ms < next_expire_ms))) next_expire_ms = ms; } @@ -260,45 +258,42 @@ static void cshutdn_perform(struct cshutdn *cshutdn, } if(next_expire_ms) - Curl_expire_ex(data, next_expire_ms, EXPIRE_SHUTDOWN); + Curl_expire_ex(admin, next_expire_ms, EXPIRE_SHUTDOWN); } static void cshutdn_terminate_all(struct cshutdn *cshutdn, - struct Curl_easy *data, int timeout_ms) { - struct curltime started = *Curl_pgrs_now(data); + struct Curl_easy *admin = cshutdn->multi->admin; + struct curltime started = *Curl_pgrs_now(admin); struct Curl_llist_node *e; struct Curl_sigpipe_ctx sigpipe_ctx; - DEBUGASSERT(cshutdn); - DEBUGASSERT(data); - - CURL_TRC_M(data, "[SHUTDOWN] shutdown all"); + CURL_TRC_M(admin, "[SHUTDOWN] shutdown all"); sigpipe_init(&sigpipe_ctx); while(Curl_llist_head(&cshutdn->list)) { timediff_t spent_ms; int remain_ms; - cshutdn_perform(cshutdn, data, &sigpipe_ctx); + cshutdn_perform(cshutdn, &sigpipe_ctx); if(!Curl_llist_head(&cshutdn->list)) { - CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly"); + CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished cleanly"); break; } /* wait for activity, timeout or "nothing" */ - spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &started); + spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(admin), &started); if(spent_ms >= (timediff_t)timeout_ms) { - CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s", + CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, %s", (timeout_ms > 0) ? "timeout" : "best effort done"); break; } remain_ms = timeout_ms - (int)spent_ms; - if(cshutdn_wait(cshutdn, data, remain_ms)) { - CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted"); + if(cshutdn_wait(cshutdn, remain_ms)) { + CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, aborted"); break; } } @@ -308,7 +303,7 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn, while(e) { struct connectdata *conn = Curl_node_elem(e); Curl_node_remove(e); - Curl_cshutdn_terminate(data, conn, FALSE); + Curl_conn_terminate(admin, conn, FALSE); e = Curl_llist_head(&cshutdn->list); } DEBUGASSERT(!Curl_llist_count(&cshutdn->list)); @@ -327,12 +322,13 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn, } void Curl_cshutdn_destroy(struct cshutdn *cshutdn, - struct Curl_easy *data) + struct Curl_easy *admin) { - if(cshutdn->initialized && data) { + if(cshutdn->initialized && admin) { int timeout_ms = 0; /* for testing, run graceful shutdown */ #ifdef DEBUGBUILD + DEBUGASSERT(!admin->mid); { const char *p = getenv("CURL_GRACEFUL_SHUTDOWN"); if(p) { @@ -343,29 +339,28 @@ void Curl_cshutdn_destroy(struct cshutdn *cshutdn, } #endif - CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms", + CURL_TRC_M(admin, "[SHUTDOWN] destroy, %zu connections, timeout=%dms", Curl_llist_count(&cshutdn->list), timeout_ms); - cshutdn_terminate_all(cshutdn, data, timeout_ms); + cshutdn_terminate_all(cshutdn, timeout_ms); } cshutdn->multi = NULL; + cshutdn->initialized = FALSE; } -size_t Curl_cshutdn_count(struct Curl_easy *data) +size_t Curl_cshutdn_count(struct cshutdn *cshutdn) { - if(data && data->multi) { - struct cshutdn *csd = &data->multi->cshutdn; - return Curl_llist_count(&csd->list); + if(cshutdn) { + return Curl_llist_count(&cshutdn->list); } return 0; } -size_t Curl_cshutdn_dest_count(struct Curl_easy *data, +size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn, const char *destination) { - if(data && data->multi) { - struct cshutdn *csd = &data->multi->cshutdn; + if(cshutdn) { size_t n = 0; - struct Curl_llist_node *e = Curl_llist_head(&csd->list); + struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list); while(e) { struct connectdata *conn = Curl_node_elem(e); if(!strcmp(destination, conn->destination)) @@ -378,17 +373,17 @@ size_t Curl_cshutdn_dest_count(struct Curl_easy *data, } static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn, - struct Curl_easy *data, struct connectdata *conn) { + struct Curl_easy *admin = cshutdn->multi->admin; CURLMcode mresult; DEBUGASSERT(cshutdn); DEBUGASSERT(cshutdn->multi->socket_cb); - Curl_attach_connection(data, conn); - mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + mresult = Curl_multi_ev_assess_conn(cshutdn->multi, admin, conn); + Curl_detach_connection(admin); return mresult; } @@ -396,47 +391,46 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, struct connectdata *conn, size_t conns_in_pool) { - struct Curl_easy *data = cshutdn->multi->admin; + struct Curl_easy *admin = cshutdn->multi->admin; size_t max_total = cshutdn->multi->max_total_connections; /* Add the connection to our shutdown list for non-blocking shutdown * during multi processing. */ if(max_total > 0 && (max_total <= (conns_in_pool + Curl_llist_count(&cshutdn->list)))) { - CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection " + CURL_TRC_M(admin, "[SHUTDOWN] discarding oldest shutdown connection " "due to connection limit of %zu", max_total); - cshutdn_destroy_oldest(cshutdn, data, NULL); + cshutdn_destroy_oldest(cshutdn, NULL); } if(cshutdn->multi->socket_cb) { - if(cshutdn_update_ev(cshutdn, data, conn)) { - CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%" + if(cshutdn_update_ev(cshutdn, conn)) { + CURL_TRC_M(admin, "[SHUTDOWN] update events failed, discarding #%" FMT_OFF_T, conn->connection_id); - Curl_cshutdn_terminate(data, conn, FALSE); + Curl_conn_terminate(admin, conn, FALSE); return; } } Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node); - CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T + CURL_TRC_M(admin, "[SHUTDOWN] added #%" FMT_OFF_T " to shutdowns, now %zu conns in shutdown", conn->connection_id, Curl_llist_count(&cshutdn->list)); } void Curl_cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx) { - cshutdn_perform(cshutdn, data, sigpipe_ctx); + cshutdn_perform(cshutdn, sigpipe_ctx); } /* return fd_set info about the shutdown connections */ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, - struct Curl_easy *data, fd_set *read_fd_set, fd_set *write_fd_set, int *maxfd) { if(Curl_llist_head(&cshutdn->list)) { + struct Curl_easy *admin = cshutdn->multi->admin; struct Curl_llist_node *e; struct easy_pollset ps; @@ -447,9 +441,9 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, CURLcode result; Curl_pollset_reset(&ps); - Curl_attach_connection(data, conn); - result = Curl_conn_adjust_pollset(data, conn, &ps); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_adjust_pollset(admin, conn, &ps); + Curl_detach_connection(admin); if(result) continue; @@ -473,13 +467,13 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, /* return information about the shutdown connections */ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_waitfds *cwfds) { unsigned int need = 0; if(Curl_llist_head(&cshutdn->list)) { struct Curl_llist_node *e; + struct Curl_easy *admin = cshutdn->multi->admin; struct easy_pollset ps; struct connectdata *conn; CURLcode result; @@ -488,9 +482,9 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); Curl_pollset_reset(&ps); - Curl_attach_connection(data, conn); - result = Curl_conn_adjust_pollset(data, conn, &ps); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_adjust_pollset(admin, conn, &ps); + Curl_detach_connection(admin); if(!result) need += Curl_waitfds_add_ps(cwfds, &ps); @@ -501,7 +495,6 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, } CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct curl_pollfds *cpfds) { CURLcode result = CURLE_OK; @@ -509,15 +502,16 @@ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, if(Curl_llist_head(&cshutdn->list)) { struct Curl_llist_node *e; struct easy_pollset ps; + struct Curl_easy *admin = cshutdn->multi->admin; struct connectdata *conn; Curl_pollset_init(&ps); for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); Curl_pollset_reset(&ps); - Curl_attach_connection(data, conn); - result = Curl_conn_adjust_pollset(data, conn, &ps); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_adjust_pollset(admin, conn, &ps); + Curl_detach_connection(admin); if(!result) result = Curl_pollfds_add_ps(cpfds, &ps); diff --git a/lib/cshutdn.h b/lib/cshutdn.h index 8479524993..a0338f6488 100644 --- a/lib/cshutdn.h +++ b/lib/cshutdn.h @@ -33,20 +33,20 @@ struct Curl_share; struct Curl_sigpipe_ctx; /* Run the shutdown of the connection once. - * Shortly attach/detach `data` to `conn` while doing so. + * Shortly attach/detach the admin handle to `conn` while doing so. * `done` will be set TRUE if any error was encountered or if * the connection was shut down completely. */ -void Curl_cshutdn_run_once(struct Curl_easy *data, - struct connectdata *conn, - bool *done); +void Curl_conn_shutdown_once(struct Curl_easy *admin, + struct connectdata *conn, + bool *done); /* Terminates the connection, e.g. closes and destroys it. * If `do_shutdown` is TRUE, the shutdown will be run once before * terminating it. * Takes ownership of `conn`. */ -void Curl_cshutdn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool do_shutdown); +void Curl_conn_terminate(struct Curl_easy *admin, + struct connectdata *conn, + bool do_shutdown); /* A `cshutdown` is always owned by a multi handle to maintain * the connections to be shut down. It registers timers and @@ -57,25 +57,28 @@ struct cshutdn { BIT(initialized); }; +/* Get the cshutdn instance relevant for `data` or NULL if there is none */ +struct cshutdn *Curl_cshutdn_get(struct Curl_easy *data); + /* Init as part of the given multi handle. */ int Curl_cshutdn_init(struct cshutdn *cshutdn, struct Curl_multi *multi); /* Terminate all remaining connections and free resources. */ void Curl_cshutdn_destroy(struct cshutdn *cshutdn, - struct Curl_easy *data); + struct Curl_easy *admin); /* Number of connections being shut down. */ -size_t Curl_cshutdn_count(struct Curl_easy *data); +size_t Curl_cshutdn_count(struct cshutdn *cshutdn); /* Number of connections to the destination being shut down. */ -size_t Curl_cshutdn_dest_count(struct Curl_easy *data, +size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn, const char *destination); /* Close the oldest connection in shutdown to destination or, * when destination is NULL for any destination. * Return TRUE if a connection has been closed. */ -bool Curl_cshutdn_close_oldest(struct Curl_easy *data, +bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, const char *destination); /* Add a connection to have it shut down. Terminate the oldest @@ -86,21 +89,17 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, /* Add sockets and POLLIN/OUT flags for connections being shut down. */ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct curl_pollfds *cpfds); unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_waitfds *cwfds); void Curl_cshutdn_setfds(struct cshutdn *cshutdn, - struct Curl_easy *data, fd_set *read_fd_set, fd_set *write_fd_set, int *maxfd); /* Run maintenance on all connections. */ void Curl_cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx); #endif /* HEADER_CURL_CSHUTDN_H */ diff --git a/lib/curl_share.c b/lib/curl_share.c index b7c331adf7..bce6419e8f 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -38,7 +38,7 @@ static void share_destroy(struct Curl_share *share) return; if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { - Curl_cpool_destroy(&share->cpool); + Curl_cpool_destroy(&share->cpool, share->admin); } Curl_dnscache_destroy(&share->dnscache); @@ -264,7 +264,7 @@ CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_CONNECT: /* It is safe to set this option several times on a share. */ if(!share->cpool.initialized) { - Curl_cpool_init(&share->cpool, share->admin, share, 103); + Curl_cpool_init(&share->cpool, share, 103); } break; @@ -371,11 +371,11 @@ CURLSHcode curl_share_cleanup(CURLSH *sh) return CURLSHE_OK; } -CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, - curl_lock_access accesstype) +CURLSHcode Curl_share_lock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type, + curl_lock_access accesstype) { - struct Curl_share *share = data->share; - if(!share) return CURLSHE_INVALID; @@ -388,10 +388,16 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, return CURLSHE_OK; } -CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) +CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, + curl_lock_access accesstype) { - struct Curl_share *share = data->share; + return Curl_share_lock_share(data->share, data, type, accesstype); +} +CURLSHcode Curl_share_unlock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type) +{ if(!share) return CURLSHE_INVALID; @@ -403,6 +409,11 @@ CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) return CURLSHE_OK; } +CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) +{ + return Curl_share_unlock_share(data->share, data, type); +} + CURLcode Curl_share_easy_unlink(struct Curl_easy *data) { struct Curl_share *share = data->share; diff --git a/lib/curl_share.h b/lib/curl_share.h index 1d49a8d1dd..5c687989e0 100644 --- a/lib/curl_share.h +++ b/lib/curl_share.h @@ -80,6 +80,14 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, curl_lock_access accesstype); CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type); +CURLSHcode Curl_share_lock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type, + curl_lock_access accesstype); +CURLSHcode Curl_share_unlock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type); + /* convenience macro to check if this handle is using a shared SSL spool */ #define CURL_SHARE_ssl_scache(data) \ ((data)->share && \ diff --git a/lib/curl_trc.c b/lib/curl_trc.c index b52193270e..ccfb5c7994 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -92,7 +92,7 @@ static struct curl_trc_feat Curl_trc_feat_ids = { static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen) { - curl_off_t cid = data->state.recent_conn_id; + curl_off_t cid = data->state.lastconnect_id; if(data->id >= 0) { if(cid >= 0) return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid); diff --git a/lib/easy.c b/lib/easy.c index aa2cad22c9..10cb72f113 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -760,7 +760,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) if(data->conn) { struct connectdata *conn = data->conn; Curl_detach_connection(data); - Curl_conn_terminate(data, conn, TRUE); + Curl_conn_close(data, conn, TRUE); DEBUGASSERT(!data->conn); } @@ -996,7 +996,6 @@ CURL *curl_easy_duphandle(CURL *curl) /* the connection pool is setup on demand */ outcurl->state.lastconnect_id = -1; - outcurl->state.recent_conn_id = -1; outcurl->id = -1; outcurl->mid = UINT32_MAX; outcurl->master_mid = UINT32_MAX; @@ -1130,7 +1129,7 @@ void curl_easy_reset(CURL *curl) data->progress.hide = TRUE; data->state.current_speed = -1; /* init to negative == impossible */ - data->state.recent_conn_id = -1; /* clear remembered connection id */ + data->state.lastconnect_id = -1; /* clear remembered connection id */ /* zero out authentication data: */ memset(&data->state.authhost, 0, sizeof(struct auth)); @@ -1259,7 +1258,7 @@ CURLcode Curl_easy_recv(struct Curl_easy *data, if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ - Curl_attach_connection(data, c); + Curl_attach_connection(data, c, TRUE); *n = 0; return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n); @@ -1290,7 +1289,7 @@ CURLcode Curl_connect_only_attach(struct Curl_easy *data) if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ - Curl_attach_connection(data, c); + Curl_attach_connection(data, c, TRUE); return CURLE_OK; } @@ -1316,7 +1315,7 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ - Curl_attach_connection(data, c); + Curl_attach_connection(data, c, TRUE); sigpipe_ignore(data, &sigpipe_ctx); result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n); diff --git a/lib/getinfo.c b/lib/getinfo.c index d9873531d5..30ee405e99 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -463,8 +463,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, *param_offt = data->id; break; case CURLINFO_CONN_ID: - *param_offt = data->conn ? - data->conn->connection_id : data->state.recent_conn_id; + *param_offt = data->state.lastconnect_id; break; case CURLINFO_EARLYDATA_SENT_T: *param_offt = data->progress.earlydata_sent; diff --git a/lib/multi.c b/lib/multi.c index b990442a92..cfa64410f1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -285,7 +285,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, if(Curl_cshutdn_init(&multi->cshutdn, multi)) goto error; - Curl_cpool_init(&multi->cpool, multi->admin, NULL, chashsize); + Curl_cpool_init(&multi->cpool, NULL, chashsize); #ifdef USE_SSL if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache)) @@ -334,7 +334,7 @@ error: Curl_multi_ev_cleanup(multi); Curl_hash_destroy(&multi->proto_hash); Curl_dnscache_destroy(&multi->dnscache); - Curl_cpool_destroy(&multi->cpool); + Curl_cpool_destroy(&multi->cpool, multi->admin); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); #ifdef USE_SSL Curl_ssl_scache_destroy(multi->ssl_scache); @@ -654,20 +654,19 @@ static void multi_done_locked(struct connectdata *conn, data->set.reuse_forbid, conn->bits.close, mdctx->premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET)); connclose(conn); - Curl_conn_terminate(data, conn, (bool)mdctx->premature); + Curl_conn_close(data, conn, (bool)mdctx->premature); } else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown" " by server, not reusing", conn->connection_id, conn->destination); connclose(conn); - Curl_conn_terminate(data, conn, (bool)mdctx->premature); + Curl_conn_close(data, conn, (bool)mdctx->premature); } else { /* the connection is no longer in use by any transfer */ if(Curl_cpool_conn_now_idle(data, conn)) { /* connection kept in the cpool */ - data->state.lastconnect_id = conn->connection_id; infof(data, "Connection #%" FMT_OFF_T " to host %s left intact", conn->connection_id, conn->destination); } @@ -845,7 +844,7 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, struct connectdata *conn; (void)Curl_getconnectinfo(data, &conn); if(conn) - Curl_conn_terminate(data, conn, TRUE); + Curl_conn_close(data, conn, TRUE); } } @@ -943,17 +942,23 @@ void Curl_detach_connection(struct Curl_easy *data) /* * Curl_attach_connection() attaches this transfer to this connection. * - * This is the only function that should assign data->conn + * This is the only function that should assign data->conn. + * `matched == TRUE` means the transfer's properties match this + * connection and it is not a temporary attach for maintenance. */ void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn) + struct connectdata *conn, + bool matched) { DEBUGASSERT(data); DEBUGASSERT(!data->conn); DEBUGASSERT(conn); DEBUGASSERT(conn->attached_xfers < UINT32_MAX); data->conn = conn; - data->state.recent_conn_id = conn->connection_id; + if(matched) + data->state.lastconnect_id = conn->connection_id; + else + DEBUGASSERT(!data->mid); /* admin handle */ conn->attached_xfers++; /* all attached transfers must be from the same multi */ if(!conn->attached_multi) @@ -1288,8 +1293,8 @@ CURLMcode curl_multi_fdset(CURLM *m, } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, - read_fd_set, write_fd_set, &this_max_fd); + Curl_cshutdn_setfds(&multi->cshutdn, read_fd_set, write_fd_set, + &this_max_fd); *max_fd = this_max_fd; Curl_pollset_cleanup(&ps); @@ -1337,7 +1342,7 @@ CURLMcode curl_multi_waitfds(CURLM *m, } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); + need += Curl_cshutdn_add_waitfds(&multi->cshutdn, &cwfds); if(need != cwfds.n && ufds) mresult = CURLM_OUT_OF_MEMORY; @@ -1575,7 +1580,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) { + if(Curl_cshutdn_add_pollfds(&multi->cshutdn, &cpfds)) { mresult = CURLM_OUT_OF_MEMORY; goto out; } @@ -1774,7 +1779,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, /* take this handle to the perform state right away */ multistate(data, MSTATE_PERFORMING); - Curl_attach_connection(data, conn); + Curl_attach_connection(data, conn, TRUE); CURL_REQ_SET_RECV(data); } return mresult; @@ -2391,7 +2396,7 @@ static CURLcode is_finished(struct Curl_multi *multi, We do not have to do this in every case block above where a failure is detected */ Curl_detach_connection(data); - Curl_conn_terminate(data, conn, dead_connection); + Curl_conn_close(data, conn, dead_connection); } } else if(data->mstate == MSTATE_CONNECT) { @@ -2711,7 +2716,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx) { - CURLMcode mresult; + CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; if(multi->dead) { @@ -2738,8 +2743,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_process(multi); #endif - Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx); - return CURLM_OK; + Curl_cshutdn_perform(&multi->cshutdn, sigpipe_ctx); + goto out; } sigpipe_apply(data, sigpipe_ctx); @@ -2758,8 +2763,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->mstate < MSTATE_COMPLETED) { /* Make sure we set the connection's current owner */ DEBUGASSERT(data->conn); - if(!data->conn) - return CURLM_INTERNAL_ERROR; + if(!data->conn) { + mresult = CURLM_INTERNAL_ERROR; + goto out; + } } /* Wait for the connect state as only then is the start time stored, but @@ -2841,7 +2848,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; default: - return CURLM_INTERNAL_ERROR; + mresult = CURLM_INTERNAL_ERROR; + goto out; } if(data->mstate >= MSTATE_CONNECT && @@ -2865,11 +2873,13 @@ statemachine_end: if(MSTATE_COMPLETED == data->mstate) { handle_completed(multi, data, result); - return CURLM_OK; + mresult = CURLM_OK; + goto out; } } while((mresult == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); +out: data->result = result; return mresult; } @@ -3014,7 +3024,7 @@ CURLMcode curl_multi_cleanup(CURLM *m) #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit); #endif - Curl_cpool_destroy(&multi->cpool); + Curl_cpool_destroy(&multi->cpool, multi->admin); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); if(multi->admin) { CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done"); @@ -3393,7 +3403,7 @@ CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) Curl_dnscache_clear(multi->admin); } if(val & CURLMNWC_CLEAR_CONNS) { - Curl_cpool_nw_changed(multi->admin); + Curl_cpool_nw_changed(&multi->cpool, multi->admin); } break; } diff --git a/lib/multiif.h b/lib/multiif.h index 45a2e7d315..44ac9bb378 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -34,7 +34,8 @@ void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn); + struct connectdata *conn, + bool matched); void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); CURLcode Curl_preconnect(struct Curl_easy *data); diff --git a/lib/url.c b/lib/url.c index 5234a53443..f2f74d8b2e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -471,7 +471,6 @@ CURLcode Curl_open(struct Curl_easy **curl) data->magic = CURLEASY_MAGIC_NUMBER; /* most recent connection is not yet defined */ data->state.lastconnect_id = -1; - data->state.recent_conn_id = -1; /* and not assigned an id yet */ data->id = -1; data->mid = UINT32_MAX; @@ -914,7 +913,7 @@ static bool url_allow_sspi_empty_creds(struct Curl_creds *conn_creds, * To avoid TOCTOU attacks, do not reuse on empty credentials * UNLESS this connection is the one used by this transfer before. */ if(!Curl_creds_has_user(conn_creds) && - (data->state.recent_conn_id != conn->connection_id)) + (data->state.lastconnect_id != conn->connection_id)) return FALSE; #else (void)conn_creds; @@ -1113,7 +1112,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) !Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) { infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating", conn->connection_id); - Curl_conn_terminate(m->data, conn, FALSE); + Curl_conn_close(m->data, conn, FALSE); return FALSE; } @@ -1128,7 +1127,7 @@ static bool url_match_result(void *userdata) if(match->found) { /* Attach it now while still under lock, so the connection does * no longer appear idle and can be reaped. */ - Curl_attach_connection(match->data, match->found); + Curl_attach_connection(match->data, match->found, TRUE); return TRUE; } else if(match->seen_single_use_conn && !match->seen_multiplex_conn) { @@ -1159,11 +1158,11 @@ static bool url_attach_existing(struct Curl_easy *data, struct connectdata *needle, bool *waitpipe) { + struct cpool *cpool = Curl_cpool_get_instance(data); struct url_conn_match match; bool success; DEBUGASSERT(!data->conn); - Curl_cpool_prune_dead(data); memset(&match, 0, sizeof(match)); match.data = data; @@ -1171,6 +1170,8 @@ static bool url_attach_existing(struct Curl_easy *data, match.now = *Curl_pgrs_now(data); match.may_multiplex = xfer_may_multiplex(data, needle); + Curl_cpool_prune_dead(cpool, data); + #ifdef USE_NTLM match.want_ntlm_http = (data->state.authhost.want & CURLAUTH_NTLM) && @@ -2303,9 +2304,9 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) goto out; /* Setup a "faked" transfer that will do nothing */ - Curl_attach_connection(data, needle); + result = Curl_cpool_add(data, needle); + Curl_attach_connection(data, needle, TRUE); needle = NULL; - result = Curl_cpool_add(data, data->conn); if(!result) { /* Setup whatever necessary for a resumed transfer */ result = setup_range(data); @@ -2411,7 +2412,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) /* Add needle to conn pool, which assigns the connection id. * Attach regardless of result, for correct handling. */ result = Curl_cpool_add(data, needle); - Curl_attach_connection(data, needle); + Curl_attach_connection(data, needle, TRUE); needle = NULL; if(result) goto out; @@ -2523,7 +2524,7 @@ out: /* We are not allowed to return failure with memory left allocated in the connectdata struct, free those here */ Curl_detach_connection(data); - Curl_conn_terminate(data, conn, TRUE); + Curl_conn_close(data, conn, TRUE); } return result; @@ -2595,6 +2596,31 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key) return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1); } +struct Curl_easy *Curl_get_admin(struct Curl_easy *data) +{ + struct Curl_easy *admin; + + if(!data->mid) /* already an admin handle */ + admin = data; + else if(data->multi) + admin = data->multi->admin; + else if(data->multi_easy) + admin = data->multi_easy->admin; + else { + DEBUGASSERT(0); /* we do not want this. does it happen? */ + admin = data; + } + if(admin != data) { + admin->set.conn_max_idle_ms = data->set.conn_max_idle_ms; + admin->set.conn_max_age_ms = data->set.conn_max_age_ms; + admin->set.upkeep_interval_ms = data->set.upkeep_interval_ms; + admin->set.timeout = data->set.timeout; + admin->set.server_response_timeout = data->set.server_response_timeout; + admin->set.no_signal = data->set.no_signal; + } + return admin; +} + CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2) { if(r1 && (r1 != CURLE_AGAIN)) diff --git a/lib/url.h b/lib/url.h index d481dabb80..43ee63cfc5 100644 --- a/lib/url.h +++ b/lib/url.h @@ -67,6 +67,12 @@ CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key, void Curl_conn_meta_remove(struct connectdata *conn, const char *key); void *Curl_conn_meta_get(struct connectdata *conn, const char *key); +/* Get an admin handle for internal operations from the given + * easy handle, if possible. The admin handle inherits certain + * properties from `data`. If no admin handle is available (not multi + * or share attached), the easy handle itself is returned. */ +struct Curl_easy *Curl_get_admin(struct Curl_easy *data); + #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ diff --git a/lib/urldata.h b/lib/urldata.h index 8601a89b8f..9aebefafcd 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -540,9 +540,7 @@ struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ - curl_off_t lastconnect_id; /* The last connection, -1 if undefined */ - curl_off_t recent_conn_id; /* The most recent connection used, might no - * longer exist */ + curl_off_t lastconnect_id; /* The last assigned connection or -1 */ struct dynbuf headerb; /* buffer to store headers in */ #ifndef CURL_DISABLE_HSTS struct curl_slist *hstslist; /* list of HSTS files set by diff --git a/tests/data/test1554 b/tests/data/test1554 index f766785647..38ea1feaa3 100644 --- a/tests/data/test1554 +++ b/tests/data/test1554 @@ -63,6 +63,8 @@ run 1: foobar and so on fun! [- Mutex unlock SHARE -] Mutex lock SHARE [- Mutex unlock SHARE +-] Mutex lock CONNECT +[- Mutex unlock CONNECT