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:
parent
1424d507aa
commit
3841569ec8
@ -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)
|
||||
};
|
||||
|
||||
@ -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)
|
||||
};
|
||||
|
||||
@ -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)
|
||||
};
|
||||
|
||||
@ -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)
|
||||
};
|
||||
|
||||
@ -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 */
|
||||
|
||||
46
lib/sendf.c
46
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;
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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)
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user