*/
struct Curl_sh_entry {
- struct curl_llist list; /* list of easy handles using this socket */
+ struct curl_hash transfers; /* hash of transfers using this socket */
unsigned int action; /* what combined action READ/WRITE this socket waits
for */
void *socketp; /* settable by users with curl_multi_assign() */
unsigned int users; /* number of transfers using this */
unsigned int readers; /* this many transfers want to read */
unsigned int writers; /* this many transfers want to write */
+ unsigned int blocked:1; /* if TRUE, blocked from being removed */
+ unsigned int removed:1; /* if TRUE, this entry is "removed" but prevented
+ from it by "blocked" being set! */
};
/* bits for 'action' having no bits means this socket is not expecting any
action */
/* look up a given socket in the socket hash, skip invalid sockets */
static struct Curl_sh_entry *sh_getentry(struct curl_hash *sh,
- curl_socket_t s)
+ curl_socket_t s,
+ bool also_hidden)
{
- if(s != CURL_SOCKET_BAD)
+ if(s != CURL_SOCKET_BAD) {
/* only look for proper sockets */
- return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+ struct Curl_sh_entry *entry =
+ Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+ if(entry && entry->removed && !also_hidden)
+ return NULL;
+ return entry;
+ }
return NULL;
}
+#define TRHASH_SIZE 13
+static size_t trhash(void *key, size_t key_length, size_t slots_num)
+{
+ size_t keyval = (size_t)key; /* this is a data pointer */
+ (void) key_length;
+
+ return (keyval % slots_num);
+}
+
+static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
+{
+ (void)k1_len;
+ (void)k2_len;
+
+ return k1 == k2;
+}
+
+static void trhash_dtor(void *nada)
+{
+ (void)nada;
+}
+
+
/* make sure this socket is present in the hash for this handle */
static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
curl_socket_t s)
{
- struct Curl_sh_entry *there = sh_getentry(sh, s);
+ struct Curl_sh_entry *there = sh_getentry(sh, s, TRUE);
struct Curl_sh_entry *check;
- if(there)
+ if(there) {
/* it is present, return fine */
+ if(there->removed)
+ there->removed = FALSE; /* clear the removed bit */
return there;
+ }
/* not present, add it */
check = calloc(1, sizeof(struct Curl_sh_entry));
if(!check)
return NULL; /* major failure */
- Curl_llist_init(&check->list, NULL);
+ if(Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash,
+ trhash_compare, trhash_dtor)) {
+ free(check);
+ return NULL;
+ }
/* make/add new hash entry */
if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
static void sh_delentry(struct Curl_sh_entry *entry,
struct curl_hash *sh, curl_socket_t s)
{
- struct curl_llist *list = &entry->list;
- struct curl_llist_element *e;
- /* clear the list of transfers first */
- for(e = list->head; e; e = list->head) {
- struct Curl_easy *dta = e->ptr;
- Curl_llist_remove(&entry->list, e, NULL);
- dta->sh_entry = NULL;
+ if(entry->blocked) {
+ entry->removed = TRUE; /* pretend */
+ return;
+ }
+ else {
+ Curl_hash_destroy(&entry->transfers);
+
+ /* We remove the hash entry. This will end up in a call to
+ sh_freeentry(). */
+ Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
}
- /* We remove the hash entry. This will end up in a call to
- sh_freeentry(). */
- Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
}
/*
return CURLM_OK;
}
-/*
- * multi_freeamsg()
- *
- * Callback used by the llist system when a single list entry is destroyed.
- */
-static void multi_freeamsg(void *a, void *b)
-{
- (void)a;
- (void)b;
-}
-
struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
int chashsize) /* connection hash */
{
if(Curl_conncache_init(&multi->conn_cache, chashsize))
goto error;
- Curl_llist_init(&multi->msglist, multi_freeamsg);
- Curl_llist_init(&multi->pending, multi_freeamsg);
+ Curl_llist_init(&multi->msglist, NULL);
+ Curl_llist_init(&multi->pending, NULL);
/* -1 means it not set by user, use the default value */
multi->maxconnects = -1;
static void detach_connnection(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
- if(data->sh_entry) {
- /* still listed as a user of a socket hash entry, remove it */
- Curl_llist_remove(&data->sh_entry->list, &data->sh_queue, NULL);
- data->sh_entry = NULL;
- }
if(conn)
Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
data->conn = NULL;
bool stream_error = FALSE;
rc = CURLM_OK;
+ DEBUGASSERT((data->mstate <= CURLM_STATE_CONNECT) ||
+ (data->mstate >= CURLM_STATE_DONE) ||
+ data->conn);
if(!data->conn &&
data->mstate > CURLM_STATE_CONNECT &&
data->mstate < CURLM_STATE_DONE) {
s = socks[i];
/* get it from the hash */
- entry = sh_getentry(&multi->sockhash, s);
+ entry = sh_getentry(&multi->sockhash, s, FALSE);
if(curraction & GETSOCK_READSOCK(i))
action |= CURL_POLL_IN;
if(action & CURL_POLL_OUT)
entry->writers++;
- /* add 'data' to the list of handles using this socket! */
- Curl_llist_insert_next(&entry->list, entry->list.tail,
- data, &data->sh_queue);
- data->sh_entry = entry;
+ /* add 'data' to the transfer hash on this socket! */
+ if(!Curl_hash_add(&entry->transfers, (char *)data, /* hash key */
+ sizeof(struct Curl_easy *), data))
+ return CURLM_OUT_OF_MEMORY;
}
comboaction = (entry->writers? CURL_POLL_OUT : 0) |
(entry->readers ? CURL_POLL_IN : 0);
-#if 0
- infof(data, "--- Comboaction: %u readers %u writers\n",
- entry->readers, entry->writers);
-#endif
- /* check if it has the same action set */
- if(entry->action == comboaction)
+ /* socket existed before and has the same action set as before */
+ if(sincebefore && (entry->action == comboaction))
/* same, continue */
continue;
- /* we know (entry != NULL) at this point, see the logic above */
if(multi->socket_cb)
- multi->socket_cb(data,
- s,
- comboaction,
- multi->socket_userp,
+ multi->socket_cb(data, s, comboaction, multi->socket_userp,
entry->socketp);
entry->action = comboaction; /* store the current action state */
if(stillused)
continue;
- entry = sh_getentry(&multi->sockhash, s);
+ entry = sh_getentry(&multi->sockhash, s, FALSE);
/* if this is NULL here, the socket has been closed and notified so
already by Curl_multi_closed() */
if(entry) {
entry->socketp);
sh_delentry(entry, &multi->sockhash, s);
}
+ else {
+ /* still users, but remove this handle as a user of this socket */
+ Curl_hash_delete(&entry->transfers, (char *)data,
+ sizeof(struct Curl_easy *));
+ }
}
} /* for loop over numsocks */
if(multi) {
/* this is set if this connection is part of a handle that is added to
a multi handle, and only then this is necessary */
- struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
+ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s, FALSE);
if(entry) {
if(multi->socket_cb)
return result;
}
if(s != CURL_SOCKET_TIMEOUT) {
- struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
+ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s, FALSE);
if(!entry)
/* Unmatched socket, we can't act on it but we ignore this fact. In
and just move on. */
;
else {
- struct curl_llist *list = &entry->list;
- struct curl_llist_element *e;
- struct curl_llist_element *enext;
+ struct curl_hash_iterator iter;
+ struct curl_hash_element *he;
SIGPIPE_VARIABLE(pipe_st);
- /* the socket can be shared by many transfers, iterate */
- for(e = list->head; e; e = enext) {
- data = (struct Curl_easy *)e->ptr;
+ /* block this sockhash entry from being removed in a sub function called
+ from here */
+ entry->blocked = TRUE;
+ DEBUGASSERT(!entry->removed);
- /* assign 'enext' here since the 'e' struct might be cleared
- further down in the singlesocket() call */
- enext = e->next;
+ /* the socket can be shared by many transfers, iterate */
+ Curl_hash_start_iterate(&entry->transfers, &iter);
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
+ data = (struct Curl_easy *)he->ptr;
DEBUGASSERT(data);
DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
return result;
}
}
+ if(entry->removed) {
+ entry->blocked = FALSE; /* unblock */
+ sh_delentry(entry, &multi->sockhash, s); /* delete for real */
+ }
/* Now we fall-through and do the timer-based stuff, since we don't want
to force the user to have to deal with timeouts as long as at least
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
- there = sh_getentry(&multi->sockhash, s);
+ there = sh_getentry(&multi->sockhash, s, FALSE);
if(!there)
return CURLM_BAD_SOCKET;
statename[data->mstate], data->numsocks);
for(i = 0; i < data->numsocks; i++) {
curl_socket_t s = data->sockets[i];
- struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
+ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s, FALSE);
fprintf(stderr, "%d ", (int)s);
if(!entry) {