]> granicus.if.org Git - curl/commitdiff
low-speed-limit: avoid timeout flood
authorDaniel Stenberg <daniel@haxx.se>
Mon, 25 Aug 2014 09:34:14 +0000 (11:34 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 31 Aug 2014 21:50:01 +0000 (23:50 +0200)
Introducing Curl_expire_latest(). To be used when we the code flow only
wants to get called at a later time that is "no later than X" so that
something can be checked (and another timeout be added).

The low-speed logic for example could easily be made to set very many
expire timeouts if it would be called faster or sooner than what it had
set its own timer and this goes for a few other timers too that aren't
explictiy checked for timer expiration in the code.

If there's no condition the code that says if(time-passed >= TIME), then
Curl_expire_latest() is preferred to Curl_expire().

If there exists such a condition, it is on the other hand important that
Curl_expire() is used and not the other.

Bug: http://curl.haxx.se/mail/lib-2014-06/0235.html
Reported-by: Florian Weimer
lib/asyn-ares.c
lib/asyn-thread.c
lib/connect.c
lib/multi.c
lib/multiif.h
lib/speedcheck.c

index cb99a1f46e098eb2f7565768074e305f0088e23d..01a9c9b50e80dde7836b38d89c755ae6684f6018 100644 (file)
@@ -235,7 +235,7 @@ int Curl_resolver_getsock(struct connectdata *conn,
   milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
   if(milli == 0)
     milli += 10;
-  Curl_expire(conn->data, milli);
+  Curl_expire_latest(conn->data, milli);
 
   return max;
 }
index 6cdc9adff44796ed2e933abeff5a6a3baa2ba29e..e4ad32bb7dd53ffb14beefc58236c71dfdc6b6b2 100644 (file)
@@ -541,7 +541,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
       td->poll_interval = 250;
 
     td->interval_end = elapsed + td->poll_interval;
-    Curl_expire(conn->data, td->poll_interval);
+    Curl_expire_latest(conn->data, td->poll_interval);
   }
 
   return CURLE_OK;
index 6a79e64de7b97236145a3f2c403b189e7763725d..fb315fc8d1fa1c8fb113114c3dd55d9a954134be 100644 (file)
@@ -1054,7 +1054,7 @@ singleipconnect(struct connectdata *conn,
 
   conn->connecttime = Curl_tvnow();
   if(conn->num_addr > 1)
-    Curl_expire(data, conn->timeoutms_per_addr);
+    Curl_expire_latest(data, conn->timeoutms_per_addr);
 
   /* Connect TCP sockets, bind UDP */
   if(!isconnected && (conn->socktype == SOCK_STREAM)) {
index 20fc372faa46a969447edcc8df27b568a5ad29bb..557be06df3cb82f27e9f6ba5217b2582b6b0d228 100644 (file)
@@ -1491,7 +1491,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
                            data->set.buffer_size : BUFSIZE);
         timeout_ms = Curl_sleep_time(data->set.max_send_speed,
                                      data->progress.ulspeed, buffersize);
-        Curl_expire(data, timeout_ms);
+        Curl_expire_latest(data, timeout_ms);
         break;
       }
 
@@ -1507,7 +1507,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
                            data->set.buffer_size : BUFSIZE);
         timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
                                      data->progress.dlspeed, buffersize);
-        Curl_expire(data, timeout_ms);
+        Curl_expire_latest(data, timeout_ms);
         break;
       }
 
@@ -1569,7 +1569,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 
         /* expire the new receiving pipeline head */
         if(data->easy_conn->recv_pipe->head)
-          Curl_expire(data->easy_conn->recv_pipe->head->ptr, 1);
+          Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 1);
 
         /* Check if we can move pending requests to send pipe */
         Curl_multi_process_pending_handles(multi);
@@ -2682,6 +2682,46 @@ void Curl_expire(struct SessionHandle *data, long milli)
 #endif
 }
 
+/*
+ * Curl_expire_latest()
+ *
+ * This is like Curl_expire() but will only add a timeout node to the list of
+ * timers if there is no timeout that will expire before the given time.
+ *
+ * Use this function if the code logic risks calling this function many times
+ * or if there's no particular conditional wait in the code for this specific
+ * time-out period to expire.
+ *
+ */
+void Curl_expire_latest(struct SessionHandle *data, long milli)
+{
+  struct timeval *exp = &data->state.expiretime;
+
+  struct timeval set;
+
+  set = Curl_tvnow();
+  set.tv_sec += milli/1000;
+  set.tv_usec += (milli%1000)*1000;
+
+  if(set.tv_usec >= 1000000) {
+    set.tv_sec++;
+    set.tv_usec -= 1000000;
+  }
+
+  if(exp->tv_sec || exp->tv_usec) {
+    /* This means that the struct is added as a node in the splay tree.
+       Compare if the new time is earlier, and only remove-old/add-new if it
+         is. */
+    long diff = curlx_tvdiff(set, *exp);
+    if(diff > 0)
+      /* the new expire time was later than the top time, so just skip this */
+      return;
+  }
+
+  /* Just add the timeout like normal */
+  Curl_expire(data, milli);
+}
+
 CURLMcode curl_multi_assign(CURLM *multi_handle,
                             curl_socket_t s, void *hashp)
 {
index 1cbd31009c6c47c51ad7eb300b0a1a7996bfd467..c77b3ca3bf8ee0485b74825ce9228d8a1fbeb856 100644 (file)
@@ -26,6 +26,7 @@
  * Prototypes for library-wide functions provided by multi.c
  */
 void Curl_expire(struct SessionHandle *data, long milli);
+void Curl_expire_latest(struct SessionHandle *data, long milli);
 
 bool Curl_multi_pipeline_enabled(const struct Curl_multi* multi);
 void Curl_multi_handlePipeBreak(struct SessionHandle *data);
index ea17a597544c5dc0aef3fde6196ed4b9cee813df..ac7447c41070abdc768b8027f18b21ca44e3c425 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -57,7 +57,7 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
     }
     else {
       /* wait complete low_speed_time */
-      Curl_expire(data, nextcheck);
+      Curl_expire_latest(data, nextcheck);
     }
   }
   else {
@@ -68,7 +68,7 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
       /* if there is a low speed limit enabled, we set the expire timer to
          make this connection's speed get checked again no later than when
          this time is up */
-      Curl_expire(data, data->set.low_speed_time*1000);
+      Curl_expire_latest(data, data->set.low_speed_time*1000);
   }
   return CURLE_OK;
 }