Permalink
Browse files

always-multi: always use non-blocking internals

Remove internal separated behavior of the easy vs multi intercace.
curl_easy_perform() is now using the multi interface itself.

Several minor multi interface quirks and bugs have been fixed in the
process.

Much help with debugging this has been provided by: Yang Tse
  • Loading branch information...
1 parent 9fd88ab commit c43127414d89ccb9ef6517081f68986d991bcfb3 @bagder bagder committed Jan 17, 2013
View
@@ -21,8 +21,7 @@
2. libcurl - multi interface
2.1 More non-blocking
- 2.2 Remove easy interface internally
- 2.4 Fix HTTP Pipelining for PUT
+ 2.2 Fix HTTP Pipelining for PUT
3. Documentation
3.1 More and better
@@ -191,16 +190,7 @@
- The "DONE" operation (post transfer protocol-specific actions) for the
protocols SFTP, SMTP, FTP. Fixing Curl_done() for this is a worthy task.
-2.2 Remove easy interface internally
-
- Make curl_easy_perform() a wrapper-function that simply creates a multi
- handle, adds the easy handle to it, runs curl_multi_perform() until the
- transfer is done, then detach the easy handle, destroy the multi handle and
- return the easy handle's return code. This will thus make everything
- internally use and assume the multi interface. The select()-loop should use
- curl_multi_socket().
-
-2.4 Fix HTTP Pipelining for PUT
+2.2 Fix HTTP Pipelining for PUT
HTTP Pipelining can be a way to greatly enhance performance for multiple
serial requests and currently libcurl only supports that for HEAD and GET
View
@@ -43,4 +43,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
gopher.h axtls.h cyassl.h http_proxy.h non-ascii.h asyn.h curl_ntlm.h \
curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h curl_ntlm_msgs.h \
curl_sasl.h curl_schannel.h curl_multibyte.h curl_darwinssl.h \
- hostcheck.h bundles.h conncache.h curl_setup_once.h
+ hostcheck.h bundles.h conncache.h curl_setup_once.h multihandle.h
View
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2013, 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
@@ -47,7 +47,7 @@ static void free_bundle_hash_entry(void *freethis)
Curl_bundle_destroy(b);
}
-struct conncache *Curl_conncache_init(conncachetype type)
+struct conncache *Curl_conncache_init(void)
{
struct conncache *connc;
@@ -63,9 +63,6 @@ struct conncache *Curl_conncache_init(conncachetype type)
return NULL;
}
- connc->type = type;
- connc->num_connections = 0;
-
return connc;
}
View
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) 2012, 2013, Linus Nielsen Feltzing, <linus@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,18 +22,12 @@
*
***************************************************************************/
-typedef enum {
- CONNCACHE_PRIVATE, /* used for an easy handle alone */
- CONNCACHE_MULTI /* shared within a multi handle */
-} conncachetype;
-
struct conncache {
struct curl_hash *hash;
- conncachetype type;
size_t num_connections;
};
-struct conncache *Curl_conncache_init(conncachetype type);
+struct conncache *Curl_conncache_init(void);
void Curl_conncache_destroy(struct conncache *connc);
View
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, 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
@@ -74,6 +74,8 @@
#include "sslgen.h" /* for Curl_ssl_check_cxn() */
#include "progress.h"
#include "warnless.h"
+#include "conncache.h"
+#include "multihandle.h"
/* The last #include file should be: */
#include "memdebug.h"
@@ -980,8 +982,7 @@ singleipconnect(struct connectdata *conn,
/* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
connect(). We can be sure of this since connect() cannot return 1. */
- if((WAITCONN_TIMEOUT == rc) &&
- (data->state.used_interface == Curl_if_multi)) {
+ if(WAITCONN_TIMEOUT == rc) {
/* Timeout when running the multi interface */
*sockp = sockfd;
return CURLE_OK;
@@ -1072,9 +1073,8 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
/* start connecting to the IP curr_addr points to */
res = singleipconnect(conn, curr_addr,
- /* don't hang when doing multi */
- (data->state.used_interface == Curl_if_multi)?0:
- conn->timeoutms_per_addr, &sockfd, connected);
+ 0, /* don't hang when doing multi */
+ &sockfd, connected);
if(res)
return res;
@@ -1112,6 +1112,21 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
return CURLE_OK;
}
+struct connfind {
+ struct connectdata *tofind;
+ bool found;
+};
+
+static int conn_is_conn(struct connectdata *conn, void *param)
+{
+ struct connfind *f = (struct connfind *)param;
+ if(conn == f->tofind) {
+ f->found = TRUE;
+ return 1;
+ }
+ return 0;
+}
+
/*
* Used to extract socket and connectdata struct for the most recent
* transfer on the given SessionHandle.
@@ -1125,8 +1140,21 @@ curl_socket_t Curl_getconnectinfo(struct SessionHandle *data,
DEBUGASSERT(data);
- if(data->state.lastconnect) {
+ /* this only works for an easy handle that has been used for
+ curl_easy_perform()! */
+ if(data->state.lastconnect && data->multi_easy) {
struct connectdata *c = data->state.lastconnect;
+ struct connfind find;
+ find.tofind = data->state.lastconnect;
+ find.found = FALSE;
+
+ Curl_conncache_foreach(data->multi_easy->conn_cache, &find, conn_is_conn);
+
+ if(!find.found) {
+ data->state.lastconnect = NULL;
+ return CURL_SOCKET_BAD;
+ }
+
if(connp)
/* only store this if the caller cares for it */
*connp = c;
View
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, 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
@@ -385,40 +385,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) {
@@ -429,108 +435,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;
-
- if(!data)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ /* assign this after curl_multi_add_handle() since that function checks for
+ it and rejects this handle otherwise */
+ data->multi = multi;
- if(! (data->share && data->share->hostcache)) {
- /* this handle is not using a shared dns cache */
+ while(!done && !mcode) {
+ int still_running;
- if(data->set.global_dns_cache &&
- (data->dns.hostcachetype != HCACHE_GLOBAL)) {
- /* global dns cache was requested but still isn't */
- struct curl_hash *ptr;
+ mcode = curl_multi_wait(multi, NULL, 0, 1000, NULL);
- 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(mcode == CURLM_OK)
+ mcode = curl_multi_perform(multi, &still_running);
- 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;
+ /* only read 'still_running' if curl_multi_perform() return OK */
+ if((mcode == CURLM_OK) && !still_running) {
+ msg = curl_multi_info_read(multi, &rc);
+ if(msg) {
+ code = msg->data.result;
+ done = TRUE;
}
}
-
- 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.conn_cache) {
- /* Oops, no connection cache, create one */
- data->state.conn_cache = Curl_conncache_init(CONNCACHE_PRIVATE);
- if(!data->state.conn_cache)
- return CURLE_OUT_OF_MEMORY;
- }
+ mcode = curl_multi_remove_handle(multi, easy);
- return Curl_perform(data);
+ /* The multi handle is kept alive, owned by the easy handle */
+ return code;
}
-#endif
/*
* curl_easy_cleanup() is the external interface to cleaning/freeing the given
@@ -553,10 +484,6 @@ void Curl_easy_addmulti(struct SessionHandle *data,
void *multi)
{
data->multi = multi;
- if(multi == NULL)
- /* the association is cleared, mark the easy handle as not used by an
- interface */
- data->state.used_interface = Curl_if_none;
}
void Curl_easy_initHandleData(struct SessionHandle *data)
Oops, something went wrong.

0 comments on commit c431274

Please sign in to comment.