each socket is used by exactly one easy handle, but of course each easy handle
can and will use more than one socket
This commit is contained in:
parent
6e520c4cdc
commit
0ec96e4279
@ -43,15 +43,12 @@ Implementation of the curl_multi_socket API
|
|||||||
handle conversion on its own. I find it very unlikely that applications
|
handle conversion on its own. I find it very unlikely that applications
|
||||||
would want to do that and since libcurl would need such a lookup on its own
|
would want to do that and since libcurl would need such a lookup on its own
|
||||||
anyway since we didn't want to force applications to do that translation
|
anyway since we didn't want to force applications to do that translation
|
||||||
code (it would be optional), it seemed like an unnecessary option. I also
|
code (it would be optional), it seemed like an unnecessary option.
|
||||||
realized that when we use underlying libraries such as c-ares (for DNS
|
|
||||||
asynch resolving) there might in fact be more than one transfer waiting for
|
Instead I created an internal "socket to easy handles" hash table that given
|
||||||
action on the same socket and thus it makes the lookup even tricker and even
|
a socket (file descriptor) return the easy handle that waits for action on
|
||||||
less likely to ever get done by applications. Instead I created an internal
|
that socket. This hash is made using the already existing hash code
|
||||||
"socket to easy handles" hash table that given a socket (file descriptor)
|
(previously only used for the DNS cache).
|
||||||
returns a list of easy handles that waits for some action on that socket.
|
|
||||||
This hash is made using the already existing hash code (previously only used
|
|
||||||
for the DNS cache).
|
|
||||||
|
|
||||||
To make libcurl be able to report plain sockets in the socket callback, I
|
To make libcurl be able to report plain sockets in the socket callback, I
|
||||||
had to re-organize the internals of the curl_multi_fdset() etc so that the
|
had to re-organize the internals of the curl_multi_fdset() etc so that the
|
||||||
|
117
lib/multi.c
117
lib/multi.c
@ -83,7 +83,6 @@ typedef enum {
|
|||||||
struct socketstate {
|
struct socketstate {
|
||||||
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
|
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
|
||||||
long action; /* socket action bitmap */
|
long action; /* socket action bitmap */
|
||||||
long timeout[MAX_SOCKSPEREASYHANDLE];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Curl_one_easy {
|
struct Curl_one_easy {
|
||||||
@ -173,19 +172,20 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We add one of these structs to the sockhash, and then if we add more easy
|
* We add one of these structs to the sockhash for a particular socket
|
||||||
* 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 {
|
||||||
struct Curl_sh_entry *next;
|
|
||||||
struct Curl_sh_entry *prev;
|
|
||||||
struct SessionHandle *easy;
|
struct SessionHandle *easy;
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
long inuse;
|
long inuse;
|
||||||
|
int action; /* what action READ/WRITE this socket waits for */
|
||||||
|
void *userp; /* settable by users (not yet decided exactly how) */
|
||||||
};
|
};
|
||||||
|
/* bits for 'action' having no bits means this socket is not expecting any
|
||||||
|
action */
|
||||||
|
#define SH_READ 1
|
||||||
|
#define SG_WRITE 2
|
||||||
|
|
||||||
/* make sure this socket is present in the hash for this handle */
|
/* make sure this socket is present in the hash for this handle */
|
||||||
static int sh_addentry(struct curl_hash *sh,
|
static int sh_addentry(struct curl_hash *sh,
|
||||||
@ -196,16 +196,9 @@ static int sh_addentry(struct curl_hash *sh,
|
|||||||
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
|
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
|
||||||
struct Curl_sh_entry *check;
|
struct Curl_sh_entry *check;
|
||||||
|
|
||||||
if(there) {
|
if(there)
|
||||||
/* verify that this particular handle is in here */
|
/* it is present, return fine */
|
||||||
check = there;
|
return 0;
|
||||||
while(check) {
|
|
||||||
if(check->easy == data)
|
|
||||||
/* it is, return fine */
|
|
||||||
return 0;
|
|
||||||
check = check->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* not present, add it */
|
/* not present, add it */
|
||||||
check = calloc(sizeof(struct Curl_sh_entry), 1);
|
check = calloc(sizeof(struct Curl_sh_entry), 1);
|
||||||
@ -213,57 +206,25 @@ static int sh_addentry(struct curl_hash *sh,
|
|||||||
return 1; /* major failure */
|
return 1; /* major failure */
|
||||||
check->easy = data;
|
check->easy = data;
|
||||||
|
|
||||||
if(there) {
|
/* make/add new hash entry */
|
||||||
/* the node for this socket is already here, now link in the struct for
|
if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check))
|
||||||
the new handle */
|
return 1; /* major failure */
|
||||||
|
|
||||||
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 */
|
return 0; /* things are good in sockhash land */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* delete the given socket + handle from the hash */
|
/* delete the given socket + handle from the hash */
|
||||||
static void sh_delentry(struct curl_hash *sh,
|
static void sh_delentry(struct curl_hash *sh, curl_socket_t s)
|
||||||
curl_socket_t s,
|
|
||||||
struct SessionHandle *data)
|
|
||||||
{
|
{
|
||||||
struct Curl_sh_entry *there =
|
struct Curl_sh_entry *there =
|
||||||
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
|
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
|
||||||
|
|
||||||
while(there) {
|
if(there) {
|
||||||
/* this socket is in the hash, now scan the list at this point and see if
|
/* this socket is in the hash */
|
||||||
the given easy handle is in there and if so remote that singe entry */
|
/* We remove the hash entry. (This'll end up in a call to
|
||||||
if(there->easy == data) {
|
sh_freeentry().) */
|
||||||
/* match! */
|
Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,14 +234,6 @@ static void sh_delentry(struct curl_hash *sh,
|
|||||||
static void sh_freeentry(void *freethis)
|
static void sh_freeentry(void *freethis)
|
||||||
{
|
{
|
||||||
struct Curl_sh_entry *p = (struct Curl_sh_entry *) 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);
|
free(p);
|
||||||
}
|
}
|
||||||
@ -1034,8 +987,9 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check what sockets we deal with and their "action state" and if we have a
|
* singlesocket() checks what sockets we deal with and their "action state"
|
||||||
* difference from last time we call the callback accordingly.
|
* and if we have a different state in any of those sockets from last time we
|
||||||
|
* call the callback accordingly.
|
||||||
*/
|
*/
|
||||||
static void singlesocket(struct Curl_multi *multi,
|
static void singlesocket(struct Curl_multi *multi,
|
||||||
struct Curl_one_easy *easy)
|
struct Curl_one_easy *easy)
|
||||||
@ -1050,9 +1004,11 @@ static void singlesocket(struct Curl_multi *multi,
|
|||||||
/* first fill in the 'current' struct with the state as it is now */
|
/* first fill in the 'current' struct with the state as it is now */
|
||||||
current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE);
|
current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE);
|
||||||
|
|
||||||
/* when filled in, we compare with the previous round's state */
|
/* when filled in, we compare with the previous round's state in a first
|
||||||
|
quick memory compare check */
|
||||||
if(memcmp(¤t, &easy->sockstate, sizeof(struct socketstate))) {
|
if(memcmp(¤t, &easy->sockstate, sizeof(struct socketstate))) {
|
||||||
/* difference, call the callback once for every socket change ! */
|
|
||||||
|
/* there is difference, call the callback once for every socket change ! */
|
||||||
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
|
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
|
||||||
int action;
|
int action;
|
||||||
curl_socket_t s = current.socks[i];
|
curl_socket_t s = current.socks[i];
|
||||||
@ -1103,7 +1059,7 @@ static void singlesocket(struct Curl_multi *multi,
|
|||||||
/* Update the sockhash accordingly */
|
/* Update the sockhash accordingly */
|
||||||
if(action == CURL_POLL_REMOVE)
|
if(action == CURL_POLL_REMOVE)
|
||||||
/* remove from hash for this easy handle */
|
/* remove from hash for this easy handle */
|
||||||
sh_delentry(multi->sockhash, s, easy->easy_handle);
|
sh_delentry(multi->sockhash, s);
|
||||||
else
|
else
|
||||||
/* make sure this socket is present in the hash for this handle */
|
/* make sure this socket is present in the hash for this handle */
|
||||||
sh_addentry(multi->sockhash, s, easy->easy_handle);
|
sh_addentry(multi->sockhash, s, easy->easy_handle);
|
||||||
@ -1138,6 +1094,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||||||
easyp = easyp->next;
|
easyp = easyp->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* or should we fall-through and do the timer-based stuff? */
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else if (s != CURL_SOCKET_TIMEOUT) {
|
else if (s != CURL_SOCKET_TIMEOUT) {
|
||||||
@ -1152,20 +1109,16 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||||||
/* Now, there is potentially a chain of easy handles in this hash
|
/* Now, there is potentially a chain of easy handles in this hash
|
||||||
entry struct and we need to deal with all of them */
|
entry struct and we need to deal with all of them */
|
||||||
|
|
||||||
do {
|
data = entry->easy;
|
||||||
data = entry->easy;
|
|
||||||
|
|
||||||
result = multi_runsingle(multi, data->set.one_easy, &running_handles);
|
result = multi_runsingle(multi, data->set.one_easy, &running_handles);
|
||||||
|
|
||||||
if(result == CURLM_OK)
|
if(result == CURLM_OK)
|
||||||
/* get the socket(s) and check if the state has been changed since
|
/* get the socket(s) and check if the state has been changed since
|
||||||
last */
|
last */
|
||||||
singlesocket(multi, data->set.one_easy);
|
singlesocket(multi, data->set.one_easy);
|
||||||
|
|
||||||
entry = entry->next;
|
|
||||||
|
|
||||||
} while(entry);
|
|
||||||
|
|
||||||
|
/* or should we fall-through and do the timer-based stuff? */
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user