From 823745b4a2b4eed69193551cfd36216f5d40790c Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Sat, 24 Nov 2012 23:06:10 +0100
Subject: [PATCH] internals: always use multi interface internally

curl_easy_perform() is now just a wrapper-function that uses multi
functions to achieve its functionality. All internals are all
non-blocking and multi interface friendly. No more two different
behaviors depending on which interface that is used.
---
 lib/easy.c     |  155 ++++++++++++++++----------------------------------------
 lib/multi.c    |    7 +--
 lib/transfer.c |    2 -
 lib/url.c      |    5 ++
 lib/urldata.h  |   11 +++-
 5 files changed, 62 insertions(+), 118 deletions(-)

diff --git a/lib/easy.c b/lib/easy.c
index 6e8ff77..906fd8b 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -390,40 +390,46 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
   return ret;
 }
 
-#ifdef CURL_MULTIEASY
-/***************************************************************************
- * This function is still only for testing purposes. It makes a great way
- * to run the full test suite on the multi interface instead of the easy one.
- ***************************************************************************
- *
- * The *new* curl_easy_perform() is the external interface that performs a
- * transfer previously setup.
+/*
+ * curl_easy_perform() is the external interface that performs a blocking
+ * transfer as previously setup.
  *
- * Wrapper-function that: creates a multi handle, adds the easy handle to it,
+ * CONCEPT: This function creates a multi handle, adds the easy handle to it,
  * runs curl_multi_perform() until the transfer is done, then detaches the
  * easy handle, destroys the multi handle and returns the easy handle's return
- * code. This will make everything internally use and assume multi interface.
+ * code.
+ *
+ * REALITY: it can't just create and destroy the multi handle that easily. It
+ * needs to keep it around since if this easy handle is used again by this
+ * function, the same multi handle must be re-used so that the same pools and
+ * caches can be used.
  */
 CURLcode curl_easy_perform(CURL *easy)
 {
   CURLM *multi;
   CURLMcode mcode;
   CURLcode code = CURLE_OK;
-  int still_running;
-  struct timeval timeout;
-  int rc;
   CURLMsg *msg;
-  fd_set fdread;
-  fd_set fdwrite;
-  fd_set fdexcep;
-  int maxfd;
+  bool done = FALSE;
+  int rc;
+  struct SessionHandle *data = easy;
 
   if(!easy)
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  multi = curl_multi_init();
-  if(!multi)
-    return CURLE_OUT_OF_MEMORY;
+  if(data->multi) {
+    failf(data, "easy handled already used in multi handle");
+    return CURLE_FAILED_INIT;
+  }
+
+  if(data->multi_easy)
+    multi = data->multi_easy;
+  else {
+    multi = curl_multi_init();
+    if(!multi)
+      return CURLE_OUT_OF_MEMORY;
+    data->multi_easy = multi;
+  }
 
   mcode = curl_multi_add_handle(multi, easy);
   if(mcode) {
@@ -434,108 +440,33 @@ CURLcode curl_easy_perform(CURL *easy)
       return CURLE_FAILED_INIT;
   }
 
-  /* we start some action by calling perform right away */
-
-  do {
-    while(CURLM_CALL_MULTI_PERFORM ==
-          curl_multi_perform(multi, &still_running));
-
-    if(!still_running)
-      break;
-
-    FD_ZERO(&fdread);
-    FD_ZERO(&fdwrite);
-    FD_ZERO(&fdexcep);
-
-    /* timeout once per second */
-    timeout.tv_sec = 1;
-    timeout.tv_usec = 0;
-
-    /* Old deprecated style: get file descriptors from the transfers */
-    curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
-    rc = Curl_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
-
-    /* The way is to extract the sockets and wait for them without using
-       select. This whole alternative version should probably rather use the
-       curl_multi_socket() approach. */
-
-    if(rc == -1)
-      /* select error */
-      break;
-
-    /* timeout or data to send/receive => loop! */
-  } while(still_running);
-
-  msg = curl_multi_info_read(multi, &rc);
-  if(msg)
-    code = msg->data.result;
-
-  mcode = curl_multi_remove_handle(multi, easy);
-  /* what to do if it fails? */
-
-  mcode = curl_multi_cleanup(multi);
-  /* what to do if it fails? */
-
-  return code;
-}
-#else
-/*
- * curl_easy_perform() is the external interface that performs a transfer
- * previously setup.
- */
-CURLcode curl_easy_perform(CURL *curl)
-{
-  struct SessionHandle *data = (struct SessionHandle *)curl;
+  /* assign this after curl_multi_add_handle() since that function checks for
+     it and rejects this handle otherwise */
+  data->multi = multi;
 
-  if(!data)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
+  while(!done && !mcode) {
+    int still_running;
 
-  if(! (data->share && data->share->hostcache)) {
-    /* this handle is not using a shared dns cache */
+    mcode = curl_multi_wait(multi, NULL, 0, 1000, NULL);
 
-    if(data->set.global_dns_cache &&
-       (data->dns.hostcachetype != HCACHE_GLOBAL)) {
-      /* global dns cache was requested but still isn't */
-      struct curl_hash *ptr;
+    if(mcode == CURLM_OK)
+      mcode = curl_multi_perform(multi, &still_running);
 
-      if(data->dns.hostcachetype == HCACHE_PRIVATE) {
-        /* if the current cache is private, kill it first */
-        Curl_hash_destroy(data->dns.hostcache);
-        data->dns.hostcachetype = HCACHE_NONE;
-        data->dns.hostcache = NULL;
+    if(!still_running) {
+      msg = curl_multi_info_read(multi, &rc);
+      if(msg) {
+        code = msg->data.result;
+        done = TRUE;
       }
-
-      ptr = Curl_global_host_cache_init();
-      if(ptr) {
-        /* only do this if the global cache init works */
-        data->dns.hostcache = ptr;
-        data->dns.hostcachetype = HCACHE_GLOBAL;
-      }
-    }
-
-    if(!data->dns.hostcache) {
-      data->dns.hostcachetype = HCACHE_PRIVATE;
-      data->dns.hostcache = Curl_mk_dnscache();
-
-      if(!data->dns.hostcache)
-        /* While we possibly could survive and do good without a host cache,
-           the fact that creating it failed indicates that things are truly
-           screwed up and we should bail out! */
-        return CURLE_OUT_OF_MEMORY;
     }
-
   }
 
-  if(!data->state.connc) {
-    /* oops, no connection cache, make one up */
-    data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1L);
-    if(!data->state.connc)
-      return CURLE_OUT_OF_MEMORY;
-  }
+  mcode = curl_multi_remove_handle(multi, easy);
+
+  /* The multi handle is kept alive, owned by the easy handle */
 
-  return Curl_perform(data);
+  return code;
 }
-#endif
 
 /*
  * curl_easy_cleanup() is the external interface to cleaning/freeing the given
diff --git a/lib/multi.c b/lib/multi.c
index b4e01bd..284e23a 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -954,7 +954,7 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
   int bitmap;
   unsigned int i;
   unsigned int nfds = extra_nfds;
-  struct pollfd *ufds;
+  struct pollfd *ufds = NULL;
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -983,7 +983,8 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
     easy = easy->next; /* check next handle */
   }
 
-  ufds = (struct pollfd *)malloc(nfds * sizeof(struct pollfd));
+  if(nfds)
+    ufds = (struct pollfd *)malloc(nfds * sizeof(struct pollfd));
   nfds = 0;
 
   /* Add the curl handles to our pollfds first */
@@ -1030,7 +1031,7 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
   else
     i = 0;
 
-  free(ufds);
+  Curl_safefree(ufds);
   if(ret)
     *ret = i;
   return CURLM_OK;
diff --git a/lib/transfer.c b/lib/transfer.c
index 51b2f77..ad043d6 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -2074,8 +2074,6 @@ static CURLcode Curl_do_perform(struct SessionHandle *data)
   char *newurl = NULL; /* possibly a new URL to follow to! */
   followtype follow = FOLLOW_NONE;
 
-  data->state.used_interface = Curl_if_easy;
-
   res = Curl_pretransfer(data);
   if(res)
     return res;
diff --git a/lib/url.c b/lib/url.c
index 601d8d3..83d3523 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -445,6 +445,11 @@ CURLcode Curl_close(struct SessionHandle *data)
        and detach this handle from there. */
     curl_multi_remove_handle(data->multi, data);
 
+  if(data->multi_easy)
+    /* when curl_easy_perform() is used, it creates its own multi handle to
+       use and this is the one */
+    curl_multi_cleanup(data->multi_easy);
+
   /* Destroy the timeout list that is held in the easy handle. It is
      /normally/ done by curl_multi_remove_handle() but this is "just in
      case" */
diff --git a/lib/urldata.h b/lib/urldata.h
index 4116c34..7228f64 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1165,6 +1165,11 @@ struct UrlState {
     Curl_if_multi
   } used_interface;
 
+  /* when curl_easy_perform() is called, the multi handle is "owned" by
+     the easy handle so curl_easy_cleanup() on such an easy handle will
+     also close the multi handle! */
+  bool multi_owned_by_easy;
+
   struct conncache *connc; /* points to the connection cache this handle
                               uses */
 
@@ -1619,7 +1624,11 @@ struct Names {
 struct SessionHandle {
   struct Names dns;
   struct Curl_multi *multi;    /* if non-NULL, points to the multi handle
-                                  struct to which this "belongs" */
+                                  struct to which this "belongs" when used by
+                                  the multi interface */
+  struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle
+                                    struct to which this "belongs" when used
+                                    by the easy interface */
   struct Curl_one_easy *multi_pos; /* if non-NULL, points to its position
                                       in multi controlling structure to assist
                                       in removal. */
-- 
1.7.10.4

