mirror of
https://github.com/curl/curl.git
synced 2026-05-30 06:17:28 +03:00
lib: introduce struct easy_poll_set for poll information
Connection filter had a `get_select_socks()` method, inspired by the various `getsocks` functions involved during the lifetime of a transfer. These, depending on transfer state (CONNECT/DO/DONE/ etc.), return sockets to monitor and flag if this shall be done for POLLIN and/or POLLOUT. Due to this design, sockets and flags could only be added, not removed. This led to problems in filters like HTTP/2 where flow control prohibits the sending of data until the peer increases the flow window. The general transfer loop wants to write, adds POLLOUT, the socket is writeable but no data can be written. This leads to cpu busy loops. To prevent that, HTTP/2 did set the `SEND_HOLD` flag of such a blocked transfer, so the transfer loop cedes further attempts. This works if only one such filter is involved. If a HTTP/2 transfer goes through a HTTP/2 proxy, two filters are setting/clearing this flag and may step on each other's toes. Connection filters `get_select_socks()` is replaced by `adjust_pollset()`. They get passed a `struct easy_pollset` that keeps up to `MAX_SOCKSPEREASYHANDLE` sockets and their `POLLIN|POLLOUT` flags. This struct is initialized in `multi_getsock()` by calling the various `getsocks()` implementations based on transfer state, as before. After protocol handlers/transfer loop have set the sockets and flags they want, the `easy_pollset` is *always* passed to the filters. Filters "higher" in the chain are called first, starting at the first not-yet-connection one. Each filter may add sockets and/or change flags. When all flags are removed, the socket itself is removed from the pollset. Example: * transfer wants to send, adds POLLOUT * http/2 filter has a flow control block, removes POLLOUT and adds POLLIN (it is waiting on a WINDOW_UPDATE from the server) * TLS filter is connected and changes nothing * h2-proxy filter also has a flow control block on its tunnel stream, removes POLLOUT and adds POLLIN also. * socket filter is connected and changes nothing * The resulting pollset is then mixed together with all other transfers and their pollsets, just as before. Use of `SEND_HOLD` is no longer necessary in the filters. All filters are adapted for the changed method. The handling in `multi.c` has been adjusted, but its state handling the the protocol handlers' `getsocks` method are untouched. The most affected filters are http/2, ngtcp2, quiche and h2-proxy. TLS filters needed to be adjusted for the connecting handshake read/write handling. No noticeable difference in performance was detected in local scorecard runs. Closes #11833
This commit is contained in:
parent
29e198bc71
commit
47f5b1a37f
29 changed files with 692 additions and 603 deletions
|
|
@ -325,42 +325,25 @@ out:
|
|||
return result;
|
||||
}
|
||||
|
||||
static int cf_hc_get_select_socks(struct Curl_cfilter *cf,
|
||||
static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
curl_socket_t *socks)
|
||||
struct easy_pollset *ps)
|
||||
{
|
||||
struct cf_hc_ctx *ctx = cf->ctx;
|
||||
size_t i, j, s;
|
||||
int brc, rc = GETSOCK_BLANK;
|
||||
curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE];
|
||||
struct cf_hc_baller *ballers[2];
|
||||
if(!cf->connected) {
|
||||
struct cf_hc_ctx *ctx = cf->ctx;
|
||||
struct cf_hc_baller *ballers[2];
|
||||
size_t i;
|
||||
|
||||
if(cf->connected)
|
||||
return cf->next->cft->get_select_socks(cf->next, data, socks);
|
||||
|
||||
ballers[0] = &ctx->h3_baller;
|
||||
ballers[1] = &ctx->h21_baller;
|
||||
for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
|
||||
struct cf_hc_baller *b = ballers[i];
|
||||
if(!cf_hc_baller_is_active(b))
|
||||
continue;
|
||||
brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks);
|
||||
CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc);
|
||||
if(!brc)
|
||||
continue;
|
||||
for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) {
|
||||
if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) {
|
||||
socks[s] = bsocks[j];
|
||||
if(brc & GETSOCK_WRITESOCK(j))
|
||||
rc |= GETSOCK_WRITESOCK(s);
|
||||
if(brc & GETSOCK_READSOCK(j))
|
||||
rc |= GETSOCK_READSOCK(s);
|
||||
s++;
|
||||
}
|
||||
ballers[0] = &ctx->h3_baller;
|
||||
ballers[1] = &ctx->h21_baller;
|
||||
for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
|
||||
struct cf_hc_baller *b = ballers[i];
|
||||
if(!cf_hc_baller_is_active(b))
|
||||
continue;
|
||||
Curl_conn_cf_adjust_pollset(b->cf, data, ps);
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool cf_hc_data_pending(struct Curl_cfilter *cf,
|
||||
|
|
@ -455,7 +438,7 @@ struct Curl_cftype Curl_cft_http_connect = {
|
|||
cf_hc_connect,
|
||||
cf_hc_close,
|
||||
Curl_cf_def_get_host,
|
||||
cf_hc_get_select_socks,
|
||||
cf_hc_adjust_pollset,
|
||||
cf_hc_data_pending,
|
||||
Curl_cf_def_send,
|
||||
Curl_cf_def_recv,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue