1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-22 08:08:50 -05:00

First curl_multi_socket() commit. Should primarily be considered as an internal

code rearrange to fit the future better.
This commit is contained in:
Daniel Stenberg 2006-04-10 15:00:53 +00:00
parent 5dc02d53c3
commit 686d90745b
21 changed files with 1609 additions and 545 deletions

View File

@ -8,7 +8,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
content_encoding.c share.c http_digest.c md5.c http_negotiate.c \ content_encoding.c share.c http_digest.c md5.c http_negotiate.c \
http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \ http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \
hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \ hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \
select.c gtls.c sslgen.c tftp.c select.c gtls.c sslgen.c tftp.c splay.c
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \
@ -18,6 +18,6 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \ share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \
inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \ inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \
setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \ setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \
gtls.h tftp.h sockaddr.h gtls.h tftp.h sockaddr.h splay.h

View File

@ -98,6 +98,7 @@
#include "memory.h" #include "memory.h"
#include "select.h" #include "select.h"
#include "url.h" /* for Curl_safefree() */ #include "url.h" /* for Curl_safefree() */
#include "multiif.h"
#include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "sockaddr.h" /* required for Curl_sockaddr_storage */
/* The last #include file should be: */ /* The last #include file should be: */
@ -534,6 +535,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
CURLcode code = CURLE_OK; CURLcode code = CURLE_OK;
curl_socket_t sockfd = conn->sock[sockindex]; curl_socket_t sockfd = conn->sock[sockindex];
long allow = DEFAULT_CONNECT_TIMEOUT; long allow = DEFAULT_CONNECT_TIMEOUT;
long allow_total = 0;
long has_passed; long has_passed;
curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
@ -546,12 +548,12 @@ CURLcode Curl_is_connected(struct connectdata *conn,
/* subtract the most strict timeout of the ones */ /* subtract the most strict timeout of the ones */
if(data->set.timeout && data->set.connecttimeout) { if(data->set.timeout && data->set.connecttimeout) {
if (data->set.timeout < data->set.connecttimeout) if (data->set.timeout < data->set.connecttimeout)
allow = data->set.timeout*1000; allow_total = allow = data->set.timeout*1000;
else else
allow = data->set.connecttimeout*1000; allow = data->set.connecttimeout*1000;
} }
else if(data->set.timeout) { else if(data->set.timeout) {
allow = data->set.timeout*1000; allow_total = allow = data->set.timeout*1000;
} }
else if(data->set.connecttimeout) { else if(data->set.connecttimeout) {
allow = data->set.connecttimeout*1000; allow = data->set.connecttimeout*1000;
@ -564,10 +566,13 @@ CURLcode Curl_is_connected(struct connectdata *conn,
} }
if(conn->bits.tcpconnect) { if(conn->bits.tcpconnect) {
/* we are connected already! */ /* we are connected already! */
Curl_expire(data, allow_total);
*connected = TRUE; *connected = TRUE;
return CURLE_OK; return CURLE_OK;
} }
Curl_expire(data, allow);
/* check for connect without timeout as we want to return immediately */ /* check for connect without timeout as we want to return immediately */
rc = waitconnect(sockfd, 0); rc = waitconnect(sockfd, 0);
@ -818,6 +823,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
return CURLE_OPERATION_TIMEOUTED; return CURLE_OPERATION_TIMEOUTED;
} }
} }
Curl_expire(data, timeout_ms);
/* Max time for each address */ /* Max time for each address */
num_addr = Curl_num_addresses(remotehost->addr); num_addr = Curl_num_addresses(remotehost->addr);

View File

@ -96,6 +96,7 @@
#include "select.h" #include "select.h"
#include "parsedate.h" /* for the week day and month names */ #include "parsedate.h" /* for the week day and month names */
#include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "multiif.h"
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
#include "inet_ntoa_r.h" #include "inet_ntoa_r.h"
@ -718,27 +719,24 @@ static CURLcode ftp_state_pwd(struct connectdata *conn)
} }
/* For the FTP "protocol connect" and "doing" phases only */ /* For the FTP "protocol connect" and "doing" phases only */
CURLcode Curl_ftp_fdset(struct connectdata *conn, int Curl_ftp_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks)
int *max_fdp)
{ {
struct FTP *ftp = conn->proto.ftp; struct FTP *ftp = conn->proto.ftp;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
if(!numsocks)
return GETSOCK_BLANK;
socks[0] = conn->sock[FIRSTSOCKET];
if(ftp->sendleft) { if(ftp->sendleft) {
/* write mode */ /* write mode */
FD_SET(sockfd, write_fd_set); return GETSOCK_WRITESOCK(0);
} }
else {
/* read mode */ /* read mode */
FD_SET(sockfd, read_fd_set); return GETSOCK_READSOCK(0);
}
if((int)sockfd > *max_fdp)
*max_fdp = (int)sockfd;
return CURLE_OK;
} }
/* This is called after the FTP_QUOTE state is passed. /* This is called after the FTP_QUOTE state is passed.

View File

@ -34,10 +34,9 @@ CURLcode Curl_GetFTPResponse(ssize_t *nread, struct connectdata *conn,
int *ftpcode); int *ftpcode);
CURLcode Curl_ftp_nextconnect(struct connectdata *conn); CURLcode Curl_ftp_nextconnect(struct connectdata *conn);
CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, bool *done); CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, bool *done);
CURLcode Curl_ftp_fdset(struct connectdata *conn, int Curl_ftp_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks);
int *max_fdp);
CURLcode Curl_ftp_doing(struct connectdata *conn, CURLcode Curl_ftp_doing(struct connectdata *conn,
bool *dophase_done); bool *dophase_done);
#endif /* CURL_DISABLE_FTP */ #endif /* CURL_DISABLE_FTP */

View File

@ -124,8 +124,11 @@ mk_hash_element(char *key, size_t key_len, const void *p)
(struct curl_hash_element *) malloc(sizeof(struct curl_hash_element)); (struct curl_hash_element *) malloc(sizeof(struct curl_hash_element));
if(he) { if(he) {
char *dup = strdup(key); char *dup = malloc(key_len);
if(dup) { if(dup) {
/* copy the key */
memcpy(dup, key, key_len);
he->key = dup; he->key = dup;
he->key_len = key_len; he->key_len = key_len;
he->ptr = (void *) p; he->ptr = (void *) p;
@ -179,6 +182,23 @@ Curl_hash_add(struct curl_hash *h, char *key, size_t key_len, void *p)
return NULL; /* failure */ return NULL; /* failure */
} }
/* remove the identified hash entry, returns non-zero on failure */
int Curl_hash_delete(struct curl_hash *h, char *key, size_t key_len)
{
struct curl_llist_element *le;
struct curl_hash_element *he;
struct curl_llist *l = FETCH_LIST(h, key, key_len);
for (le = l->head; le; le = le->next) {
he = le->ptr;
if (hash_key_compare(he->key, he->key_len, key, key_len)) {
Curl_llist_remove(l, le, (void *) h);
return 0;
}
}
return 1;
}
void * void *
Curl_hash_pick(struct curl_hash *h, char *key, size_t key_len) Curl_hash_pick(struct curl_hash *h, char *key, size_t key_len)
{ {
@ -186,9 +206,7 @@ Curl_hash_pick(struct curl_hash *h, char *key, size_t key_len)
struct curl_hash_element *he; struct curl_hash_element *he;
struct curl_llist *l = FETCH_LIST(h, key, key_len); struct curl_llist *l = FETCH_LIST(h, key, key_len);
for (le = l->head; for (le = l->head; le; le = le->next) {
le;
le = le->next) {
he = le->ptr; he = le->ptr;
if (hash_key_compare(he->key, he->key_len, key, key_len)) { if (hash_key_compare(he->key, he->key_len, key, key_len)) {
return he->ptr; return he->ptr;

View File

@ -105,17 +105,15 @@
* Returns: CURLE_OK always! * Returns: CURLE_OK always!
*/ */
CURLcode Curl_resolv_fdset(struct connectdata *conn, int Curl_resolv_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks)
int *max_fdp)
{ {
int max = ares_fds(conn->data->state.areschannel, int max = ares_getsock(conn->data->state.areschannel,
read_fd_set, write_fd_set); (int *)socks, numsocks);
*max_fdp = max;
return CURLE_OK; return max;
} }
/* /*

View File

@ -160,6 +160,14 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
CURLcode Curl_wait_for_resolv(struct connectdata *conn, CURLcode Curl_wait_for_resolv(struct connectdata *conn,
struct Curl_dns_entry **dnsentry); struct Curl_dns_entry **dnsentry);
/* Curl_resolv_getsock() is a generic function that exists in multiple versions
depending on what name resolve technology we've built to use. The function
is called from the multi_getsock() function */
int Curl_resolv_getsock(struct connectdata *conn,
curl_socket_t *sock,
int numsocks);
#if 0
/* Curl_resolv_fdset() is a generic function that exists in multiple versions /* Curl_resolv_fdset() is a generic function that exists in multiple versions
depending on what name resolve technology we've built to use. The function depending on what name resolve technology we've built to use. The function
is called from the curl_multi_fdset() function */ is called from the curl_multi_fdset() function */
@ -167,8 +175,11 @@ CURLcode Curl_resolv_fdset(struct connectdata *conn,
fd_set *read_fd_set, fd_set *read_fd_set,
fd_set *write_fd_set, fd_set *write_fd_set,
int *max_fdp); int *max_fdp);
#endif
/* unlock a previously resolved dns entry */ /* unlock a previously resolved dns entry */
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns); void Curl_resolv_unlock(struct SessionHandle *data,
struct Curl_dns_entry *dns);
/* for debugging purposes only: */ /* for debugging purposes only: */
void Curl_scan_cache_used(void *user, void *ptr); void Curl_scan_cache_used(void *user, void *ptr);

View File

@ -126,17 +126,15 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
* It is present here to keep #ifdefs out from multi.c * It is present here to keep #ifdefs out from multi.c
*/ */
CURLcode Curl_resolv_fdset(struct connectdata *conn, int Curl_resolv_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *sock,
fd_set *write_fd_set, int numsocks)
int *max_fdp)
{ {
(void)conn; (void)conn;
(void)read_fd_set; (void)sock;
(void)write_fd_set; (void)numsocks;
(void)max_fdp;
return CURLE_OK; return 0; /* no bits since we don't use any socks */
} }
#endif /* truly sync */ #endif /* truly sync */

View File

@ -97,6 +97,7 @@
#include "select.h" #include "select.h"
#include "parsedate.h" /* for the week day and month names */ #include "parsedate.h" /* for the week day and month names */
#include "strtoofft.h" #include "strtoofft.h"
#include "multiif.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -1416,26 +1417,25 @@ CURLcode Curl_https_connecting(struct connectdata *conn, bool *done)
} }
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
CURLcode Curl_https_proto_fdset(struct connectdata *conn, int Curl_https_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks)
int *max_fdp)
{ {
if (conn->protocol & PROT_HTTPS) { if (conn->protocol & PROT_HTTPS) {
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
if(!numsocks)
return GETSOCK_BLANK;
if (connssl->connecting_state == ssl_connect_2_writing) { if (connssl->connecting_state == ssl_connect_2_writing) {
/* write mode */ /* write mode */
FD_SET(sockfd, write_fd_set); socks[0] = conn->sock[FIRSTSOCKET];
if((int)sockfd > *max_fdp) return GETSOCK_WRITESOCK(0);
*max_fdp = (int)sockfd;
} }
else if (connssl->connecting_state == ssl_connect_2_reading) { else if (connssl->connecting_state == ssl_connect_2_reading) {
/* read mode */ /* read mode */
FD_SET(sockfd, read_fd_set); socks[0] = conn->sock[FIRSTSOCKET];
if((int)sockfd > *max_fdp) return GETSOCK_READSOCK(0);
*max_fdp = (int)sockfd;
} }
} }
return CURLE_OK; return CURLE_OK;

View File

@ -8,7 +8,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -38,10 +38,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done);
CURLcode Curl_http_done(struct connectdata *, CURLcode); CURLcode Curl_http_done(struct connectdata *, CURLcode);
CURLcode Curl_http_connect(struct connectdata *conn, bool *done); CURLcode Curl_http_connect(struct connectdata *conn, bool *done);
CURLcode Curl_https_connecting(struct connectdata *conn, bool *done); CURLcode Curl_https_connecting(struct connectdata *conn, bool *done);
CURLcode Curl_https_proto_fdset(struct connectdata *conn, int Curl_https_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks);
int *max_fdp);
/* The following functions are defined in http_chunks.c */ /* The following functions are defined in http_chunks.c */
void Curl_httpchunk_init(struct connectdata *conn); void Curl_httpchunk_init(struct connectdata *conn);

View File

@ -46,6 +46,7 @@
#include "easyif.h" #include "easyif.h"
#include "multiif.h" #include "multiif.h"
#include "sendf.h" #include "sendf.h"
#include "timeval.h"
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
@ -73,6 +74,18 @@ typedef enum {
CURLM_STATE_LAST /* not a true state, never use this */ CURLM_STATE_LAST /* not a true state, never use this */
} CURLMstate; } CURLMstate;
/* we support 16 sockets per easy handle. Set the corresponding bit to what
action we should wait for */
#define MAX_SOCKSPEREASYHANDLE 16
#define GETSOCK_READABLE (0x00ff)
#define GETSOCK_WRITABLE (0xff00)
struct socketstate {
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
long action; /* socket action bitmap */
long timeout[MAX_SOCKSPEREASYHANDLE];
};
struct Curl_one_easy { struct Curl_one_easy {
/* first, two fields for the linked list of these */ /* first, two fields for the linked list of these */
struct Curl_one_easy *next; struct Curl_one_easy *next;
@ -90,6 +103,8 @@ struct Curl_one_easy {
will be deleted when this handle is removed will be deleted when this handle is removed
from the multi-handle */ from the multi-handle */
int msg_num; /* number of messages left in 'msg' to return */ int msg_num; /* number of messages left in 'msg' to return */
struct socketstate sockstate; /* for the socket API magic */
}; };
#define CURL_MULTI_HANDLE 0x000bab1e #define CURL_MULTI_HANDLE 0x000bab1e
@ -111,8 +126,21 @@ struct Curl_multi {
int num_msgs; /* total amount of messages in the easy handles */ int num_msgs; /* total amount of messages in the easy handles */
/* callback function and user data pointer for the *socket() API */
curl_socket_callback socket_cb;
void *socket_userp;
/* Hostname cache */ /* Hostname cache */
struct curl_hash *hostcache; struct curl_hash *hostcache;
/* timetree points to the splay-tree of time nodes to figure out expire
times of all currently set timers */
struct Curl_tree *timetree;
/* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
the pluralis form, there can be more than one easy handle waiting on the
same actual socket) */
struct curl_hash *sockhash;
}; };
/* always use this function to change state, to make debugging easier */ /* always use this function to change state, to make debugging easier */
@ -134,6 +162,7 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
}; };
CURLMstate oldstate = easy->state; CURLMstate oldstate = easy->state;
#endif #endif
easy->state = state; easy->state = state;
#ifdef CURLDEBUG #ifdef CURLDEBUG
@ -143,25 +172,166 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
#endif #endif
} }
/*
* We add one of these structs to the sockhash, and then if we add more easy
* handles for the same socket we just link them with the next/prev pointers
* from the node added to the hash. We only remove the node from the hash when
* the final easy handle/socket associated with the node is removed.
*/
struct Curl_sh_entry {
struct Curl_sh_entry *next;
struct Curl_sh_entry *prev;
struct SessionHandle *easy;
time_t timestamp;
long inuse;
};
/* make sure this socket is present in the hash for this handle */
static int sh_addentry(struct curl_hash *sh,
curl_socket_t s,
struct SessionHandle *data)
{
struct Curl_sh_entry *there =
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
struct Curl_sh_entry *check;
if(there) {
/* verify that this particular handle is in here */
check = there;
while(check) {
if(check->easy == data)
/* it is, return fine */
return 0;
check = check->next;
}
}
/* not present, add it */
check = calloc(sizeof(struct Curl_sh_entry), 1);
if(!check)
return 1; /* major failure */
check->easy = data;
if(there) {
/* the node for this socket is already here, now link in the struct for
the new handle */
check->next = there->next; /* get the previous next to point to */
there->next = check; /* make the new next point to the new entry */
check->next->prev = check; /* make sure the next one points back to the
new one */
/* check->prev = NULL; is already cleared and we have no previous
node */
}
else {
/* make/add new hash entry */
if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check))
return 1; /* major failure */
}
return 0; /* things are good in sockhash land */
}
/* delete the given socket + handle from the hash */
static void sh_delentry(struct curl_hash *sh,
curl_socket_t s,
struct SessionHandle *data)
{
struct Curl_sh_entry *there =
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
while(there) {
/* this socket is in the hash, now scan the list at this point and see if
the given easy handle is in there and if so remote that singe entry */
if(there->easy == data) {
/* match! */
if(there->next || there->prev) {
/* it is not the only handle for this socket, so only unlink this
particular easy handle and leave the actional hash entry */
/* unlink */
there->next->prev = there->prev;
there->prev->next = there->next;
free(there);
}
else {
/* This is the only easy handle for this socket, we must remove the
hash entry. (This'll end up in a call to sh_freeentry().) */
Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
}
break;
}
there = there->next;
}
}
/*
* free a sockhash entry
*/
static void sh_freeentry(void *freethis)
{
struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
struct Curl_sh_entry *more = p->next;
/* if there's a chain of more handles, remove that chain first */
while(more) {
struct Curl_sh_entry *next = more->next;
free(more);
more = next;
}
free(p);
}
/*
* sh_init() creates a new socket hash and returns the handle for it.
*
* Quote from README.multi_socket:
*
* "Some tests at 7000 and 9000 connections showed that the socket hash lookup
* is somewhat of a bottle neck. Its current implementation may be a bit too
* limiting. It simply has a fixed-size array, and on each entry in the array
* it has a linked list with entries. So the hash only checks which list to
* scan through. The code I had used so for used a list with merely 7 slots
* (as that is what the DNS hash uses) but with 7000 connections that would
* make an average of 1000 nodes in each list to run through. I upped that to
* 97 slots (I believe a prime is suitable) and noticed a significant speed
* increase. I need to reconsider the hash implementation or use a rather
* large default value like this. At 9000 connections I was still below 10us
* per call."
*
*/
static struct curl_hash *sh_init(void)
{
return Curl_hash_alloc(97, sh_freeentry);
}
CURLM *curl_multi_init(void) CURLM *curl_multi_init(void)
{ {
struct Curl_multi *multi; struct Curl_multi *multi = (void *)calloc(sizeof(struct Curl_multi), 1);
multi = (void *)malloc(sizeof(struct Curl_multi)); if(!multi)
if(multi) {
memset(multi, 0, sizeof(struct Curl_multi));
multi->type = CURL_MULTI_HANDLE;
}
else
return NULL; return NULL;
multi->type = CURL_MULTI_HANDLE;
multi->hostcache = Curl_mk_dnscache(); multi->hostcache = Curl_mk_dnscache();
if(!multi->hostcache) { if(!multi->hostcache) {
/* failure, free mem and bail out */ /* failure, free mem and bail out */
free(multi); free(multi);
multi = NULL; return NULL;
} }
multi->sockhash = sh_init();
if(!multi->sockhash) {
/* failure, free mem and bail out */
Curl_hash_destroy(multi->hostcache);
free(multi);
return NULL;
}
return (CURLM *) multi; return (CURLM *) multi;
} }
@ -170,6 +340,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
{ {
struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy; struct Curl_one_easy *easy;
int i;
/* First, make some basic checks that the CURLM handle is a good handle */ /* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi)) if(!GOOD_MULTI_HANDLE(multi))
@ -180,12 +351,12 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
return CURLM_BAD_EASY_HANDLE; return CURLM_BAD_EASY_HANDLE;
/* Now, time to add an easy handle to the multi stack */ /* Now, time to add an easy handle to the multi stack */
easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy)); easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1);
if(!easy) if(!easy)
return CURLM_OUT_OF_MEMORY; return CURLM_OUT_OF_MEMORY;
/* clean it all first (just to be sure) */ for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
memset(easy, 0, sizeof(struct Curl_one_easy)); easy->sockstate.socks[i] = CURL_SOCKET_BAD;
/* set the easy handle */ /* set the easy handle */
easy->easy_handle = easy_handle; easy->easy_handle = easy_handle;
@ -209,6 +380,9 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
Curl_easy_addmulti(easy_handle, multi_handle); Curl_easy_addmulti(easy_handle, multi_handle);
/* make the SessionHandle struct refer back to this struct */
easy->easy_handle->set.one_easy = easy;
/* increase the node-counter */ /* increase the node-counter */
multi->num_easy++; multi->num_easy++;
@ -257,6 +431,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
if(easy->next) if(easy->next)
easy->next->prev = easy->prev; easy->next->prev = easy->prev;
easy->easy_handle->set.one_easy = NULL; /* detached */
/* NOTE NOTE NOTE /* NOTE NOTE NOTE
We do not touch the easy handle here! */ We do not touch the easy handle here! */
if (easy->msg) if (easy->msg)
@ -271,6 +447,65 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
return CURLM_BAD_EASY_HANDLE; /* twasn't found */ return CURLM_BAD_EASY_HANDLE; /* twasn't found */
} }
static int waitconnect_getsock(struct connectdata *conn,
curl_socket_t *sock,
int numsocks)
{
if(!numsocks)
return GETSOCK_BLANK;
sock[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_WRITESOCK(0);
}
static int domore_getsock(struct connectdata *conn,
curl_socket_t *sock,
int numsocks)
{
if(!numsocks)
return GETSOCK_BLANK;
/* When in DO_MORE state, we could be either waiting for us
to connect to a remote site, or we could wait for that site
to connect to us. It makes a difference in the way: if we
connect to the site we wait for the socket to become writable, if
the site connects to us we wait for it to become readable */
sock[0] = conn->sock[SECONDARYSOCKET];
return GETSOCK_WRITESOCK(0);
}
/* returns bitmapped flags for this handle and its sockets */
static int multi_getsock(struct Curl_one_easy *easy,
curl_socket_t *socks, /* points to numsocks number
of sockets */
int numsocks)
{
switch(easy->state) {
default:
return 0;
case CURLM_STATE_WAITRESOLVE:
return Curl_resolv_getsock(easy->easy_conn, socks, numsocks);
case CURLM_STATE_PROTOCONNECT:
return Curl_protocol_getsock(easy->easy_conn, socks, numsocks);
case CURLM_STATE_DOING:
return Curl_doing_getsock(easy->easy_conn, socks, numsocks);
case CURLM_STATE_WAITCONNECT:
return waitconnect_getsock(easy->easy_conn, socks, numsocks);
case CURLM_STATE_DO_MORE:
return domore_getsock(easy->easy_conn, socks, numsocks);
case CURLM_STATE_PERFORM:
return Curl_single_getsock(easy->easy_conn, socks, numsocks);
}
}
CURLMcode curl_multi_fdset(CURLM *multi_handle, CURLMcode curl_multi_fdset(CURLM *multi_handle,
fd_set *read_fd_set, fd_set *write_fd_set, fd_set *read_fd_set, fd_set *write_fd_set,
fd_set *exc_fd_set, int *max_fd) fd_set *exc_fd_set, int *max_fd)
@ -281,104 +516,58 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy; struct Curl_one_easy *easy;
int this_max_fd=-1; int this_max_fd=-1;
curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
int bitmap;
int i;
(void)exc_fd_set; /* not used */
if(!GOOD_MULTI_HANDLE(multi)) if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE; return CURLM_BAD_HANDLE;
*max_fd = -1; /* so far none! */
easy=multi->easy.next; easy=multi->easy.next;
while(easy) { while(easy) {
switch(easy->state) { bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
default:
break;
case CURLM_STATE_WAITRESOLVE:
/* waiting for a resolve to complete */
Curl_resolv_fdset(easy->easy_conn, read_fd_set, write_fd_set,
&this_max_fd);
if(this_max_fd > *max_fd)
*max_fd = this_max_fd;
break;
case CURLM_STATE_PROTOCONNECT: for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
Curl_protocol_fdset(easy->easy_conn, read_fd_set, write_fd_set, curl_socket_t s = CURL_SOCKET_BAD;
&this_max_fd);
if(this_max_fd > *max_fd)
*max_fd = this_max_fd;
break;
case CURLM_STATE_DOING: if(bitmap & GETSOCK_READSOCK(i)) {
Curl_doing_fdset(easy->easy_conn, read_fd_set, write_fd_set, FD_SET(sockbunch[i], read_fd_set);
&this_max_fd); s = sockbunch[i];
if(this_max_fd > *max_fd)
*max_fd = this_max_fd;
break;
case CURLM_STATE_WAITCONNECT:
case CURLM_STATE_DO_MORE:
{
/* when we're waiting for a connect, we wait for the socket to
become writable */
struct connectdata *conn = easy->easy_conn;
curl_socket_t sockfd;
if(CURLM_STATE_WAITCONNECT == easy->state) {
sockfd = conn->sock[FIRSTSOCKET];
FD_SET(sockfd, write_fd_set);
} }
if(bitmap & GETSOCK_WRITESOCK(i)) {
FD_SET(sockbunch[i], write_fd_set);
s = sockbunch[i];
}
if(s == CURL_SOCKET_BAD)
/* this socket is unused, break out of loop */
break;
else { else {
/* When in DO_MORE state, we could be either waiting for us if(s > this_max_fd)
to connect to a remote site, or we could wait for that site this_max_fd = s;
to connect to us. It makes a difference in the way: if we }
connect to the site we wait for the socket to become writable, if
the site connects to us we wait for it to become readable */
sockfd = conn->sock[SECONDARYSOCKET];
FD_SET(sockfd, write_fd_set);
} }
if((int)sockfd > *max_fd)
*max_fd = (int)sockfd;
}
break;
case CURLM_STATE_PERFORM:
/* This should have a set of file descriptors for us to set. */
/* after the transfer is done, go DONE */
Curl_single_fdset(easy->easy_conn,
read_fd_set, write_fd_set,
exc_fd_set, &this_max_fd);
/* remember the maximum file descriptor */
if(this_max_fd > *max_fd)
*max_fd = this_max_fd;
break;
}
easy = easy->next; /* check next handle */ easy = easy->next; /* check next handle */
} }
*max_fd = this_max_fd;
return CURLM_OK; return CURLM_OK;
} }
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct Curl_one_easy *easy,
int *running_handles)
{ {
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
bool done;
CURLMcode result=CURLM_OK;
struct Curl_message *msg = NULL; struct Curl_message *msg = NULL;
bool connected; bool connected;
bool async; bool async;
bool protocol_connect; bool protocol_connect;
bool dophase_done; bool dophase_done;
bool done;
CURLMcode result = CURLM_OK;
*running_handles = 0; /* bump this once for every living handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
easy=multi->easy.next;
while(easy) {
do { do {
if (CURLM_STATE_WAITCONNECT <= easy->state && if (CURLM_STATE_WAITCONNECT <= easy->state &&
easy->state <= CURLM_STATE_DO && easy->state <= CURLM_STATE_DO &&
@ -731,10 +920,45 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
multi->num_msgs++; /* increase message counter */ multi->num_msgs++; /* increase message counter */
} }
return result;
}
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
CURLMcode returncode=CURLM_OK;
struct Curl_tree *t;
*running_handles = 0; /* bump this once for every living handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
easy=multi->easy.next;
while(easy) {
CURLMcode result = multi_runsingle(multi, easy, running_handles);
if(result)
returncode = result;
easy = easy->next; /* operate on next handle */ easy = easy->next; /* operate on next handle */
} }
return result; /*
* Simply remove all expired timers from the splay since handles are dealt
* with unconditionally by this function and curl_multi_timeout() requires
* that already passed/handled expire times are removed from the splay.
*/
do {
struct timeval now = Curl_tvnow();
int key = now.tv_sec; /* drop the usec part */
multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
} while(t);
return returncode;
} }
/* This is called when an easy handle is cleanup'ed that is part of a multi /* This is called when an easy handle is cleanup'ed that is part of a multi
@ -753,6 +977,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
if(GOOD_MULTI_HANDLE(multi)) { if(GOOD_MULTI_HANDLE(multi)) {
multi->type = 0; /* not good anymore */ multi->type = 0; /* not good anymore */
Curl_hash_destroy(multi->hostcache); Curl_hash_destroy(multi->hostcache);
Curl_hash_destroy(multi->sockhash);
/* remove all easy handles */ /* remove all easy handles */
easy = multi->easy.next; easy = multi->easy.next;
@ -807,3 +1032,311 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
else else
return NULL; return NULL;
} }
/*
* Check what sockets we deal with and their "action state" and if we have a
* difference from last time we call the callback accordingly.
*/
static void singlesocket(struct Curl_multi *multi,
struct Curl_one_easy *easy)
{
struct socketstate current;
int i;
memset(&current, 0, sizeof(current));
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
current.socks[i] = CURL_SOCKET_BAD;
/* first fill in the 'current' struct with the state as it is now */
current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE);
/* when filled in, we compare with the previous round's state */
if(memcmp(&current, &easy->sockstate, sizeof(struct socketstate))) {
/* difference, call the callback once for every socket change ! */
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
int action;
curl_socket_t s = current.socks[i];
/* Ok, this approach is probably too naive and simple-minded but
it might work for a start */
if((easy->sockstate.socks[i] == CURL_SOCKET_BAD) &&
(s == CURL_SOCKET_BAD)) {
/* no socket now and there was no socket before */
break;
}
if(s == CURL_SOCKET_BAD) {
/* socket is removed */
action = CURL_POLL_REMOVE;
s = easy->sockstate.socks[i]; /* this is the removed socket */
}
else {
if(easy->sockstate.socks[i] == s) {
/* still the same socket, but are we waiting for the same actions? */
unsigned int curr;
unsigned int prev;
/* the current read/write bits for this particular socket */
curr = current.action & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i));
/* the previous read/write bits for this particular socket */
prev = easy->sockstate.action &
(GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i));
if(curr == prev)
continue;
}
action = (current.action & GETSOCK_READSOCK(i)?CURL_POLL_IN:0) |
(current.action & GETSOCK_WRITESOCK(i)?CURL_POLL_OUT:0);
}
/* call the callback with this new info */
if(multi->socket_cb) {
multi->socket_cb(easy->easy_handle,
s,
action,
multi->socket_userp);
}
/* Update the sockhash accordingly */
if(action == CURL_POLL_REMOVE)
/* remove from hash for this easy handle */
sh_delentry(multi->sockhash, s, easy->easy_handle);
else
/* make sure this socket is present in the hash for this handle */
sh_addentry(multi->sockhash, s, easy->easy_handle);
}
/* copy the current state to the storage area */
memcpy(&easy->sockstate, &current, sizeof(struct socketstate));
}
else {
/* identical, nothing new happened so we don't do any callbacks */
}
}
static CURLMcode multi_socket(struct Curl_multi *multi,
bool checkall,
curl_socket_t s)
{
CURLMcode result = CURLM_OK;
int running_handles;
struct SessionHandle *data = NULL;
struct Curl_tree *t;
if(checkall) {
struct Curl_one_easy *easyp;
result = curl_multi_perform(multi, &running_handles);
/* walk through each easy handle and do the socket state change magic
and callbacks */
easyp=multi->easy.next;
while(easyp) {
singlesocket(multi, easyp);
easyp = easyp->next;
}
return result;
}
else if (s != CURL_SOCKET_TIMEOUT) {
struct Curl_sh_entry *entry =
Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
if(!entry)
/* unmatched socket, major problemo! */
return CURLM_BAD_SOCKET; /* better return code? */
/* Now, there is potentially a chain of easy handles in this hash
entry struct and we need to deal with all of them */
do {
data = entry->easy;
result = multi_runsingle(multi, data->set.one_easy, &running_handles);
if(result == CURLM_OK)
/* get the socket(s) and check if the state has been changed since
last */
singlesocket(multi, data->set.one_easy);
entry = entry->next;
} while(entry);
return result;
}
/*
* The loop following here will go on as long as there are expire-times left
* to process in the splay and 'data' will be re-assigned for every expired
* handle we deal with.
*/
do {
int key;
struct timeval now;
/* the first loop lap 'data' can be NULL */
if(data) {
result = multi_runsingle(multi, data->set.one_easy, &running_handles);
if(result == CURLM_OK)
/* get the socket(s) and check if the state has been changed since
last */
singlesocket(multi, data->set.one_easy);
}
/* Check if there's one (more) expired timer to deal with! This function
extracts a matching node if there is one */
now = Curl_tvnow();
key = now.tv_sec; /* drop the usec part */
multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
if(t)
data = t->payload;
} while(t);
return result;
}
CURLMcode curl_multi_setopt(CURLM *multi_handle,
CURLMoption option, ...)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
CURLMcode res = CURLM_OK;
va_list param;
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
va_start(param, option);
switch(option) {
case CURLMOPT_SOCKETFUNCTION:
multi->socket_cb = va_arg(param, curl_socket_callback);
break;
case CURLMOPT_SOCKETDATA:
multi->socket_userp = va_arg(param, void *);
break;
default:
res = CURLM_UNKNOWN_OPTION;
}
va_end(param);
return res;
}
CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s)
{
#if 0
printf("multi_socket(%d)\n", (int)s);
#endif
return multi_socket((struct Curl_multi *)multi_handle, FALSE, s);
}
CURLMcode curl_multi_socket_all(CURLM *multi_handle)
{
return multi_socket((struct Curl_multi *)multi_handle,
TRUE, CURL_SOCKET_BAD);
}
CURLMcode curl_multi_timeout(CURLM *multi_handle,
long *timeout_ms)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
if(multi->timetree) {
/* we have a tree of expire times */
struct timeval now = Curl_tvnow();
/* splay the lowest to the bottom */
multi->timetree = Curl_splay(0, multi->timetree);
/* At least currently, the splay key is a time_t for the expire time */
*timeout_ms = (multi->timetree->key - now.tv_sec) * 1000 -
now.tv_usec/1000;
if(*timeout_ms < 0)
/* 0 means immediately */
*timeout_ms = 0;
}
else
*timeout_ms = -1;
return CURLM_OK;
}
/* given a number of milliseconds from now to use to set the 'act before
this'-time for the transfer, to be extracted by curl_multi_timeout() */
void Curl_expire(struct SessionHandle *data, long milli)
{
struct Curl_multi *multi = data->multi;
struct timeval *nowp = &data->state.expiretime;
/* this is only interesting for multi-interface using libcurl, and only
while there is still a multi interface struct remaining! */
if(!multi)
return;
if(!milli) {
/* No timeout, clear the time data. */
if(nowp->tv_sec) {
/* Since this is an cleared time, we must remove the previous entry from
the splay tree */
multi->timetree = Curl_splayremovebyaddr(multi->timetree,
&data->state.timenode);
infof(data, "Expire cleared\n");
}
nowp->tv_sec = nowp->tv_usec = 0;
}
else {
struct timeval set;
int rest;
set = Curl_tvnow();
set.tv_sec += milli/1000;
set.tv_usec += (milli%1000)*1000;
rest = (int)(set.tv_usec - 1000000);
if(rest > 0) {
/* bigger than a full microsec */
set.tv_sec++;
set.tv_usec -= 1000000;
}
if(nowp->tv_sec) {
/* compare if the new time is earlier, and only set it if so */
long diff = curlx_tvdiff(set, *nowp);
if(diff > 0)
/* the new expire time was later so we don't change this */
return;
/* Since this is an updated time, we must remove the previous entry from
the splay tree first and then re-add the new value */
multi->timetree = Curl_splayremovebyaddr(multi->timetree,
&data->state.timenode);
}
*nowp = set;
infof(data, "Expire at %ld / %ld (%ldms)\n",
(long)nowp->tv_sec, (long)nowp->tv_usec, milli);
data->state.timenode.payload = data;
multi->timetree = Curl_splayinsert((int)nowp->tv_sec,
multi->timetree,
&data->state.timenode);
}
#if 0
Curl_splayprint(multi->timetree, 0, TRUE);
#endif
}

View File

@ -26,5 +26,19 @@
/* /*
* Prototypes for library-wide functions provided by multi.c * Prototypes for library-wide functions provided by multi.c
*/ */
void Curl_expire(struct SessionHandle *data, long milli);
void Curl_multi_rmeasy(void *multi, CURL *data); void Curl_multi_rmeasy(void *multi, CURL *data);
/* the write bits start at bit 16 for the *getsock() bitmap */
#define GETSOCK_WRITEBITSTART 16
#define GETSOCK_BLANK 0 /* no bits set */
/* set the bit for the given sock number to make the bitmap for writable */
#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x)))
/* set the bit for the given sock number to make the bitmap for readable */
#define GETSOCK_READSOCK(x) (1 << (x))
#endif /* __MULTIIF_H */ #endif /* __MULTIIF_H */

View File

@ -29,6 +29,7 @@
#include <curl/curl.h> #include <curl/curl.h>
#include "urldata.h" #include "urldata.h"
#include "sendf.h" #include "sendf.h"
#include "multiif.h"
#include "speedcheck.h" #include "speedcheck.h"
void Curl_speedinit(struct SessionHandle *data) void Curl_speedinit(struct SessionHandle *data)
@ -43,13 +44,13 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
data->set.low_speed_time && data->set.low_speed_time &&
(Curl_tvlong(data->state.keeps_speed) != 0) && (Curl_tvlong(data->state.keeps_speed) != 0) &&
(data->progress.current_speed < data->set.low_speed_limit)) { (data->progress.current_speed < data->set.low_speed_limit)) {
long howlong = Curl_tvdiff(now, data->state.keeps_speed);
/* We are now below the "low speed limit". If we are below it /* We are now below the "low speed limit". If we are below it
for "low speed time" seconds we consider that enough reason for "low speed time" seconds we consider that enough reason
to abort the download. */ to abort the download. */
if( (Curl_tvdiff(now, data->state.keeps_speed)/1000) > if( (howlong/1000) > data->set.low_speed_time) {
data->set.low_speed_time) {
/* we have been this slow for long enough, now die */ /* we have been this slow for long enough, now die */
failf(data, failf(data,
"Operation too slow. " "Operation too slow. "
@ -58,6 +59,7 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
data->set.low_speed_time); data->set.low_speed_time);
return CURLE_OPERATION_TIMEOUTED; return CURLE_OPERATION_TIMEOUTED;
} }
Curl_expire(data, howlong);
} }
else { else {
/* we keep up the required speed all right */ /* we keep up the required speed all right */

406
lib/splay.c Normal file
View File

@ -0,0 +1,406 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1997 - 2006, 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
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "splay.h"
#define compare(i,j) ((i)-(j))
/* Set this to a key value that will *NEVER* appear otherwise */
#define KEY_NOTUSED -1
/*
* Splay using the key i (which may or may not be in the tree.) The starting
* root is t.
*/
struct Curl_tree *Curl_splay(int i, struct Curl_tree *t)
{
struct Curl_tree N, *l, *r, *y;
int comp;
if (t == NULL)
return t;
N.smaller = N.larger = NULL;
l = r = &N;
for (;;) {
comp = compare(i, t->key);
if (comp < 0) {
if (t->smaller == NULL)
break;
if (compare(i, t->smaller->key) < 0) {
y = t->smaller; /* rotate smaller */
t->smaller = y->larger;
y->larger = t;
t = y;
if (t->smaller == NULL)
break;
}
r->smaller = t; /* link smaller */
r = t;
t = t->smaller;
}
else if (comp > 0) {
if (t->larger == NULL)
break;
if (compare(i, t->larger->key) > 0) {
y = t->larger; /* rotate larger */
t->larger = y->smaller;
y->smaller = t;
t = y;
if (t->larger == NULL)
break;
}
l->larger = t; /* link larger */
l = t;
t = t->larger;
}
else {
break;
}
}
l->larger = r->smaller = NULL;
l->larger = t->smaller; /* assemble */
r->smaller = t->larger;
t->smaller = N.larger;
t->larger = N.smaller;
return t;
}
/* Insert key i into the tree t. Return a pointer to the resulting tree or
NULL if something went wrong. */
struct Curl_tree *Curl_splayinsert(int i, struct Curl_tree *t,
struct Curl_tree *area)
{
if (area == NULL)
return t;
if (t != NULL) {
t = Curl_splay(i,t);
if (compare(i, t->key)==0) {
/* it already exists one of this size */
area->same = t;
area->key = i;
area->smaller = t->smaller;
area->larger = t->larger;
t->smaller = area;
t->key = KEY_NOTUSED;
return area; /* new root node */
}
}
if (t == NULL) {
area->smaller = area->larger = NULL;
}
else if (compare(i, t->key) < 0) {
area->smaller = t->smaller;
area->larger = t;
t->smaller = NULL;
}
else {
area->larger = t->larger;
area->smaller = t;
t->larger = NULL;
}
area->key = i;
area->same = NULL; /* no identical node (yet) */
return area;
}
/* Deletes 'i' from the tree if it's there (with an exact match). Returns a
pointer to the resulting tree. */
struct Curl_tree *Curl_splayremove(int i, struct Curl_tree *t,
struct Curl_tree **removed)
{
struct Curl_tree *x;
if (t==NULL)
return NULL;
t = Curl_splay(i,t);
if (compare(i, t->key) == 0) { /* found it */
/* FIRST! Check if there is a list with identical sizes */
if((x = t->same)) {
/* there is, pick one from the list */
/* 'x' is the new root node */
x->key = t->key;
x->larger = t->larger;
x->smaller = t->smaller;
*removed = t;
return x; /* new root */
}
if (t->smaller == NULL) {
x = t->larger;
}
else {
x = Curl_splay(i, t->smaller);
x->larger = t->larger;
}
*removed = t;
return x;
}
else {
*removed = NULL; /* no match */
return t; /* It wasn't there */
}
}
/* Finds and deletes the best-fit node from the tree. Return a pointer to the
resulting tree. best-fit means the node with the given or lower number */
struct Curl_tree *Curl_splaygetbest(int i, struct Curl_tree *t,
struct Curl_tree **removed)
{
struct Curl_tree *x;
if (!t) {
*removed = NULL; /* none removed since there was no root */
return NULL;
}
t = Curl_splay(i,t);
if(compare(i, t->key) < 0) {
/* too big node, try the smaller chain */
if(t->smaller)
t=Curl_splay(t->smaller->key, t);
else {
/* fail */
*removed = NULL;
return t;
}
}
if (compare(i, t->key) >= 0) { /* found it */
/* FIRST! Check if there is a list with identical sizes */
x = t->same;
if(x) {
/* there is, pick one from the list */
/* 'x' is the new root node */
x->key = t->key;
x->larger = t->larger;
x->smaller = t->smaller;
*removed = t;
return x; /* new root */
}
if (t->smaller == NULL) {
x = t->larger;
}
else {
x = Curl_splay(i, t->smaller);
x->larger = t->larger;
}
*removed = t;
return x;
}
else {
*removed = NULL; /* no match */
return t; /* It wasn't there */
}
}
/* Deletes the node we point out from the tree if it's there. Return a pointer
to the resulting tree. */
struct Curl_tree *Curl_splayremovebyaddr(struct Curl_tree *t,
struct Curl_tree *remove)
{
struct Curl_tree *x;
if (!t || !remove)
return NULL;
if(KEY_NOTUSED == remove->key) {
/* just unlink ourselves nice and quickly: */
remove->smaller->same = remove->same;
if(remove->same)
remove->same->smaller = remove->smaller;
/* voila, we're done! */
return t;
}
t = Curl_splay(remove->key, t);
/* Check if there is a list with identical sizes */
x = t->same;
if(x) {
/* 'x' is the new root node */
x->key = t->key;
x->larger = t->larger;
x->smaller = t->smaller;
return x; /* new root */
}
/* Remove the actualy root node: */
if (t->smaller == NULL)
x = t->larger;
else {
x = Curl_splay(remove->key, t->smaller);
x->larger = t->larger;
}
return x;
}
#ifdef CURLDEBUG
int Curl_splayprint(struct Curl_tree * t, int d, char output)
{
int distance=0;
struct Curl_tree *node;
int i;
if (t == NULL)
return 0;
distance += Curl_splayprint(t->larger, d+1, output);
for (i=0; i<d; i++)
if(output)
printf(" ");
if(output) {
printf("%d[%d]", t->key, i);
}
for(node = t->same; node; node = node->same) {
distance += i; /* this has the same "virtual" distance */
if(output)
printf(" [+]");
}
if(output)
puts("");
distance += i;
distance += Curl_splayprint(t->smaller, d+1, output);
return distance;
}
#endif
#ifdef TEST_SPLAY
/*#define TEST2 */
#define MAX 50
#define OUTPUT 0 /* 1 enables, 0 disables */
/* A sample use of these functions. Start with the empty tree, insert some
stuff into it, and then delete it */
int main(int argc, char **argv)
{
struct Curl_tree *root, *t;
void *ptrs[MAX];
long sizes[]={
50, 60, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200, 300,
220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200,
300, 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120,
200, 300, 220, 80, 90};
int i;
root = NULL; /* the empty tree */
for (i = 0; i < MAX; i++) {
ptrs[i] = t = (struct Curl_tree *)malloc(sizeof(struct Curl_tree));
if(!t) {
puts("out of memory!");
return 0;
}
#ifdef TEST2
root = Curl_splayinsert(sizes[i], root, t);
#else
root = Curl_splayinsert((541*i)&1023, root, t);
#endif
}
#if 0
puts("Result:");
printtree(root, 0, 1);
#endif
#if 1
for (i=0; root; i+=30) {
Curl_splayprint(root, 0, 1);
do {
root = Curl_splaygetbest(i, root, &t);
if(t)
printf("bestfit %d became %d\n", i, t->key);
else
printf("bestfit %d failed!\n", i);
} while(t && root);
}
#endif
#if 0
for (i = 0; i < MAX; i++) {
printf("remove pointer %d size %d\n", i, sizes[i]);
root = removebyaddr(root, (struct Curl_tree *)ptrs[i]);
Curl_splayprint(root, 0, 1);
}
#endif
#if 0
#ifdef WEIGHT
for (i = -1; i<=root->weight; i++) {
t = find_rank(i, root);
if (t == NULL) {
printf("could not find a node of rank %d.\n", i);
} else {
printf("%d is of rank %d\n", t->key, i);
}
}
#endif
#endif
#if 0
#ifdef TEST2
for (i = 0; i < MAX; i++) {
printf("remove size %d\n", sizes[i]);
root = Curl_splayremove(sizes[i], root, &t);
free(t);
Curl_splayprint(root, 0, 1);
}
#endif
#endif
return 0;
}
#endif /* TEST_SPLAY */

50
lib/splay.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef __SPLAY_H
#define __SPLAY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1997 - 2006, 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
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
struct Curl_tree {
struct Curl_tree *smaller; /* smaller node */
struct Curl_tree *larger; /* larger node */
struct Curl_tree *same; /* points to a node with identical key */
int key; /* the "sort" key */
void *payload; /* data the splay code doesn't care about */
};
struct Curl_tree *Curl_splay(int i, struct Curl_tree *t);
struct Curl_tree *Curl_splayinsert(int key, struct Curl_tree *t,
struct Curl_tree *new);
struct Curl_tree *Curl_splayremove(int key, struct Curl_tree *t,
struct Curl_tree **removed);
struct Curl_tree *Curl_splaygetbest(int key, struct Curl_tree *t,
struct Curl_tree **removed);
struct Curl_tree *Curl_splayremovebyaddr(struct Curl_tree *t,
struct Curl_tree *remove);
#ifdef CURLDEBUG
int Curl_splayprint(struct Curl_tree * t, int d, char output);
#else
#define Curl_splayprint(x,y,z)
#endif
#endif

View File

@ -329,6 +329,12 @@ curl_multi_strerror(CURLMcode error)
case CURLM_INTERNAL_ERROR: case CURLM_INTERNAL_ERROR:
return "internal error"; return "internal error";
case CURLM_BAD_SOCKET:
return "invalid socket argument";
case CURLM_UNKNOWN_OPTION:
return "unknown option";
case CURLM_LAST: case CURLM_LAST:
break; break;
} }

View File

@ -101,6 +101,7 @@
#include "share.h" #include "share.h"
#include "memory.h" #include "memory.h"
#include "select.h" #include "select.h"
#include "multiif.h"
#include "easyif.h" /* for Curl_convert_to_network prototype */ #include "easyif.h" /* for Curl_convert_to_network prototype */
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
@ -1522,34 +1523,42 @@ CURLcode Curl_readwrite_init(struct connectdata *conn)
} }
/* /*
* Curl_single_fdset() gets called by the multi interface code when the app * Curl_single_getsock() gets called by the multi interface code when the app
* has requested to get the fd_sets for the current connection. This function * has requested to get the sockets for the current connection. This function
* will then be called once for every connection that the multi interface * will then be called once for every connection that the multi interface
* keeps track of. This function will only be called for connections that are * keeps track of. This function will only be called for connections that are
* in the proper state to have this information available. * in the proper state to have this information available.
*/ */
void Curl_single_fdset(struct connectdata *conn, int Curl_single_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *sock, /* points to numsocks number
fd_set *write_fd_set, of sockets */
fd_set *exc_fd_set, int numsocks)
int *max_fd)
{ {
*max_fd = -1; /* init */ int bitmap = GETSOCK_BLANK;
int index = 0;
if(numsocks < 2)
/* simple check but we might need two slots */
return GETSOCK_BLANK;
if(conn->keep.keepon & KEEP_READ) { if(conn->keep.keepon & KEEP_READ) {
FD_SET(conn->sockfd, read_fd_set); bitmap |= GETSOCK_READSOCK(index);
*max_fd = (int)conn->sockfd; sock[index] = conn->sockfd;
} }
if(conn->keep.keepon & KEEP_WRITE) { if(conn->keep.keepon & KEEP_WRITE) {
FD_SET(conn->writesockfd, write_fd_set);
/* since sockets are curl_socket_t nowadays, we typecast it to int here if((conn->sockfd != conn->writesockfd) &&
to compare it nicely */ (conn->keep.keepon & KEEP_READ)) {
if((int)conn->writesockfd > *max_fd) /* only if they are not the same socket and we had a readable one,
*max_fd = (int)conn->writesockfd; we increase index */
index++;
sock[index] = conn->writesockfd;
} }
/* we don't use exceptions, only touch that one to prevent compiler
warnings! */ bitmap |= GETSOCK_WRITESOCK(index);
*exc_fd_set = *exc_fd_set; }
return bitmap;
} }

View File

@ -28,11 +28,9 @@ CURLcode Curl_second_connect(struct connectdata *conn);
CURLcode Curl_posttransfer(struct SessionHandle *data); CURLcode Curl_posttransfer(struct SessionHandle *data);
CURLcode Curl_follow(struct SessionHandle *data, char *newurl, bool retry); CURLcode Curl_follow(struct SessionHandle *data, char *newurl, bool retry);
CURLcode Curl_readwrite(struct connectdata *conn, bool *done); CURLcode Curl_readwrite(struct connectdata *conn, bool *done);
void Curl_single_fdset(struct connectdata *conn, int Curl_single_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks);
fd_set *exc_fd_set,
int *max_fd);
CURLcode Curl_readwrite_init(struct connectdata *conn); CURLcode Curl_readwrite_init(struct connectdata *conn);
CURLcode Curl_readrewind(struct connectdata *conn); CURLcode Curl_readrewind(struct connectdata *conn);
CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp); CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp);

View File

@ -1552,6 +1552,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
NULL, Curl_scan_cache_used); NULL, Curl_scan_cache_used);
#endif #endif
Curl_expire(data, 0); /* shut off timers */
Curl_hostcache_prune(data); /* kill old DNS cache entries */ Curl_hostcache_prune(data); /* kill old DNS cache entries */
/* /*
@ -2318,26 +2319,22 @@ static void verboseconnect(struct connectdata *conn)
conn->ip_addr_str, conn->port); conn->ip_addr_str, conn->port);
} }
CURLcode Curl_protocol_fdset(struct connectdata *conn, int Curl_protocol_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks)
int *max_fdp)
{ {
CURLcode res = CURLE_OK; if(conn->curl_proto_getsock)
if(conn->curl_proto_fdset) return conn->curl_proto_getsock(conn, socks, numsocks);
res = conn->curl_proto_fdset(conn, read_fd_set, write_fd_set, max_fdp); return GETSOCK_BLANK;
return res;
} }
CURLcode Curl_doing_fdset(struct connectdata *conn, int Curl_doing_getsock(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks)
int *max_fdp)
{ {
CURLcode res = CURLE_OK; if(conn && conn->curl_doing_getsock)
if(conn && conn->curl_doing_fdset) return conn->curl_doing_getsock(conn, socks, numsocks);
res = conn->curl_doing_fdset(conn, read_fd_set, write_fd_set, max_fdp); return GETSOCK_BLANK;
return res;
} }
/* /*
@ -3034,7 +3031,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_done = Curl_http_done; conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect; conn->curl_connect = Curl_http_connect;
conn->curl_connecting = Curl_https_connecting; conn->curl_connecting = Curl_https_connecting;
conn->curl_proto_fdset = Curl_https_proto_fdset; conn->curl_proto_getsock = Curl_https_getsock;
#else /* USE_SS */ #else /* USE_SS */
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME
@ -3086,8 +3083,8 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_connect = Curl_ftp_connect; conn->curl_connect = Curl_ftp_connect;
conn->curl_connecting = Curl_ftp_multi_statemach; conn->curl_connecting = Curl_ftp_multi_statemach;
conn->curl_doing = Curl_ftp_doing; conn->curl_doing = Curl_ftp_doing;
conn->curl_proto_fdset = Curl_ftp_fdset; conn->curl_proto_getsock = Curl_ftp_getsock;
conn->curl_doing_fdset = Curl_ftp_fdset; conn->curl_doing_getsock = Curl_ftp_getsock;
conn->curl_disconnect = Curl_ftp_disconnect; conn->curl_disconnect = Curl_ftp_disconnect;
} }
@ -4027,6 +4024,8 @@ CURLcode Curl_done(struct connectdata **connp,
struct connectdata *conn = *connp; struct connectdata *conn = *connp;
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
Curl_expire(data, 0); /* stop timer */
if(conn->bits.done) if(conn->bits.done)
return CURLE_OK; /* Curl_done() has already been called */ return CURLE_OK; /* Curl_done() has already been called */

View File

@ -45,6 +45,16 @@ CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done);
CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done); CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done);
CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done); CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done);
void Curl_safefree(void *ptr); void Curl_safefree(void *ptr);
int Curl_protocol_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks);
int Curl_doing_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks);
#if 0
CURLcode Curl_protocol_fdset(struct connectdata *conn, CURLcode Curl_protocol_fdset(struct connectdata *conn,
fd_set *read_fd_set, fd_set *read_fd_set,
fd_set *write_fd_set, fd_set *write_fd_set,
@ -54,3 +64,5 @@ CURLcode Curl_doing_fdset(struct connectdata *conn,
fd_set *write_fd_set, fd_set *write_fd_set,
int *max_fdp); int *max_fdp);
#endif #endif
#endif

View File

@ -96,6 +96,7 @@
#include "http_chunks.h" /* for the structs and enum stuff */ #include "http_chunks.h" /* for the structs and enum stuff */
#include "hostip.h" #include "hostip.h"
#include "hash.h" #include "hash.h"
#include "splay.h"
#ifdef HAVE_GSSAPI #ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU # ifdef HAVE_GSSGNU
@ -657,17 +658,15 @@ struct connectdata {
/* Called from the multi interface during the PROTOCONNECT phase, and it /* Called from the multi interface during the PROTOCONNECT phase, and it
should then return a proper fd set */ should then return a proper fd set */
CURLcode (*curl_proto_fdset)(struct connectdata *conn, int (*curl_proto_getsock)(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks);
int *max_fdp);
/* Called from the multi interface during the DOING phase, and it should /* Called from the multi interface during the DOING phase, and it should
then return a proper fd set */ then return a proper fd set */
CURLcode (*curl_doing_fdset)(struct connectdata *conn, int (*curl_doing_getsock)(struct connectdata *conn,
fd_set *read_fd_set, curl_socket_t *socks,
fd_set *write_fd_set, int numsocks);
int *max_fdp);
/* This function *MAY* be set to a protocol-dependent function that is run /* This function *MAY* be set to a protocol-dependent function that is run
* by the curl_disconnect(), as a step in the disconnection. * by the curl_disconnect(), as a step in the disconnection.
@ -932,10 +931,11 @@ struct UrlState {
#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
ENGINE *engine; ENGINE *engine;
#endif /* USE_SSLEAY */ #endif /* USE_SSLEAY */
struct timeval expiretime; /* set this with Curl_expire() only */
struct Curl_tree timenode; /* for the splay stuff */
/* a place to store the most recenlty set FTP entrypath */ /* a place to store the most recenlty set FTP entrypath */
char *most_recent_ftp_entrypath; char *most_recent_ftp_entrypath;
}; };
@ -968,6 +968,8 @@ struct DynamicStatic {
* 'struct UrlState' instead. The only exceptions MUST note the changes in * 'struct UrlState' instead. The only exceptions MUST note the changes in
* the 'DynamicStatic' struct. * the 'DynamicStatic' struct.
*/ */
struct Curl_one_easy; /* declared and used only in multi.c */
struct Curl_multi; /* declared and used only in multi.c */
struct UserDefined { struct UserDefined {
FILE *err; /* the stderr user data goes here */ FILE *err; /* the stderr user data goes here */
@ -1071,6 +1073,12 @@ struct UserDefined {
char *private_data; /* Private data */ char *private_data; /* Private data */
struct Curl_one_easy *one_easy; /* When adding an easy handle to a multi
handle, an internal 'Curl_one_easy'
struct is created and this is a pointer
to the particular struct associated with
this SessionHandle */
struct curl_slist *http200aliases; /* linked list of aliases for http200 */ struct curl_slist *http200aliases; /* linked list of aliases for http200 */
long ip_version; long ip_version;
@ -1139,7 +1147,7 @@ struct UserDefined {
struct SessionHandle { struct SessionHandle {
struct curl_hash *hostcache; struct curl_hash *hostcache;
void *multi; /* if non-NULL, points to the multi handle struct Curl_multi *multi; /* if non-NULL, points to the multi handle
struct of which this "belongs" */ struct of which this "belongs" */
struct Curl_share *share; /* Share, handles global variable mutexing */ struct Curl_share *share; /* Share, handles global variable mutexing */
struct UserDefined set; /* values set by the libcurl user */ struct UserDefined set; /* values set by the libcurl user */