mirror of
https://github.com/curl/curl.git
synced 2026-08-25 22:43:39 +03:00
shutdowns: split shutdown handling from connection pool
Further testing with timeouts in event based processing revealed that our current shutdown handling in the connection pool was not clear enough. Graceful shutdowns can only happen inside a multi handle and it was confusing to track in the code which situation actually applies. It seems better to split the shutdown handling off and have that code always be part of a multi handle. Add `cshutdn.[ch]` with its own struct to maintain connections being shut down. A `cshutdn` always belongs to a multi handle and uses that for socket/timeout monitoring. The `cpool`, which can be part of a multi or share, either passes connections to a `cshutdn` or terminates them with a one-time, best effort. Add an `admin` easy handle to each multi and share. This is used to perform all maintenance operations where no "real" easy handle is available. This solves the problem that the multi admin handle requires some additional initialisation (e.g. timeout list). The share needs its admin handle as it is often cleaned up when no other transfer or multi handle exists any more. But we need a `data` in almost every call. Fix file:// handling of errors when adding a new connection to the pool. Changes in `curl` itself: - for parallel transfers, do not set a connection pool in the share, rely on the multi's connection pool instead. While not a requirement for the new `cshutdn` to work, this is a) helpful in testing to trigger graceful shutdowns b) a broader code coverage of libcurl via the curl tool - on test_event with uv, cleanup the multi handle before returning from parallel_event(). The uv struct is on the stack, cleanup of the multi later will crash when it tries to register sockets. This is a "eat your own dogfood" related fix. Closes #16508
This commit is contained in:
parent
3afa47b627
commit
df672695e5
23 changed files with 1144 additions and 955 deletions
62
lib/multi.c
62
lib/multi.c
|
|
@ -227,8 +227,23 @@ struct Curl_multi *Curl_multi_handle(size_t ev_hashsize, /* event hash */
|
|||
Curl_hash_init(&multi->proto_hash, 23,
|
||||
Curl_hash_str, Curl_str_key_compare, ph_freeentry);
|
||||
|
||||
multi->admin = curl_easy_init();
|
||||
if(!multi->admin)
|
||||
goto error;
|
||||
/* Initialize admin handle to operate inside this multi */
|
||||
multi->admin->multi = multi;
|
||||
multi->admin->state.internal = TRUE;
|
||||
Curl_llist_init(&multi->admin->state.timeoutlist, NULL);
|
||||
#ifdef DEBUGBUILD
|
||||
if(getenv("CURL_DEBUG"))
|
||||
multi->admin->set.verbose = TRUE;
|
||||
#endif
|
||||
|
||||
if(Curl_cshutdn_init(&multi->cshutdn, multi))
|
||||
goto error;
|
||||
|
||||
if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect,
|
||||
multi, NULL, chashsize))
|
||||
multi->admin, NULL, chashsize))
|
||||
goto error;
|
||||
|
||||
if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
|
||||
|
|
@ -264,7 +279,13 @@ error:
|
|||
Curl_hash_destroy(&multi->proto_hash);
|
||||
Curl_hash_destroy(&multi->hostcache);
|
||||
Curl_cpool_destroy(&multi->cpool);
|
||||
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
||||
Curl_ssl_scache_destroy(multi->ssl_scache);
|
||||
if(multi->admin) {
|
||||
multi->admin->multi = NULL;
|
||||
Curl_close(&multi->admin);
|
||||
}
|
||||
|
||||
free(multi);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -396,6 +417,15 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
|
|||
Curl_cpool_xfer_init(data);
|
||||
multi_warn_debug(multi, data);
|
||||
|
||||
/* The admin handle only ever has default timeouts set. To improve the
|
||||
state somewhat we clone the timeouts from each added handle so that the
|
||||
admin handle always has the same timeouts as the most recently added
|
||||
easy handle. */
|
||||
multi->admin->set.timeout = data->set.timeout;
|
||||
multi->admin->set.server_response_timeout =
|
||||
data->set.server_response_timeout;
|
||||
multi->admin->set.no_signal = data->set.no_signal;
|
||||
|
||||
CURL_TRC_M(data, "added, transfers=%u", multi->num_easy);
|
||||
return CURLM_OK;
|
||||
}
|
||||
|
|
@ -475,7 +505,7 @@ static void multi_done_locked(struct connectdata *conn,
|
|||
conn->bits.close, mdctx->premature,
|
||||
Curl_conn_is_multiplex(conn, FIRSTSOCKET));
|
||||
connclose(conn, "disconnecting");
|
||||
Curl_cpool_disconnect(data, conn, mdctx->premature);
|
||||
Curl_conn_terminate(data, conn, mdctx->premature);
|
||||
}
|
||||
else {
|
||||
/* the connection is no longer in use by any transfer */
|
||||
|
|
@ -684,7 +714,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
|
|||
curl_socket_t s;
|
||||
s = Curl_getconnectinfo(data, &c);
|
||||
if((s != CURL_SOCKET_BAD) && c) {
|
||||
Curl_cpool_disconnect(data, c, TRUE);
|
||||
Curl_conn_terminate(data, c, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1032,7 +1062,8 @@ CURLMcode curl_multi_fdset(CURLM *m,
|
|||
}
|
||||
}
|
||||
|
||||
Curl_cpool_setfds(&multi->cpool, read_fd_set, write_fd_set, &this_max_fd);
|
||||
Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
|
||||
read_fd_set, write_fd_set, &this_max_fd);
|
||||
|
||||
*max_fd = this_max_fd;
|
||||
|
||||
|
|
@ -1068,7 +1099,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
|
|||
need += Curl_waitfds_add_ps(&cwfds, &ps);
|
||||
}
|
||||
|
||||
need += Curl_cpool_add_waitfds(&multi->cpool, &cwfds);
|
||||
need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
|
||||
|
||||
if(need != cwfds.n && ufds) {
|
||||
result = CURLM_OUT_OF_MEMORY;
|
||||
|
|
@ -1146,7 +1177,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
|
|||
}
|
||||
}
|
||||
|
||||
if(Curl_cpool_add_pollfds(&multi->cpool, &cpfds)) {
|
||||
if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) {
|
||||
result = CURLM_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -2492,7 +2523,7 @@ statemachine_end:
|
|||
We do not have to do this in every case block above where a
|
||||
failure is detected */
|
||||
Curl_detach_connection(data);
|
||||
Curl_cpool_disconnect(data, conn, dead_connection);
|
||||
Curl_conn_terminate(data, conn, dead_connection);
|
||||
}
|
||||
}
|
||||
else if(data->mstate == MSTATE_CONNECT) {
|
||||
|
|
@ -2581,7 +2612,7 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
|
|||
pointer now */
|
||||
n = Curl_node_next(e);
|
||||
|
||||
if(data && data != multi->cpool.idata) {
|
||||
if(data && data != multi->admin) {
|
||||
/* connection pool handle is processed below */
|
||||
sigpipe_apply(data, &pipe_st);
|
||||
result = multi_runsingle(multi, &now, data);
|
||||
|
|
@ -2590,8 +2621,8 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
|
|||
}
|
||||
}
|
||||
|
||||
sigpipe_apply(multi->cpool.idata, &pipe_st);
|
||||
Curl_cpool_multi_perform(multi, CURL_SOCKET_TIMEOUT);
|
||||
sigpipe_apply(multi->admin, &pipe_st);
|
||||
Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT);
|
||||
sigpipe_restore(&pipe_st);
|
||||
|
||||
if(multi_ischanged(m, TRUE))
|
||||
|
|
@ -2690,6 +2721,11 @@ CURLMcode curl_multi_cleanup(CURLM *m)
|
|||
}
|
||||
|
||||
Curl_cpool_destroy(&multi->cpool);
|
||||
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
||||
if(multi->admin) {
|
||||
multi->admin->multi = NULL;
|
||||
Curl_close(&multi->admin);
|
||||
}
|
||||
|
||||
multi->magic = 0; /* not good anymore */
|
||||
|
||||
|
|
@ -2855,7 +2891,7 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
|
|||
continue;
|
||||
|
||||
(void)add_next_timeout(mrc->now, multi, data);
|
||||
if(data == multi->cpool.idata) {
|
||||
if(data == multi->admin) {
|
||||
mrc->run_cpool = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2931,8 +2967,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||
|
||||
out:
|
||||
if(mrc.run_cpool) {
|
||||
sigpipe_apply(multi->cpool.idata, &mrc.pipe_st);
|
||||
Curl_cpool_multi_perform(multi, s);
|
||||
sigpipe_apply(multi->admin, &mrc.pipe_st);
|
||||
Curl_cshutdn_perform(&multi->cshutdn, multi->admin, s);
|
||||
}
|
||||
sigpipe_restore(&mrc.pipe_st);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue