1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-21 23:58:49 -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 \
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 \
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 \
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 \
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 \
gtls.h tftp.h sockaddr.h
gtls.h tftp.h sockaddr.h splay.h

View File

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

View File

@ -96,6 +96,7 @@
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "multiif.h"
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
#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 */
CURLcode Curl_ftp_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
int Curl_ftp_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
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) {
/* write mode */
FD_SET(sockfd, write_fd_set);
return GETSOCK_WRITESOCK(0);
}
else {
/* read mode */
FD_SET(sockfd, read_fd_set);
}
if((int)sockfd > *max_fdp)
*max_fdp = (int)sockfd;
return CURLE_OK;
return GETSOCK_READSOCK(0);
}
/* 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);
CURLcode Curl_ftp_nextconnect(struct connectdata *conn);
CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, bool *done);
CURLcode Curl_ftp_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp);
int Curl_ftp_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks);
CURLcode Curl_ftp_doing(struct connectdata *conn,
bool *dophase_done);
#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));
if(he) {
char *dup = strdup(key);
char *dup = malloc(key_len);
if(dup) {
/* copy the key */
memcpy(dup, key, key_len);
he->key = dup;
he->key_len = key_len;
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 */
}
/* 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 *
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_llist *l = FETCH_LIST(h, key, key_len);
for (le = l->head;
le;
le = le->next) {
for (le = l->head; le; le = le->next) {
he = le->ptr;
if (hash_key_compare(he->key, he->key_len, key, key_len)) {
return he->ptr;

View File

@ -105,17 +105,15 @@
* Returns: CURLE_OK always!
*/
CURLcode Curl_resolv_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
int Curl_resolv_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
int max = ares_fds(conn->data->state.areschannel,
read_fd_set, write_fd_set);
*max_fdp = max;
int max = ares_getsock(conn->data->state.areschannel,
(int *)socks, numsocks);
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,
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
depending on what name resolve technology we've built to use. The 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 *write_fd_set,
int *max_fdp);
#endif
/* 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: */
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
*/
CURLcode Curl_resolv_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
int Curl_resolv_getsock(struct connectdata *conn,
curl_socket_t *sock,
int numsocks)
{
(void)conn;
(void)read_fd_set;
(void)write_fd_set;
(void)max_fdp;
(void)sock;
(void)numsocks;
return CURLE_OK;
return 0; /* no bits since we don't use any socks */
}
#endif /* truly sync */

View File

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

View File

@ -46,6 +46,7 @@
#include "easyif.h"
#include "multiif.h"
#include "sendf.h"
#include "timeval.h"
/* The last #include file should be: */
#include "memdebug.h"
@ -73,6 +74,18 @@ typedef enum {
CURLM_STATE_LAST /* not a true state, never use this */
} 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 {
/* first, two fields for the linked list of these */
struct Curl_one_easy *next;
@ -90,6 +103,8 @@ struct Curl_one_easy {
will be deleted when this handle is removed
from the multi-handle */
int msg_num; /* number of messages left in 'msg' to return */
struct socketstate sockstate; /* for the socket API magic */
};
#define CURL_MULTI_HANDLE 0x000bab1e
@ -111,8 +126,21 @@ struct Curl_multi {
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 */
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 */
@ -134,6 +162,7 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
};
CURLMstate oldstate = easy->state;
#endif
easy->state = state;
#ifdef CURLDEBUG
@ -143,25 +172,166 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
#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)
{
struct Curl_multi *multi;
struct Curl_multi *multi = (void *)calloc(sizeof(struct Curl_multi), 1);
multi = (void *)malloc(sizeof(struct Curl_multi));
if(multi) {
memset(multi, 0, sizeof(struct Curl_multi));
multi->type = CURL_MULTI_HANDLE;
}
else
if(!multi)
return NULL;
multi->type = CURL_MULTI_HANDLE;
multi->hostcache = Curl_mk_dnscache();
if(!multi->hostcache) {
/* failure, free mem and bail out */
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;
}
@ -170,6 +340,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
int i;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
@ -180,12 +351,12 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
return CURLM_BAD_EASY_HANDLE;
/* 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)
return CURLM_OUT_OF_MEMORY;
/* clean it all first (just to be sure) */
memset(easy, 0, sizeof(struct Curl_one_easy));
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
easy->sockstate.socks[i] = CURL_SOCKET_BAD;
/* set the 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);
/* make the SessionHandle struct refer back to this struct */
easy->easy_handle->set.one_easy = easy;
/* increase the node-counter */
multi->num_easy++;
@ -257,6 +431,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
if(easy->next)
easy->next->prev = easy->prev;
easy->easy_handle->set.one_easy = NULL; /* detached */
/* NOTE NOTE NOTE
We do not touch the easy handle here! */
if (easy->msg)
@ -271,6 +447,65 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
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,
fd_set *read_fd_set, fd_set *write_fd_set,
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_one_easy *easy;
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))
return CURLM_BAD_HANDLE;
*max_fd = -1; /* so far none! */
easy=multi->easy.next;
while(easy) {
switch(easy->state) {
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;
bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
case CURLM_STATE_PROTOCONNECT:
Curl_protocol_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;
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
curl_socket_t s = CURL_SOCKET_BAD;
case CURLM_STATE_DOING:
Curl_doing_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_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_READSOCK(i)) {
FD_SET(sockbunch[i], read_fd_set);
s = sockbunch[i];
}
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 {
/* 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 */
sockfd = conn->sock[SECONDARYSOCKET];
FD_SET(sockfd, write_fd_set);
if(s > this_max_fd)
this_max_fd = s;
}
}
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 */
}
*max_fd = this_max_fd;
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;
bool connected;
bool async;
bool protocol_connect;
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 {
if (CURLM_STATE_WAITCONNECT <= easy->state &&
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 */
}
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 */
}
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
@ -753,6 +977,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
if(GOOD_MULTI_HANDLE(multi)) {
multi->type = 0; /* not good anymore */
Curl_hash_destroy(multi->hostcache);
Curl_hash_destroy(multi->sockhash);
/* remove all easy handles */
easy = multi->easy.next;
@ -807,3 +1032,311 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
else
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
*/
void Curl_expire(struct SessionHandle *data, long milli);
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 */

View File

@ -29,6 +29,7 @@
#include <curl/curl.h>
#include "urldata.h"
#include "sendf.h"
#include "multiif.h"
#include "speedcheck.h"
void Curl_speedinit(struct SessionHandle *data)
@ -43,13 +44,13 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
data->set.low_speed_time &&
(Curl_tvlong(data->state.keeps_speed) != 0) &&
(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
for "low speed time" seconds we consider that enough reason
to abort the download. */
if( (Curl_tvdiff(now, data->state.keeps_speed)/1000) >
data->set.low_speed_time) {
if( (howlong/1000) > data->set.low_speed_time) {
/* we have been this slow for long enough, now die */
failf(data,
"Operation too slow. "
@ -58,6 +59,7 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
data->set.low_speed_time);
return CURLE_OPERATION_TIMEOUTED;
}
Curl_expire(data, howlong);
}
else {
/* 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:
return "internal error";
case CURLM_BAD_SOCKET:
return "invalid socket argument";
case CURLM_UNKNOWN_OPTION:
return "unknown option";
case CURLM_LAST:
break;
}

View File

@ -101,6 +101,7 @@
#include "share.h"
#include "memory.h"
#include "select.h"
#include "multiif.h"
#include "easyif.h" /* for Curl_convert_to_network prototype */
#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
* has requested to get the fd_sets for the current connection. This function
* Curl_single_getsock() gets called by the multi interface code when the app
* has requested to get the sockets for the current connection. This function
* 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
* in the proper state to have this information available.
*/
void Curl_single_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd)
int Curl_single_getsock(struct connectdata *conn,
curl_socket_t *sock, /* points to numsocks number
of sockets */
int numsocks)
{
*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) {
FD_SET(conn->sockfd, read_fd_set);
*max_fd = (int)conn->sockfd;
bitmap |= GETSOCK_READSOCK(index);
sock[index] = conn->sockfd;
}
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
to compare it nicely */
if((int)conn->writesockfd > *max_fd)
*max_fd = (int)conn->writesockfd;
if((conn->sockfd != conn->writesockfd) &&
(conn->keep.keepon & KEEP_READ)) {
/* only if they are not the same socket and we had a readable one,
we increase index */
index++;
sock[index] = conn->writesockfd;
}
/* we don't use exceptions, only touch that one to prevent compiler
warnings! */
*exc_fd_set = *exc_fd_set;
bitmap |= GETSOCK_WRITESOCK(index);
}
return bitmap;
}

View File

@ -28,11 +28,9 @@ CURLcode Curl_second_connect(struct connectdata *conn);
CURLcode Curl_posttransfer(struct SessionHandle *data);
CURLcode Curl_follow(struct SessionHandle *data, char *newurl, bool retry);
CURLcode Curl_readwrite(struct connectdata *conn, bool *done);
void Curl_single_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd);
int Curl_single_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks);
CURLcode Curl_readwrite_init(struct connectdata *conn);
CURLcode Curl_readrewind(struct connectdata *conn);
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);
#endif
Curl_expire(data, 0); /* shut off timers */
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);
}
CURLcode Curl_protocol_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
int Curl_protocol_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
CURLcode res = CURLE_OK;
if(conn->curl_proto_fdset)
res = conn->curl_proto_fdset(conn, read_fd_set, write_fd_set, max_fdp);
return res;
if(conn->curl_proto_getsock)
return conn->curl_proto_getsock(conn, socks, numsocks);
return GETSOCK_BLANK;
}
CURLcode Curl_doing_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
int Curl_doing_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
CURLcode res = CURLE_OK;
if(conn && conn->curl_doing_fdset)
res = conn->curl_doing_fdset(conn, read_fd_set, write_fd_set, max_fdp);
return res;
if(conn && conn->curl_doing_getsock)
return conn->curl_doing_getsock(conn, socks, numsocks);
return GETSOCK_BLANK;
}
/*
@ -3034,7 +3031,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
conn->curl_connecting = Curl_https_connecting;
conn->curl_proto_fdset = Curl_https_proto_fdset;
conn->curl_proto_getsock = Curl_https_getsock;
#else /* USE_SS */
failf(data, LIBCURL_NAME
@ -3086,8 +3083,8 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_connect = Curl_ftp_connect;
conn->curl_connecting = Curl_ftp_multi_statemach;
conn->curl_doing = Curl_ftp_doing;
conn->curl_proto_fdset = Curl_ftp_fdset;
conn->curl_doing_fdset = Curl_ftp_fdset;
conn->curl_proto_getsock = Curl_ftp_getsock;
conn->curl_doing_getsock = Curl_ftp_getsock;
conn->curl_disconnect = Curl_ftp_disconnect;
}
@ -4027,6 +4024,8 @@ CURLcode Curl_done(struct connectdata **connp,
struct connectdata *conn = *connp;
struct SessionHandle *data=conn->data;
Curl_expire(data, 0); /* stop timer */
if(conn->bits.done)
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_doing(struct connectdata *conn, bool *done);
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,
fd_set *read_fd_set,
fd_set *write_fd_set,
@ -54,3 +64,5 @@ CURLcode Curl_doing_fdset(struct connectdata *conn,
fd_set *write_fd_set,
int *max_fdp);
#endif
#endif

View File

@ -96,6 +96,7 @@
#include "http_chunks.h" /* for the structs and enum stuff */
#include "hostip.h"
#include "hash.h"
#include "splay.h"
#ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU
@ -657,17 +658,15 @@ struct connectdata {
/* Called from the multi interface during the PROTOCONNECT phase, and it
should then return a proper fd set */
CURLcode (*curl_proto_fdset)(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp);
int (*curl_proto_getsock)(struct connectdata *conn,
curl_socket_t *socks,
int numsocks);
/* Called from the multi interface during the DOING phase, and it should
then return a proper fd set */
CURLcode (*curl_doing_fdset)(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp);
int (*curl_doing_getsock)(struct connectdata *conn,
curl_socket_t *socks,
int numsocks);
/* This function *MAY* be set to a protocol-dependent function that is run
* 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)
ENGINE *engine;
#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 */
char *most_recent_ftp_entrypath;
};
@ -968,6 +968,8 @@ struct DynamicStatic {
* 'struct UrlState' instead. The only exceptions MUST note the changes in
* 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 {
FILE *err; /* the stderr user data goes here */
@ -1071,6 +1073,12 @@ struct UserDefined {
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 */
long ip_version;
@ -1139,7 +1147,7 @@ struct UserDefined {
struct SessionHandle {
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 Curl_share *share; /* Share, handles global variable mutexing */
struct UserDefined set; /* values set by the libcurl user */