]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
transfer: do not use EXPIRE_NOW while blocked
authorStefan Eissing <stefan@eissing.org>
Fri, 7 Jun 2024 12:38:51 +0000 (14:38 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 13 Jun 2024 13:13:43 +0000 (15:13 +0200)
- 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

lib/c-hyper.c
lib/http.c
lib/http_chunks.c
lib/mime.c
lib/multi.c
lib/sendf.c
lib/sendf.h
lib/smtp.c
lib/transfer.c
lib/transfer.h

index d6c27add1f3f027cb2e046ea17fdbad8266d3577..17178d7086a84fdc34761acf0bd80a71b362a695 100644 (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)
 };
index b31f344716983d35c1c426e542f3df2fd85e7ce0..d6be6bd012036ab02024e4a703e13cfb3fb03b3f 100644 (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)
 };
index 48e7e462bad32a822da6a4fc817f43ab92411e7c..c8cda0808bec8f3657f29446c09d8c18c458b0fe 100644 (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)
 };
index 428843fa551f72ec3f3cd3f3f735b2fea8cc2049..68696a102e324d09714f60ee95f8dd56f6a5b6a4 100644 (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)
 };
index 88841c71c7781376a9abe39d1559967bdecf412b..fad1899fa22382fe196d1a3018384317d10d7729 100644 (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 */
index 8a9ccb01b86449eb064eee830418ba3d4ac84aae..f904d850e1a4732bdb2bc26f368750f8e49c7b58 100644 (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;
index 82a290257a5dd9a6e32782e627cff7dabbf83a38..dc1b82edfecd6269db51438b6225fd56273903dc 100644 (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.
  */
index 99b47cb0a4c08deb4f8170f331eff0a2bdcb7af8..2acfe73ce37dc01b1f8a3c675683471f80298373 100644 (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)
 };
index 579de18d7b39f72d770b4ad93ad2cf22de914966..ee4abe65b5f448195ef8e9c2ba054acb1b922278 100644 (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);
+}
index ba3bc1f62b853f158a357c05411f8392bbe59164..33d51e3b15b903bf77407193372504270c7215cc 100644 (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 */