From: Stefan Eissing Date: Fri, 7 Jun 2024 12:38:51 +0000 (+0200) Subject: transfer: do not use EXPIRE_NOW while blocked X-Git-Tag: curl-8_9_0~243 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3841569ec8521f2b5839b34d49c84c54820ac044;p=thirdparty%2Fcurl.git transfer: do not use EXPIRE_NOW while blocked - When a transfer sets `data->state.select_bits`, it is scheduled for rerun with EXPIRE_NOW. If such a transfer is blocked (due to PAUSE, for example), this will lead to a busy loop. - multi.c: check for transfer block - sendf.*: add Curl_xfer_is_blocked() - sendf.*: add client reader `is_paused()` callback - implement is_paused()` callback where needed Closes #13908 --- diff --git a/lib/c-hyper.c b/lib/c-hyper.c index d6c27add1f..17178d7086 100644 --- a/lib/c-hyper.c +++ b/lib/c-hyper.c @@ -1206,6 +1206,7 @@ static const struct Curl_crtype cr_hyper_protocol = { Curl_creader_def_resume_from, Curl_creader_def_rewind, cr_hyper_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct Curl_creader) }; diff --git a/lib/http.c b/lib/http.c index b31f344716..d6be6bd012 100644 --- a/lib/http.c +++ b/lib/http.c @@ -4488,6 +4488,7 @@ static const struct Curl_crtype cr_exp100 = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, cr_exp100_done, sizeof(struct cr_exp100_ctx) }; diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 48e7e462ba..c8cda0808b 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -659,6 +659,7 @@ const struct Curl_crtype Curl_httpchunk_encoder = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct chunked_reader) }; diff --git a/lib/mime.c b/lib/mime.c index 428843fa55..68696a102e 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -2096,6 +2096,14 @@ static CURLcode cr_mime_unpause(struct Curl_easy *data, return CURLE_OK; } +static bool cr_mime_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + return (ctx->part && ctx->part->lastreadstatus == CURL_READFUNC_PAUSE); +} + static const struct Curl_crtype cr_mime = { "cr-mime", cr_mime_init, @@ -2106,6 +2114,7 @@ static const struct Curl_crtype cr_mime = { cr_mime_resume_from, cr_mime_rewind, cr_mime_unpause, + cr_mime_is_paused, Curl_creader_def_done, sizeof(struct cr_mime_ctx) }; diff --git a/lib/multi.c b/lib/multi.c index 88841c71c7..fad1899fa2 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2495,7 +2495,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } } - else if(data->state.select_bits) { + else if(data->state.select_bits && !Curl_xfer_is_blocked(data)) { /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer won't get stuck on this transfer at the expense of other concurrent transfers */ diff --git a/lib/sendf.c b/lib/sendf.c index 8a9ccb01b8..f904d850e1 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -606,6 +606,14 @@ CURLcode Curl_creader_def_unpause(struct Curl_easy *data, return CURLE_OK; } +bool Curl_creader_def_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return FALSE; +} + void Curl_creader_def_done(struct Curl_easy *data, struct Curl_creader *reader, int premature) { @@ -624,6 +632,7 @@ struct cr_in_ctx { BIT(seen_eos); BIT(errored); BIT(has_used_cb); + BIT(is_paused); }; static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader) @@ -646,6 +655,8 @@ static CURLcode cr_in_read(struct Curl_easy *data, struct cr_in_ctx *ctx = reader->ctx; size_t nread; + ctx->is_paused = FALSE; + /* Once we have errored, we will return the same error forever */ if(ctx->errored) { *pnread = 0; @@ -704,6 +715,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, } /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ CURL_TRC_READ(data, "cr_in_read, callback returned CURL_READFUNC_PAUSE"); + ctx->is_paused = TRUE; data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ *pnread = 0; *peos = FALSE; @@ -866,6 +878,22 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, return CURLE_OK; } +static CURLcode cr_in_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + ctx->is_paused = FALSE; + return CURLE_OK; +} + +static bool cr_in_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + return ctx->is_paused; +} static const struct Curl_crtype cr_in = { "cr-in", @@ -876,7 +904,8 @@ static const struct Curl_crtype cr_in = { cr_in_total_length, cr_in_resume_from, cr_in_rewind, - Curl_creader_def_unpause, + cr_in_unpause, + cr_in_is_paused, Curl_creader_def_done, sizeof(struct cr_in_ctx) }; @@ -1032,6 +1061,7 @@ static const struct Curl_crtype cr_lc = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct cr_lc_ctx) }; @@ -1205,6 +1235,7 @@ static const struct Curl_crtype cr_null = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct Curl_creader) }; @@ -1304,6 +1335,7 @@ static const struct Curl_crtype cr_buf = { cr_buf_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct cr_buf_ctx) }; @@ -1366,6 +1398,18 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data) return result; } +bool Curl_creader_is_paused(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + + while(reader) { + if(reader->crt->is_paused(data, reader)) + return TRUE; + reader = reader->next; + } + return FALSE; +} + void Curl_creader_done(struct Curl_easy *data, int premature) { struct Curl_creader *reader = data->req.reader_stack; diff --git a/lib/sendf.h b/lib/sendf.h index 82a290257a..dc1b82edfe 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -218,6 +218,7 @@ struct Curl_crtype { struct Curl_creader *reader, curl_off_t offset); CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader); CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader); + bool (*is_paused)(struct Curl_easy *data, struct Curl_creader *reader); void (*done)(struct Curl_easy *data, struct Curl_creader *reader, int premature); size_t creader_size; /* sizeof() allocated struct Curl_creader */ @@ -268,6 +269,8 @@ CURLcode Curl_creader_def_rewind(struct Curl_easy *data, struct Curl_creader *reader); CURLcode Curl_creader_def_unpause(struct Curl_easy *data, struct Curl_creader *reader); +bool Curl_creader_def_is_paused(struct Curl_easy *data, + struct Curl_creader *reader); void Curl_creader_def_done(struct Curl_easy *data, struct Curl_creader *reader, int premature); @@ -375,6 +378,11 @@ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset); */ CURLcode Curl_creader_unpause(struct Curl_easy *data); +/** + * Return TRUE iff any of the installed readers is paused. + */ +bool Curl_creader_is_paused(struct Curl_easy *data); + /** * Tell all client readers that they are done. */ diff --git a/lib/smtp.c b/lib/smtp.c index 99b47cb0a4..2acfe73ce3 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -1925,6 +1925,7 @@ static const struct Curl_crtype cr_eob = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct cr_eob_ctx) }; diff --git a/lib/transfer.c b/lib/transfer.c index 579de18d7b..ee4abe65b5 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1323,3 +1323,15 @@ CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done) sockindex = (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]); return Curl_conn_shutdown(data, sockindex, done); } + +bool Curl_xfer_is_blocked(struct Curl_easy *data) +{ + bool want_send = ((data)->req.keepon & KEEP_SEND); + bool want_recv = ((data)->req.keepon & KEEP_RECV); + if(!want_send) + return (want_recv && Curl_cwriter_is_paused(data)); + else if(!want_recv) + return (want_send && Curl_creader_is_paused(data)); + else + return Curl_creader_is_paused(data) && Curl_cwriter_is_paused(data); +} diff --git a/lib/transfer.h b/lib/transfer.h index ba3bc1f62b..33d51e3b15 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -135,4 +135,11 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data, CURLcode Curl_xfer_send_close(struct Curl_easy *data); CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done); +/** + * Return TRUE iff the transfer is not done, but further progress + * is blocked. For example when it is only receiving and its writer + * is PAUSED. + */ +bool Curl_xfer_is_blocked(struct Curl_easy *data); + #endif /* HEADER_CURL_TRANSFER_H */