[Libguestfs] [PATCH nbdkit] curl: Try to share as much as possible between handles in the pool

Richard W.M. Jones rjones at redhat.com
Wed Feb 22 15:01:46 UTC 2023


Using the libcurl share interface we can share data between the
separate curl easy handles in the pool.  For more about this see:

https://curl.se/libcurl/c/CURLSHOPT_SHARE.html
https://gist.github.com/bagder/7eccf74f8b6d70b5abefeb7f288dba9b
https://everything.curl.dev/libcurl/sharing
---
 plugins/curl/curldefs.h |  3 +-
 plugins/curl/curl.c     |  4 ++-
 plugins/curl/pool.c     | 75 +++++++++++++++++++++++++++++++++++++++--
 3 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/plugins/curl/curldefs.h b/plugins/curl/curldefs.h
index c2a3432fc..d614379d0 100644
--- a/plugins/curl/curldefs.h
+++ b/plugins/curl/curldefs.h
@@ -117,9 +117,10 @@ struct curl_handle {
 };
 
 /* pool.c */
+extern void load_pool (void);
+extern void unload_pool (void);
 extern struct curl_handle *get_handle (void);
 extern void put_handle (struct curl_handle *ch);
-extern void free_all_handles (void);
 
 /* scripts.c */
 extern int do_scripts (struct curl_handle *ch);
diff --git a/plugins/curl/curl.c b/plugins/curl/curl.c
index b5927b5b4..b8624a6f8 100644
--- a/plugins/curl/curl.c
+++ b/plugins/curl/curl.c
@@ -101,6 +101,8 @@ curl_load (void)
     nbdkit_error ("libcurl initialization failed: %d", (int) r);
     exit (EXIT_FAILURE);
   }
+
+  load_pool ();
 }
 
 static void
@@ -112,7 +114,7 @@ curl_unload (void)
   free (password);
   free (proxy_password);
   scripts_unload ();
-  free_all_handles ();
+  unload_pool ();
   curl_global_cleanup ();
 }
 
diff --git a/plugins/curl/pool.c b/plugins/curl/pool.c
index b6f4f8e5f..536123388 100644
--- a/plugins/curl/pool.c
+++ b/plugins/curl/pool.c
@@ -52,6 +52,7 @@
 
 #include <nbdkit-plugin.h>
 
+#include "array-size.h"
 #include "ascii-ctype.h"
 #include "ascii-string.h"
 #include "cleanup.h"
@@ -73,6 +74,18 @@ static int debug_cb (CURL *handle, curl_infotype type,
 static size_t header_cb (void *ptr, size_t size, size_t nmemb, void *opaque);
 static size_t write_cb (char *ptr, size_t size, size_t nmemb, void *opaque);
 static size_t read_cb (void *ptr, size_t size, size_t nmemb, void *opaque);
+static void lock_cb (CURL *handle, curl_lock_data data,
+                     curl_lock_access access, void *userptr);
+static void unlock_cb (CURL *handle, curl_lock_data data,
+                       void *userptr);
+
+/* These locks protect access to the curl share data.  See:
+ * https://gist.github.com/bagder/7eccf74f8b6d70b5abefeb7f288dba9b
+ */
+static pthread_rwlock_t lockarray[CURL_LOCK_DATA_LAST];
+
+/* Curl share data. */
+static CURLSH *share;
 
 /* This lock protects access to the curl_handles vector below. */
 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
@@ -90,18 +103,46 @@ static curl_handle_list curl_handles = empty_vector;
 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 static size_t in_use = 0, waiting = 0;
 
-/* Close and free all handles in the pool. */
+/* Initialize pool structures. */
 void
-free_all_handles (void)
+load_pool (void)
 {
   size_t i;
 
+  for (i = 0; i < ARRAY_SIZE (lockarray); ++i)
+    pthread_rwlock_init (&lockarray[i], NULL);
+
+  share = curl_share_init ();
+  if (share == NULL) {
+    nbdkit_error ("curl_share_init: %m");
+    exit (EXIT_FAILURE);
+  }
+  curl_share_setopt (share, CURLSHOPT_LOCKFUNC, lock_cb);
+  curl_share_setopt (share, CURLSHOPT_UNLOCKFUNC, unlock_cb);
+  curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
+  curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
+  curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
+  curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
+}
+
+void
+unload_pool (void)
+{
+  size_t i;
+
+  /* Close and free all handles in the pool. */
   nbdkit_debug ("free_all_handles: number of curl handles allocated: %zu",
                 curl_handles.len);
 
   for (i = 0; i < curl_handles.len; ++i)
     free_handle (curl_handles.ptr[i]);
   curl_handle_list_reset (&curl_handles);
+
+  /* Free share data. */
+  curl_share_cleanup (share);
+
+  for (i = 0; i < ARRAY_SIZE (lockarray); ++i)
+    pthread_rwlock_destroy (&lockarray[i]);
 }
 
 /* Get a handle from the pool.
@@ -221,6 +262,9 @@ allocate_handle (void)
     goto err;
   }
 
+  /* Share settings with other handles. */
+  curl_easy_setopt (ch->c, CURLOPT_SHARE, share);
+
   /* Various options we always set.
    *
    * NB: Both here and below constants must be explicitly long because
@@ -519,3 +563,30 @@ free_handle (struct curl_handle *ch)
     curl_slist_free_all (ch->headers_copy);
   free (ch);
 }
+
+static void
+lock_cb (CURL *handle, curl_lock_data data, curl_lock_access access,
+         void *userptr)
+{
+  assert (data < ARRAY_SIZE (lockarray));
+
+  switch (access) {
+  case CURL_LOCK_ACCESS_SHARED:
+    pthread_rwlock_rdlock (&lockarray[data]);
+    break;
+  case CURL_LOCK_ACCESS_SINGLE:
+    pthread_rwlock_wrlock (&lockarray[data]);
+    break;
+  default:
+    /* CURL_LOCK_ACCESS_NONE is never used in the current libcurl code. */
+    abort ();
+  }
+}
+
+static void
+unlock_cb (CURL *handle, curl_lock_data data, void *userptr)
+{
+  assert (data < ARRAY_SIZE (lockarray));
+
+  pthread_rwlock_unlock (&lockarray[data]);
+}
-- 
2.39.0



More information about the Libguestfs mailing list