cURL / Mailing Lists / curl-library / Single Mail

curl-library

[PATCH] Restore the functionality of CURLMOPT_MAXCONNECTS. When a connection is no longer used, it is kept in the cache. If the cache is full, the oldest idle connection is closed. If no connection is idle, the current one is closed instead.

From: Linus Nielsen Feltzing <linus_at_haxx.se>
Date: Fri, 18 Jan 2013 14:11:14 +0100

---
 lib/url.c |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 97 insertions(+), 11 deletions(-)
diff --git a/lib/url.c b/lib/url.c
index 80c8a99..0e36b11 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -122,6 +122,7 @@ int curl_win32_idn_to_ascii(const char *in, char **out);
 #include "http_proxy.h"
 #include "bundles.h"
 #include "conncache.h"
+#include "multihandle.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -131,6 +132,7 @@ int curl_win32_idn_to_ascii(const char *in, char **out);
 #include "memdebug.h"
 
 /* Local static prototypes */
+static bool kill_oldest_connection(struct SessionHandle *data);
 static void conn_free(struct connectdata *conn);
 static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
 static CURLcode do_init(struct connectdata *conn);
@@ -2710,6 +2712,68 @@ static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke)
   }
 }
 
+/*
+ * This function kills and removes an existing connection in the connection
+ * cache. The connection that has been unused for the longest time.
+ *
+ * Returns FALSE if it can't find any unused connection to kill.
+ */
+static bool
+kill_oldest_connection(struct SessionHandle *data)
+{
+  struct conncache *bc = data->state.conn_cache;
+  struct curl_hash_iterator iter;
+  struct curl_llist_element *curr;
+  struct curl_hash_element *he;
+  long highscore=-1;
+  long score;
+  struct timeval now;
+  struct connectdata *conn_candidate = NULL;
+  struct connectbundle *bundle;
+
+  now = Curl_tvnow();
+
+  Curl_hash_start_iterate(bc->hash, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    struct connectdata *conn;
+
+    bundle = he->ptr;
+
+    curr = bundle->conn_list->head;
+    while(curr) {
+      conn = curr->ptr;
+
+      if(!conn->inuse) {
+        /* Set higher score for the age passed since the connection was used */
+        score = Curl_tvdiff(now, conn->now);
+
+        if(score > highscore) {
+          highscore = score;
+          conn_candidate = conn;
+        }
+      }
+      curr = curr->next;
+    }
+
+    he = Curl_hash_next_element(&iter);
+  }
+
+  if(conn_candidate) {
+    /* Set the connection's owner correctly */
+    conn_candidate->data = data;
+
+    bundle = conn_candidate->bundle;
+
+    /* the winner gets the honour of being disconnected */
+    (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
+
+    return TRUE;
+  }
+
+  return FALSE;
+}
 
 /*
  * Given one filled in connection struct (named needle), this function should
@@ -2961,11 +3025,30 @@ ConnectionExists(struct SessionHandle *data,
   return FALSE; /* no matching connecting exists */
 }
 
-/* this connection can now be marked 'idle' */
-static void
-ConnectionDone(struct connectdata *conn)
+/* Mark the connection as 'idle', or close it if the cache is full.
+   Returns TRUE if the connection is kept, or FALSE if it was closed. */
+static bool
+ConnectionDone(struct SessionHandle *data, struct connectdata *conn)
 {
+  /* data->multi->maxconnects can be negative, deal with it. */
+  size_t maxconnects =
+    (data->multi->maxconnects < 0) ? 0 : data->multi->maxconnects;
+
   conn->inuse = FALSE;
+
+  if(maxconnects > 0 &&
+     data->state.conn_cache->num_connections >= maxconnects) {
+    infof(data, "Connection cache is full, closing the oldest one.\n");
+
+    if(!kill_oldest_connection(data)) {
+      infof(data, "No connections to kill. Killing this connection (%d)\n",
+            conn->connection_id);
+      (void)Curl_disconnect(conn, FALSE);
+      return FALSE;
+    }
+  }
+
+  return TRUE;
 }
 
 /*
@@ -5182,14 +5265,17 @@ CURLcode Curl_done(struct connectdata **connp,
       result = res2;
   }
   else {
-    ConnectionDone(conn); /* the connection is no longer in use */
-
-    /* remember the most recently used connection */
-    data->state.lastconnect = conn;
-
-    infof(data, "Connection #%ld to host %s left intact\n",
-          conn->connection_id,
-          conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
+    /* the connection is no longer in use */
+    if(ConnectionDone(data, conn)) {
+      /* remember the most recently used connection */
+      data->state.lastconnect = conn;
+
+      infof(data, "Connection #%ld to host %s left intact\n",
+            conn->connection_id,
+            conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
+    }
+    else
+      data->state.lastconnect = NULL;
   }
 
   *connp = NULL; /* to make the caller of this function better detect that
-- 
1.7.10.4
--------------050503000205040200070201
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html
--------------050503000205040200070201--
Received on 2001-09-17