connections: use admin handles only for maintenance
No binary (build failed)
—
(log yok)
| @@ -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 | |
| @@ -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. */ | |
| @@ -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); | |
| @@ -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 */ | |
| @@ -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; | |
| @@ -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 && \ | |
| @@ -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); | |
| @@ -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); | |
| @@ -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; | |
| @@ -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; | |
| } | |
| @@ -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); | |
| @@ -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)) | |
| @@ -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 */ | |
| @@ -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 | |
| @@ -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 |
| </datacheck> | |
| </reply> | |