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
This commit is contained in:
Stefan Eissing 2024-06-07 14:38:51 +02:00 committed by Daniel Stenberg
parent 1424d507aa
commit 3841569ec8
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
10 changed files with 86 additions and 2 deletions

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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 */

View File

@ -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;

View File

@ -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.
*/

View File

@ -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)
};

View File

@ -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);
}

View File

@ -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 */