Hi friends,
Just a heads up about some of my current tinkering!
A very long time ago (commit 91f483c5) it struck me we should make sure that
we stop handling easy and multi interface differently internally. This, since
the current way makes things error prone and more complicated than having a
single code path. This fact has numerous times turned out to be a source for
bugs, grief and problems.
In order to accomplish this and still support the exact same API and ABI, I
want curl_easy_perform() to basically be a function that uses the multi
interface with only a single easy handle.
Time passed and now I'm about to actually try to make reality of this old
idea. I'm attaching a first very rough patch that shows what I mean. It runs a
lot of test cases fine but there's well over a dozen that breaks so I don't
consider this near a merge yet.
As you will see, the new curl_easy_perform() implementation is almost
ridiculously simple - even though I'm sure I've missed a couple of things we
need to add before we can consider it a true replacement.
I'm aware of some extensive pending patches coming up for the multi interface
for pipelining and more, so I will make sure to not rock the boat too much
before that happens. I will instead continue working and make all test cases
work with my new approach.
--
/ daniel.haxx.se
From 823745b4a2b4eed69193551cfd36216f5d40790c Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <[email protected]>
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
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html