summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/multi.c')
-rw-r--r--libs/libcurl/src/multi.c610
1 files changed, 308 insertions, 302 deletions
diff --git a/libs/libcurl/src/multi.c b/libs/libcurl/src/multi.c
index c388a3d8d8..fd2ee5238f 100644
--- a/libs/libcurl/src/multi.c
+++ b/libs/libcurl/src/multi.c
@@ -57,7 +57,7 @@
/*
CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
- to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every
+ to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every
CURL handle takes 45-50 K memory, therefore this 3K are not significant.
*/
#ifndef CURL_SOCKET_HASH_TABLE_SIZE
@@ -135,7 +135,7 @@ static void init_completed(struct Curl_easy *data)
{
/* this is a completed transfer */
- /* Important: reset the conn pointer so that we don't point to memory
+ /* Important: reset the conn pointer so that we do not point to memory
that could be freed anytime */
Curl_detach_connection(data);
Curl_expire_clear(data); /* stop all timers */
@@ -175,7 +175,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state
#endif
if(oldstate == state)
- /* don't bother when the new state is the same as the old state */
+ /* do not bother when the new state is the same as the old state */
return;
data->mstate = state;
@@ -191,7 +191,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state
#endif
if(state == MSTATE_COMPLETED) {
- /* changing to COMPLETED means there's one less easy handle 'alive' */
+ /* changing to COMPLETED means there is one less easy handle 'alive' */
DEBUGASSERT(data->multi->num_alive > 0);
data->multi->num_alive--;
if(!data->multi->num_alive) {
@@ -343,7 +343,7 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
curl_socket_t fd = *((curl_socket_t *) key);
(void) key_length;
- return (fd % slots_num);
+ return (fd % (curl_socket_t)slots_num);
}
/*
@@ -354,12 +354,12 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
* "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
+ * it has a linked list with entries. 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."
*
@@ -370,6 +370,17 @@ static void sh_init(struct Curl_hash *hash, size_t hashsize)
sh_freeentry);
}
+/* multi->proto_hash destructor. Should never be called as elements
+ * MUST be added with their own destructor */
+static void ph_freeentry(void *p)
+{
+ (void)p;
+ /* Will always be FALSE. Cannot use a 0 assert here since compilers
+ * are not in agreement if they then want a NORETURN attribute or
+ * not. *sigh* */
+ DEBUGASSERT(p == NULL);
+}
+
/*
* multi_addmsg()
*
@@ -396,7 +407,10 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
sh_init(&multi->sockhash, hashsize);
- if(Curl_conncache_init(&multi->conn_cache, chashsize))
+ Curl_hash_init(&multi->proto_hash, 23,
+ Curl_hash_str, Curl_str_key_compare, ph_freeentry);
+
+ if(Curl_conncache_init(&multi->conn_cache, multi, chashsize))
goto error;
Curl_llist_init(&multi->msglist, NULL);
@@ -412,14 +426,7 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
goto error;
#else
#ifdef ENABLE_WAKEUP
- if(wakeup_create(multi->wakeup_pair) < 0) {
- multi->wakeup_pair[0] = CURL_SOCKET_BAD;
- multi->wakeup_pair[1] = CURL_SOCKET_BAD;
- }
- else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 ||
- curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) {
- wakeup_close(multi->wakeup_pair[0]);
- wakeup_close(multi->wakeup_pair[1]);
+ if(wakeup_create(multi->wakeup_pair, TRUE) < 0) {
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
}
@@ -431,6 +438,7 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
error:
sockhash_destroy(&multi->sockhash);
+ Curl_hash_destroy(&multi->proto_hash);
Curl_hash_destroy(&multi->hostcache);
Curl_conncache_destroy(&multi->conn_cache);
free(multi);
@@ -544,10 +552,10 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
Curl_llist_init(&data->state.timeoutlist, NULL);
/*
- * No failure allowed in this function beyond this point. And no
- * modification of easy nor multi handle allowed before this except for
- * potential multi's connection cache growing which won't be undone in this
- * function no matter what.
+ * No failure allowed in this function beyond this point. No modification of
+ * easy nor multi handle allowed before this except for potential multi's
+ * connection cache growing which will not be undone in this function no
+ * matter what.
*/
if(data->set.errorbuffer)
data->set.errorbuffer[0] = 0;
@@ -684,8 +692,8 @@ static CURLcode multi_done(struct Curl_easy *data,
case CURLE_ABORTED_BY_CALLBACK:
case CURLE_READ_ERROR:
case CURLE_WRITE_ERROR:
- /* When we're aborted due to a callback return code it basically have to
- be counted as premature as there is trouble ahead if we don't. We have
+ /* When we are aborted due to a callback return code it basically have to
+ be counted as premature as there is trouble ahead if we do not. We have
many callbacks and protocols work differently, we could potentially do
this more fine-grained in the future. */
premature = TRUE;
@@ -749,8 +757,8 @@ static CURLcode multi_done(struct Curl_easy *data,
restrictions in our or the server's end
if premature is TRUE, it means this connection was said to be DONE before
- the entire request operation is complete and thus we can't know in what
- state it is for reusing, so we're forced to close it. In a perfect world
+ the entire request operation is complete and thus we cannot know in what
+ state it is for reusing, so we are forced to close it. In a perfect world
we can add code that keep track of if we really must close it here or not,
but currently we have no such detail knowledge.
*/
@@ -863,7 +871,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
if(data->conn &&
data->mstate > MSTATE_DO &&
data->mstate < MSTATE_COMPLETED) {
- /* Set connection owner so that the DONE function closes it. We can
+ /* Set connection owner so that the DONE function closes it. We can
safely do this here since connection is killed. */
streamclose(data->conn, "Removed with partial response");
}
@@ -872,7 +880,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
/* multi_done() clears the association between the easy handle and the
connection.
- Note that this ignores the return code simply because there's
+ Note that this ignores the return code simply because there is
nothing really useful to do with it anyway! */
(void)multi_done(data, data->result, premature);
}
@@ -906,7 +914,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
what we want */
data->mstate = MSTATE_COMPLETED;
- /* This ignores the return code even in case of problems because there's
+ /* This ignores the return code even in case of problems because there is
nothing more to do about that, here */
(void)singlesocket(multi, easy); /* to let the application know what sockets
that vanish with this handle */
@@ -918,7 +926,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
/* This removes a handle that was part the multi interface that used
CONNECT_ONLY, that connection is now left alive but since this handle
has bits.close set nothing can use that transfer anymore and it is
- forbidden from reuse. And this easy handle cannot find the connection
+ forbidden from reuse. This easy handle cannot find the connection
anymore once removed from the multi handle
Better close the connection here, at once.
@@ -945,12 +953,12 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
#endif
/* as this was using a shared connection cache we clear the pointer to that
- since we're not part of that multi handle anymore */
+ since we are not part of that multi handle anymore */
data->state.conn_cache = NULL;
data->multi = NULL; /* clear the association to this multi handle */
- /* make sure there's no pending message in the queue sent from this easy
+ /* make sure there is no pending message in the queue sent from this easy
handle */
for(e = multi->msglist.head; e; e = e->next) {
struct Curl_message *msg = e->ptr;
@@ -1002,8 +1010,9 @@ void Curl_detach_connection(struct Curl_easy *data)
* This is the only function that should assign data->conn
*/
void Curl_attach_connection(struct Curl_easy *data,
- struct connectdata *conn)
+ struct connectdata *conn)
{
+ DEBUGASSERT(data);
DEBUGASSERT(!data->conn);
DEBUGASSERT(conn);
data->conn = conn;
@@ -1016,12 +1025,14 @@ void Curl_attach_connection(struct Curl_easy *data,
static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
{
struct connectdata *conn = data->conn;
- (void)socks;
- /* Not using `conn->sockfd` as `Curl_xfer_setup()` initializes
- * that *after* the connect. */
- if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) {
+ curl_socket_t sockfd;
+
+ if(!conn)
+ return GETSOCK_BLANK;
+ sockfd = Curl_conn_get_socket(data, FIRSTSOCKET);
+ if(sockfd != CURL_SOCKET_BAD) {
/* Default is to wait to something from the server */
- socks[0] = conn->sock[FIRSTSOCKET];
+ socks[0] = sockfd;
return GETSOCK_READSOCK(0);
}
return GETSOCK_BLANK;
@@ -1030,11 +1041,16 @@ static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks)
{
struct connectdata *conn = data->conn;
- if(conn && conn->handler->proto_getsock)
+ curl_socket_t sockfd;
+
+ if(!conn)
+ return GETSOCK_BLANK;
+ if(conn->handler->proto_getsock)
return conn->handler->proto_getsock(data, conn, socks);
- else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+ sockfd = Curl_conn_get_socket(data, FIRSTSOCKET);
+ if(sockfd != CURL_SOCKET_BAD) {
/* Default is to wait to something from the server */
- socks[0] = conn->sockfd;
+ socks[0] = sockfd;
return GETSOCK_READSOCK(0);
}
return GETSOCK_BLANK;
@@ -1043,9 +1059,11 @@ static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks)
{
struct connectdata *conn = data->conn;
- if(conn && conn->handler->domore_getsock)
+ if(!conn)
+ return GETSOCK_BLANK;
+ if(conn->handler->domore_getsock)
return conn->handler->domore_getsock(data, conn, socks);
- else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+ else if(conn->sockfd != CURL_SOCKET_BAD) {
/* Default is that we want to send something to the server */
socks[0] = conn->sockfd;
return GETSOCK_WRITESOCK(0);
@@ -1056,9 +1074,11 @@ static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks)
{
struct connectdata *conn = data->conn;
- if(conn && conn->handler->doing_getsock)
+ if(!conn)
+ return GETSOCK_BLANK;
+ if(conn->handler->doing_getsock)
return conn->handler->doing_getsock(data, conn, socks);
- else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+ else if(conn->sockfd != CURL_SOCKET_BAD) {
/* Default is that we want to send something to the server */
socks[0] = conn->sockfd;
return GETSOCK_WRITESOCK(0);
@@ -1069,7 +1089,6 @@ static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
{
struct connectdata *conn = data->conn;
-
if(!conn)
return GETSOCK_BLANK;
else if(conn->handler->perform_getsock)
@@ -1106,6 +1125,7 @@ static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
static void multi_getsock(struct Curl_easy *data,
struct easy_pollset *ps)
{
+ bool expect_sockets = TRUE;
/* The no connection case can happen when this is called from
curl_multi_remove_handle() => singlesocket() => multi_getsock().
*/
@@ -1119,11 +1139,14 @@ static void multi_getsock(struct Curl_easy *data,
case MSTATE_SETUP:
case MSTATE_CONNECT:
/* nothing to poll for yet */
+ expect_sockets = FALSE;
break;
case MSTATE_RESOLVING:
Curl_pollset_add_socks(data, ps, Curl_resolv_getsock);
- /* connection filters are not involved in this phase */
+ /* connection filters are not involved in this phase. It's ok if we get no
+ * sockets to wait for. Resolving can wake up from other sources. */
+ expect_sockets = FALSE;
break;
case MSTATE_CONNECTING:
@@ -1157,19 +1180,29 @@ static void multi_getsock(struct Curl_easy *data,
case MSTATE_RATELIMITING:
/* we need to let time pass, ignore socket(s) */
+ expect_sockets = FALSE;
break;
case MSTATE_DONE:
case MSTATE_COMPLETED:
case MSTATE_MSGSENT:
/* nothing more to poll for */
+ expect_sockets = FALSE;
break;
default:
failf(data, "multi_getsock: unexpected multi state %d", data->mstate);
DEBUGASSERT(0);
+ expect_sockets = FALSE;
break;
}
+
+ if(expect_sockets && !ps->num &&
+ !(data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) &&
+ Curl_conn_is_ip_connected(data, FIRSTSOCKET)) {
+ infof(data, "WARNING: no socket in pollset, transfer may stall!");
+ DEBUGASSERT(0);
+ }
}
CURLMcode curl_multi_fdset(struct Curl_multi *multi,
@@ -1197,7 +1230,7 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
for(i = 0; i < ps.num; i++) {
if(!FDSET_SOCK(ps.sockets[i]))
- /* pretend it doesn't exist */
+ /* pretend it does not exist */
continue;
if(ps.actions[i] & CURL_POLL_IN)
FD_SET(ps.sockets[i], read_fd_set);
@@ -1219,12 +1252,9 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi,
unsigned int *fd_count)
{
struct Curl_easy *data;
- unsigned int nfds = 0;
+ struct curl_waitfds cwfds;
struct easy_pollset ps;
- unsigned int i;
CURLMcode result = CURLM_OK;
- struct curl_waitfd *ufd;
- unsigned int j;
if(!ufds)
return CURLM_BAD_FUNCTION_ARGUMENT;
@@ -1235,48 +1265,29 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi,
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
+ Curl_waitfds_init(&cwfds, ufds, size);
memset(&ps, 0, sizeof(ps));
for(data = multi->easyp; data; data = data->next) {
multi_getsock(data, &ps);
-
- for(i = 0; i < ps.num; i++) {
- if(nfds < size) {
- curl_socket_t fd = ps.sockets[i];
- int fd_idx = -1;
-
- /* Simple linear search to skip an already added descriptor */
- for(j = 0; j < nfds; j++) {
- if(ufds[j].fd == fd) {
- fd_idx = (int)j;
- break;
- }
- }
-
- if(fd_idx < 0) {
- ufd = &ufds[nfds++];
- ufd->fd = ps.sockets[i];
- ufd->events = 0;
- }
- else
- ufd = &ufds[fd_idx];
-
- if(ps.actions[i] & CURL_POLL_IN)
- ufd->events |= CURL_WAIT_POLLIN;
- if(ps.actions[i] & CURL_POLL_OUT)
- ufd->events |= CURL_WAIT_POLLOUT;
- }
- else
- return CURLM_OUT_OF_MEMORY;
+ if(Curl_waitfds_add_ps(&cwfds, &ps)) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
}
}
+ if(Curl_conncache_add_waitfds(&multi->conn_cache, &cwfds)) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
+ }
+
+out:
if(fd_count)
- *fd_count = nfds;
+ *fd_count = cwfds.n;
return result;
}
#ifdef USE_WINSOCK
-/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't
+/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets cannot
* be reset this way because an empty datagram would be sent. #9203
*
* "On Windows the internal state of FD_WRITE as returned from
@@ -1291,29 +1302,6 @@ static void reset_socket_fdwrite(curl_socket_t s)
}
#endif
-static CURLMcode ufds_increase(struct pollfd **pfds, unsigned int *pfds_len,
- unsigned int inc, bool *is_malloced)
-{
- struct pollfd *new_fds, *old_fds = *pfds;
- unsigned int new_len = *pfds_len + inc;
-
- new_fds = calloc(new_len, sizeof(struct pollfd));
- if(!new_fds) {
- if(*is_malloced)
- free(old_fds);
- *pfds = NULL;
- *pfds_len = 0;
- return CURLM_OUT_OF_MEMORY;
- }
- memcpy(new_fds, old_fds, (*pfds_len) * sizeof(struct pollfd));
- if(*is_malloced)
- free(old_fds);
- *pfds = new_fds;
- *pfds_len = new_len;
- *is_malloced = TRUE;
- return CURLM_OK;
-}
-
#define NUM_POLLS_ON_STACK 10
static CURLMcode multi_wait(struct Curl_multi *multi,
@@ -1330,10 +1318,9 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
long timeout_internal;
int retcode = 0;
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
- struct pollfd *ufds = &a_few_on_stack[0];
- unsigned int ufds_len = NUM_POLLS_ON_STACK;
- unsigned int nfds = 0, curl_nfds = 0; /* how many ufds are in use */
- bool ufds_malloc = FALSE;
+ struct curl_pollfds cpfds;
+ unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */
+ CURLMcode result = CURLM_OK;
#ifdef USE_WINSOCK
WSANETWORKEVENTS wsa_events;
DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
@@ -1351,139 +1338,105 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
if(timeout_ms < 0)
return CURLM_BAD_FUNCTION_ARGUMENT;
- /* If the internally desired timeout is actually shorter than requested from
- the outside, then use the shorter time! But only if the internal timer
- is actually larger than -1! */
- (void)multi_timeout(multi, &timeout_internal);
- if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
- timeout_ms = (int)timeout_internal;
-
- memset(ufds, 0, ufds_len * sizeof(struct pollfd));
+ Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
memset(&ps, 0, sizeof(ps));
/* Add the curl handles to our pollfds first */
for(data = multi->easyp; data; data = data->next) {
multi_getsock(data, &ps);
-
- for(i = 0; i < ps.num; i++) {
- short events = 0;
-#ifdef USE_WINSOCK
- long mask = 0;
-#endif
- if(ps.actions[i] & CURL_POLL_IN) {
-#ifdef USE_WINSOCK
- mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
-#endif
- events |= POLLIN;
- }
- if(ps.actions[i] & CURL_POLL_OUT) {
-#ifdef USE_WINSOCK
- mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
- reset_socket_fdwrite(ps.sockets[i]);
-#endif
- events |= POLLOUT;
- }
- if(events) {
- if(nfds && ps.sockets[i] == ufds[nfds-1].fd) {
- ufds[nfds-1].events |= events;
- }
- else {
- if(nfds >= ufds_len) {
- if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc))
- return CURLM_OUT_OF_MEMORY;
- }
- DEBUGASSERT(nfds < ufds_len);
- ufds[nfds].fd = ps.sockets[i];
- ufds[nfds].events = events;
- ++nfds;
- }
- }
-#ifdef USE_WINSOCK
- if(mask) {
- if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) {
- if(ufds_malloc)
- free(ufds);
- return CURLM_INTERNAL_ERROR;
- }
- }
-#endif
+ if(Curl_pollfds_add_ps(&cpfds, &ps)) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
}
}
- curl_nfds = nfds; /* what curl internally used in ufds */
+ if(Curl_conncache_add_pollfds(&multi->conn_cache, &cpfds)) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
+ }
+ curl_nfds = cpfds.n; /* what curl internally uses in cpfds */
/* Add external file descriptions from poll-like struct curl_waitfd */
for(i = 0; i < extra_nfds; i++) {
+ unsigned short events = 0;
+ if(extra_fds[i].events & CURL_WAIT_POLLIN)
+ events |= POLLIN;
+ if(extra_fds[i].events & CURL_WAIT_POLLPRI)
+ events |= POLLPRI;
+ if(extra_fds[i].events & CURL_WAIT_POLLOUT)
+ events |= POLLOUT;
+ if(Curl_pollfds_add_sock(&cpfds, extra_fds[i].fd, events)) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
#ifdef USE_WINSOCK
+ /* Set the WSA events based on the collected pollds */
+ for(i = 0; i < cpfds.n; i++) {
long mask = 0;
- if(extra_fds[i].events & CURL_WAIT_POLLIN)
+ if(cpfds.pfds[i].events & POLLIN)
mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
- if(extra_fds[i].events & CURL_WAIT_POLLPRI)
+ if(cpfds.pfds[i].events & POLLPRI)
mask |= FD_OOB;
- if(extra_fds[i].events & CURL_WAIT_POLLOUT) {
+ if(cpfds.pfds[i].events & POLLOUT) {
mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
- reset_socket_fdwrite(extra_fds[i].fd);
+ reset_socket_fdwrite(cpfds.pfds[i].fd);
}
- if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) {
- if(ufds_malloc)
- free(ufds);
- return CURLM_INTERNAL_ERROR;
- }
-#endif
- if(nfds >= ufds_len) {
- if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc))
- return CURLM_OUT_OF_MEMORY;
+ if(mask) {
+ if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
+ }
}
- DEBUGASSERT(nfds < ufds_len);
- ufds[nfds].fd = extra_fds[i].fd;
- ufds[nfds].events = 0;
- if(extra_fds[i].events & CURL_WAIT_POLLIN)
- ufds[nfds].events |= POLLIN;
- if(extra_fds[i].events & CURL_WAIT_POLLPRI)
- ufds[nfds].events |= POLLPRI;
- if(extra_fds[i].events & CURL_WAIT_POLLOUT)
- ufds[nfds].events |= POLLOUT;
- ++nfds;
}
+#endif
#ifdef ENABLE_WAKEUP
#ifndef USE_WINSOCK
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
- if(nfds >= ufds_len) {
- if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc))
- return CURLM_OUT_OF_MEMORY;
+ if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
}
- DEBUGASSERT(nfds < ufds_len);
- ufds[nfds].fd = multi->wakeup_pair[0];
- ufds[nfds].events = POLLIN;
- ++nfds;
}
#endif
#endif
+ /* We check the internal timeout *AFTER* we collected all sockets to
+ * poll. Collecting the sockets may install new timers by protocols
+ * and connection filters.
+ * Use the shorter one of the internal and the caller requested timeout. */
+ (void)multi_timeout(multi, &timeout_internal);
+ if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
+ timeout_ms = (int)timeout_internal;
+
#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
- if(nfds || use_wakeup) {
+ if(cpfds.n || use_wakeup) {
#else
- if(nfds) {
+ if(cpfds.n) {
#endif
int pollrc;
#ifdef USE_WINSOCK
- if(nfds)
- pollrc = Curl_poll(ufds, nfds, 0); /* just pre-check with WinSock */
+ if(cpfds.n) /* just pre-check with WinSock */
+ pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0);
else
pollrc = 0;
#else
- pollrc = Curl_poll(ufds, nfds, timeout_ms); /* wait... */
+ pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */
#endif
- if(pollrc < 0)
- return CURLM_UNRECOVERABLE_POLL;
+ if(pollrc < 0) {
+ result = CURLM_UNRECOVERABLE_POLL;
+ goto out;
+ }
if(pollrc > 0) {
retcode = pollrc;
#ifdef USE_WINSOCK
}
else { /* now wait... if not ready during the pre-check (pollrc == 0) */
- WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, timeout_ms, FALSE);
+ WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms,
+ FALSE);
}
/* With WinSock, we have to run the following section unconditionally
to call WSAEventSelect(fd, event, 0) on all the sockets */
@@ -1493,7 +1446,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
struct, the bit values of the actual underlying poll() implementation
may not be the same as the ones in the public libcurl API! */
for(i = 0; i < extra_nfds; i++) {
- unsigned r = ufds[curl_nfds + i].revents;
+ unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents;
unsigned short mask = 0;
#ifdef USE_WINSOCK
curl_socket_t s = extra_fds[i].fd;
@@ -1510,7 +1463,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
}
WSAEventSelect(s, multi->wsa_event, 0);
if(!pollrc) {
- extra_fds[i].revents = mask;
+ extra_fds[i].revents = (short)mask;
continue;
}
#endif
@@ -1520,7 +1473,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
mask |= CURL_WAIT_POLLOUT;
if(r & POLLPRI)
mask |= CURL_WAIT_POLLPRI;
- extra_fds[i].revents = mask;
+ extra_fds[i].revents = (short)mask;
}
#ifdef USE_WINSOCK
@@ -1547,7 +1500,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
#else
#ifdef ENABLE_WAKEUP
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
- if(ufds[curl_nfds + extra_nfds].revents & POLLIN) {
+ if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) {
char buf[64];
ssize_t nread;
while(1) {
@@ -1571,18 +1524,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
}
}
- if(ufds_malloc)
- free(ufds);
if(ret)
*ret = retcode;
#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
- if(extrawait && !nfds && !use_wakeup) {
+ if(extrawait && !cpfds.n && !use_wakeup) {
#else
- if(extrawait && !nfds) {
+ if(extrawait && !cpfds.n) {
#endif
long sleep_ms = 0;
- /* Avoid busy-looping when there's nothing particular to wait for */
+ /* Avoid busy-looping when there is nothing particular to wait for */
if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) {
if(sleep_ms > timeout_ms)
sleep_ms = timeout_ms;
@@ -1594,7 +1545,9 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
}
}
- return CURLM_OK;
+out:
+ Curl_pollfds_cleanup(&cpfds);
+ return result;
}
CURLMcode curl_multi_wait(struct Curl_multi *multi,
@@ -1623,6 +1576,15 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
it has to be careful only to access parts of the
Curl_multi struct that are constant */
+#if defined(ENABLE_WAKEUP) && !defined(USE_WINSOCK)
+#ifdef USE_EVENTFD
+ const void *buf;
+ const uint64_t val = 1;
+#else
+ char buf[1];
+#endif
+#endif
+
/* GOOD_MULTI_HANDLE can be safely called */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -1636,8 +1598,11 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
making it safe to access from another thread after the init part
and before cleanup */
if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
- char buf[1];
+#ifdef USE_EVENTFD
+ buf = &val;
+#else
buf[0] = 1;
+#endif
while(1) {
/* swrite() is not thread-safe in general, because concurrent calls
can have their messages interleaved, but in this case the content
@@ -1646,7 +1611,7 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
The write socket is set to non-blocking, this way this function
cannot block, making it safe to call even from the same thread
that will call curl_multi_wait(). If swrite() returns that it
- would block, it's considered successful because it means that
+ would block, it is considered successful because it means that
previous calls to this function will wake up the poll(). */
if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) {
int err = SOCKERRNO;
@@ -1710,7 +1675,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
if(!rc) {
struct SingleRequest *k = &data->req;
- /* pass in NULL for 'conn' here since we don't want to init the
+ /* pass in NULL for 'conn' here since we do not want to init the
connection, only this transfer */
Curl_init_do(data, NULL);
@@ -1742,7 +1707,7 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done)
* second connection.
*
* 'complete' can return 0 for incomplete, 1 for done and -1 for go back to
- * DOING state there's more work to do!
+ * DOING state there is more work to do!
*/
static CURLcode multi_do_more(struct Curl_easy *data, int *complete)
@@ -1870,10 +1835,10 @@ static CURLcode protocol_connect(struct Curl_easy *data,
&& conn->bits.protoconnstart) {
/* We already are connected, get back. This may happen when the connect
worked fine in the first call, like when we connect to a local server
- or proxy. Note that we don't know if the protocol is actually done.
+ or proxy. Note that we do not know if the protocol is actually done.
- Unless this protocol doesn't have any protocol-connect callback, as
- then we know we're done. */
+ Unless this protocol does not have any protocol-connect callback, as
+ then we know we are done. */
if(!conn->handler->connecting)
*protocol_done = TRUE;
@@ -1890,7 +1855,7 @@ static CURLcode protocol_connect(struct Curl_easy *data,
else
*protocol_done = TRUE;
- /* it has started, possibly even completed but that knowledge isn't stored
+ /* it has started, possibly even completed but that knowledge is not stored
in this bit! */
if(!result)
conn->bits.protoconnstart = TRUE;
@@ -1904,6 +1869,20 @@ static void set_in_callback(struct Curl_multi *multi, bool value)
multi->in_callback = value;
}
+/*
+ * posttransfer() is called immediately after a transfer ends
+ */
+static void multi_posttransfer(struct Curl_easy *data)
+{
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
+ /* restore the signal handler for SIGPIPE before we get back */
+ if(!data->set.no_signal)
+ signal(SIGPIPE, data->state.prev_signal);
+#else
+ (void)data; /* unused parameter */
+#endif
+}
+
static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct curltime *nowp,
struct Curl_easy *data)
@@ -1926,7 +1905,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* a multi-level callback returned error before, meaning every individual
transfer now has failed */
result = CURLE_ABORTED_BY_CALLBACK;
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, FALSE);
multistate(data, MSTATE_COMPLETED);
}
@@ -2009,7 +1988,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(!result) {
*nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE);
if(async)
- /* We're now waiting for an asynchronous name lookup */
+ /* We are now waiting for an asynchronous name lookup */
multistate(data, MSTATE_RESOLVING);
else {
/* after the connect has been sent off, go WAITCONNECT unless the
@@ -2062,7 +2041,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Update sockets here, because the socket(s) may have been
closed and the application thus needs to be told, even if it
is likely that the same socket(s) will again be used further
- down. If the name has not yet been resolved, it is likely
+ down. If the name has not yet been resolved, it is likely
that new sockets have been opened in an attempt to contact
another resolver. */
rc = singlesocket(multi, data);
@@ -2132,7 +2111,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else if(result) {
/* failure detected */
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, TRUE);
stream_error = TRUE;
break;
@@ -2162,7 +2141,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else {
/* failure detected */
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, TRUE);
stream_error = TRUE;
}
@@ -2178,7 +2157,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else if(result) {
/* failure detected */
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, TRUE);
stream_error = TRUE;
}
@@ -2198,9 +2177,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
Curl_set_in_callback(data, false);
if(prereq_rc != CURL_PREREQFUNC_OK) {
failf(data, "operation aborted by pre-request callback");
- /* failure in pre-request callback - don't do any other processing */
+ /* failure in pre-request callback - do not do any other
+ processing */
result = CURLE_ABORTED_BY_CALLBACK;
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, FALSE);
stream_error = TRUE;
break;
@@ -2230,7 +2210,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* skip some states if it is important */
multi_done(data, CURLE_OK, FALSE);
- /* if there's no connection left, skip the DONE state */
+ /* if there is no connection left, skip the DONE state */
multistate(data, data->conn ?
MSTATE_DONE : MSTATE_COMPLETED);
rc = CURLM_CALL_MULTI_PERFORM;
@@ -2246,13 +2226,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* after DO, go DO_DONE... or DO_MORE */
else if(data->conn->bits.do_more) {
- /* we're supposed to do more, but we need to sit down, relax
+ /* we are supposed to do more, but we need to sit down, relax
and wait a little while first */
multistate(data, MSTATE_DOING_MORE);
rc = CURLM_CALL_MULTI_PERFORM;
}
else {
- /* we're done with the DO, now DID */
+ /* we are done with the DO, now DID */
multistate(data, MSTATE_DID);
rc = CURLM_CALL_MULTI_PERFORM;
}
@@ -2261,7 +2241,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
data->conn->bits.reuse) {
/*
* In this situation, a connection that we were trying to use
- * may have unexpectedly died. If possible, send the connection
+ * may have unexpectedly died. If possible, send the connection
* back to the CONNECT phase so we can try again.
*/
char *newurl = NULL;
@@ -2275,7 +2255,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
stream_error = TRUE;
}
- Curl_posttransfer(data);
+ multi_posttransfer(data);
drc = multi_done(data, result, FALSE);
/* When set to retry the connection, we must go back to the CONNECT
@@ -2295,19 +2275,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
}
else {
- /* done didn't return OK or SEND_ERROR */
+ /* done did not return OK or SEND_ERROR */
result = drc;
}
}
else {
- /* Have error handler disconnect conn if we can't retry */
+ /* Have error handler disconnect conn if we cannot retry */
stream_error = TRUE;
}
free(newurl);
}
else {
/* failure detected */
- Curl_posttransfer(data);
+ multi_posttransfer(data);
if(data->conn)
multi_done(data, result, FALSE);
stream_error = TRUE;
@@ -2329,7 +2309,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else {
/* failure detected */
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, FALSE);
stream_error = TRUE;
}
@@ -2355,7 +2335,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else {
/* failure detected */
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, FALSE);
stream_error = TRUE;
}
@@ -2367,7 +2347,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Check if we can move pending requests to send pipe */
process_pending_handles(multi); /* multiplexed */
- /* Only perform the transfer if there's a good socket to work with.
+ /* Only perform the transfer if there is a good socket to work with.
Having both BAD is a signal to skip immediately to DONE */
if((data->conn->sockfd != CURL_SOCKET_BAD) ||
(data->conn->writesockfd != CURL_SOCKET_BAD))
@@ -2397,7 +2377,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result != CURLE_HTTP2_STREAM)
streamclose(data->conn, "Transfer returned error");
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, TRUE);
}
else {
@@ -2509,8 +2489,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(result) {
/*
* The transfer phase returned error, we mark the connection to get
- * closed to prevent being reused. This is because we can't possibly
- * know if the connection is in a good shape or not now. Unless it is
+ * closed to prevent being reused. This is because we cannot possibly
+ * know if the connection is in a good shape or not now. Unless it is
* a protocol which uses two "channels" like FTP, as then the error
* happened in the data connection.
*/
@@ -2519,13 +2499,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result != CURLE_HTTP2_STREAM)
streamclose(data->conn, "Transfer returned error");
- Curl_posttransfer(data);
+ multi_posttransfer(data);
multi_done(data, result, TRUE);
}
else if(data->req.done && !Curl_cwriter_is_paused(data)) {
/* call this even if the readwrite function returned error */
- Curl_posttransfer(data);
+ multi_posttransfer(data);
/* When we follow redirects or is set to retry the connection, we must
to go back to the CONNECT state */
@@ -2552,8 +2532,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* after the transfer is done, go DONE */
- /* but first check to see if we got a location info even though we're
- not following redirects */
+ /* but first check to see if we got a location info even though we
+ are not following redirects */
if(data->req.location) {
free(newurl);
newurl = data->req.location;
@@ -2571,10 +2551,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
}
}
- else if(data->state.select_bits) {
+ else if(data->state.select_bits && !Curl_xfer_is_blocked(data)) {
/* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer
- won't get stuck on this transfer at the expense of other concurrent
- transfers */
+ will not get stuck on this transfer at the expense of other
+ concurrent transfers */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
free(newurl);
@@ -2610,8 +2590,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
}
#endif
- /* after we have DONE what we're supposed to do, go COMPLETED, and
- it doesn't matter what the multi_done() returned! */
+ /* after we have DONE what we are supposed to do, go COMPLETED, and
+ it does not matter what the multi_done() returned! */
multistate(data, MSTATE_COMPLETED);
break;
@@ -2646,7 +2626,7 @@ statemachine_end:
if(data->mstate < MSTATE_COMPLETED) {
if(result) {
/*
- * If an error was returned, and we aren't in completed state now,
+ * If an error was returned, and we are not in completed state now,
* then we go to completed and consider this transfer aborted.
*/
@@ -2658,12 +2638,12 @@ statemachine_end:
if(data->conn) {
if(stream_error) {
- /* Don't attempt to send data over a connection that timed out */
+ /* Do not attempt to send data over a connection that timed out */
bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
struct connectdata *conn = data->conn;
/* This is where we make sure that the conn pointer is reset.
- We don't have to do this in every case block above where a
+ We do not have to do this in every case block above where a
failure is detected */
Curl_detach_connection(data);
@@ -2676,13 +2656,14 @@ statemachine_end:
}
else if(data->mstate == MSTATE_CONNECT) {
/* Curl_connect() failed */
- (void)Curl_posttransfer(data);
+ multi_posttransfer(data);
+ Curl_pgrsUpdate_nometer(data);
}
multistate(data, MSTATE_COMPLETED);
rc = CURLM_CALL_MULTI_PERFORM;
}
- /* if there's still a connection to use, call the progress function */
+ /* if there is still a connection to use, call the progress function */
else if(data->conn && Curl_pgrsUpdate(data)) {
/* aborted due to progress callback return code must close the
connection */
@@ -2752,6 +2733,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
/* the current node might be unlinked in multi_runsingle(), get the next
pointer now */
struct Curl_easy *datanext = data->next;
+
if(data->set.no_signal != nosig) {
sigpipe_restore(&pipe_st);
sigpipe_ignore(data, &pipe_st);
@@ -2760,11 +2742,14 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
result = multi_runsingle(multi, &now, data);
if(result)
returncode = result;
+
data = datanext; /* operate on next handle */
} while(data);
sigpipe_restore(&pipe_st);
}
+ Curl_conncache_multi_perform(multi);
+
/*
* Simply remove all expired timers from the splay since handles are dealt
* with unconditionally by this function and curl_multi_timeout() requires
@@ -2793,7 +2778,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
}
} while(t);
- *running_handles = multi->num_alive;
+ *running_handles = (int)multi->num_alive;
if(CURLM_OK >= returncode)
returncode = Curl_update_timer(multi);
@@ -2829,6 +2814,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
/* First remove all remaining easy handles */
data = multi->easyp;
while(data) {
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLM_BAD_HANDLE;
+
nextdata = data->next;
if(!data->state.done && data->conn)
/* if DONE was never called for this handle */
@@ -2853,9 +2841,10 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
}
/* Close all the connections in the connection cache */
- Curl_conncache_close_all_connections(&multi->conn_cache);
+ Curl_conncache_multi_close_all(multi);
sockhash_destroy(&multi->sockhash);
+ Curl_hash_destroy(&multi->proto_hash);
Curl_conncache_destroy(&multi->conn_cache);
Curl_hash_destroy(&multi->hostcache);
Curl_psl_destroy(&multi->psl);
@@ -2865,12 +2854,10 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
#else
#ifdef ENABLE_WAKEUP
wakeup_close(multi->wakeup_pair[0]);
+#ifndef USE_EVENTFD
wakeup_close(multi->wakeup_pair[1]);
#endif
#endif
-
-#ifdef USE_SSL
- Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
#endif
multi_xfer_bufs_free(multi);
@@ -2927,41 +2914,54 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
struct Curl_easy *data)
{
struct easy_pollset cur_poll;
- unsigned int i;
- struct Curl_sh_entry *entry;
- curl_socket_t s;
- int rc;
+ CURLMcode mresult;
/* Fill in the 'current' struct with the state as it is now: what sockets to
supervise and for what actions */
multi_getsock(data, &cur_poll);
+ mresult = Curl_multi_pollset_ev(multi, data, &cur_poll, &data->last_poll);
+
+ if(!mresult) /* Remember for next time */
+ memcpy(&data->last_poll, &cur_poll, sizeof(cur_poll));
+ return mresult;
+}
+
+CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct easy_pollset *ps,
+ struct easy_pollset *last_ps)
+{
+ unsigned int i;
+ struct Curl_sh_entry *entry;
+ curl_socket_t s;
+ int rc;
/* We have 0 .. N sockets already and we get to know about the 0 .. M
sockets we should have from now on. Detect the differences, remove no
longer supervised ones and add new ones */
/* walk over the sockets we got right now */
- for(i = 0; i < cur_poll.num; i++) {
- unsigned char cur_action = cur_poll.actions[i];
+ for(i = 0; i < ps->num; i++) {
+ unsigned char cur_action = ps->actions[i];
unsigned char last_action = 0;
int comboaction;
- s = cur_poll.sockets[i];
+ s = ps->sockets[i];
/* get it from the hash */
entry = sh_getentry(&multi->sockhash, s);
if(entry) {
/* check if new for this transfer */
unsigned int j;
- for(j = 0; j< data->last_poll.num; j++) {
- if(s == data->last_poll.sockets[j]) {
- last_action = data->last_poll.actions[j];
+ for(j = 0; j< last_ps->num; j++) {
+ if(s == last_ps->sockets[j]) {
+ last_action = last_ps->actions[j];
break;
}
}
}
else {
- /* this is a socket we didn't have before, add it to the hash! */
+ /* this is a socket we did not have before, add it to the hash! */
entry = sh_addentry(&multi->sockhash, s);
if(!entry)
/* fatal */
@@ -2978,14 +2978,15 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
if(cur_action & CURL_POLL_OUT)
entry->writers++;
}
- else if(!last_action) {
+ else if(!last_action &&
+ !Curl_hash_pick(&entry->transfers, (char *)&data, /* hash key */
+ sizeof(struct Curl_easy *))) {
/* a new transfer using this socket */
entry->users++;
if(cur_action & CURL_POLL_IN)
entry->readers++;
if(cur_action & CURL_POLL_OUT)
entry->writers++;
-
/* add 'data' to the transfer hash on this socket! */
if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */
sizeof(struct Curl_easy *), data)) {
@@ -3014,18 +3015,19 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
}
}
- entry->action = comboaction; /* store the current action state */
+ /* store the current action state */
+ entry->action = (unsigned int)comboaction;
}
- /* Check for last_poll.sockets that no longer appear in cur_poll.sockets.
+ /* Check for last_poll.sockets that no longer appear in ps->sockets.
* Need to remove the easy handle from the multi->sockhash->transfers and
* remove multi->sockhash entry when this was the last transfer */
- for(i = 0; i< data->last_poll.num; i++) {
+ for(i = 0; i < last_ps->num; i++) {
unsigned int j;
bool stillused = FALSE;
- s = data->last_poll.sockets[i];
- for(j = 0; j < cur_poll.num; j++) {
- if(s == cur_poll.sockets[j]) {
+ s = last_ps->sockets[i];
+ for(j = 0; j < ps->num; j++) {
+ if(s == ps->sockets[j]) {
/* this is still supervised */
stillused = TRUE;
break;
@@ -3038,7 +3040,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
/* if this is NULL here, the socket has been closed and notified so
already by Curl_multi_closed() */
if(entry) {
- unsigned char oldactions = data->last_poll.actions[i];
+ unsigned char oldactions = last_ps->actions[i];
/* this socket has been removed. Decrease user count */
entry->users--;
if(oldactions & CURL_POLL_OUT)
@@ -3068,8 +3070,6 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
}
} /* for loop over num */
- /* Remember for next time */
- memcpy(&data->last_poll, &cur_poll, sizeof(data->last_poll));
return CURLM_OK;
}
@@ -3085,7 +3085,7 @@ CURLcode Curl_updatesocket(struct Curl_easy *data)
* Curl_multi_closed()
*
* Used by the connect code to tell the multi_socket code that one of the
- * sockets we were using is about to be closed. This function will then
+ * sockets we were using is about to be closed. This function will then
* remove it from the sockethash for this handle to make the multi_socket API
* behave properly, especially for the case when libcurl will create another
* socket again and it gets the same file descriptor number.
@@ -3094,7 +3094,7 @@ CURLcode Curl_updatesocket(struct Curl_easy *data)
void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
{
if(data) {
- /* if there's still an easy handle associated with this connection */
+ /* if there is still an easy handle associated with this connection */
struct Curl_multi *multi = data->multi;
if(multi) {
/* this is set if this connection is part of a handle that is added to
@@ -3170,7 +3170,7 @@ static CURLMcode add_next_timeout(struct curltime now,
/* copy the first entry to 'tv' */
memcpy(tv, &node->time, sizeof(*tv));
- /* Insert this node again into the splay. Keep the timer in the list in
+ /* Insert this node again into the splay. Keep the timer in the list in
case we need to recompute future timers. */
multi->timetree = Curl_splayinsert(*tv, multi->timetree,
&d->state.timenode);
@@ -3212,13 +3212,16 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
if(s != CURL_SOCKET_TIMEOUT) {
struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
- if(!entry)
- /* Unmatched socket, we can't act on it but we ignore this fact. In
+ if(!entry) {
+ /* Unmatched socket, we cannot act on it but we ignore this fact. In
real-world tests it has been proved that libevent can in fact give
the application actions even though the socket was just previously
asked to get removed, so thus we better survive stray socket actions
and just move on. */
- ;
+ /* The socket might come from a connection that is being shut down
+ * by the multi's conncache. */
+ Curl_conncache_multi_socket(multi, s, ev_bitmask);
+ }
else {
struct Curl_hash_iterator iter;
struct Curl_hash_element *he;
@@ -3232,18 +3235,18 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
- /* set socket event bitmask if they're not locked */
+ /* set socket event bitmask if they are not locked */
data->state.select_bits |= (unsigned char)ev_bitmask;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
- /* Now we fall-through and do the timer-based stuff, since we don't want
+ /* Now we fall-through and do the timer-based stuff, since we do not want
to force the user to have to deal with timeouts as long as at least
one connection in fact has traffic. */
data = NULL; /* set data to NULL again to avoid calling
- multi_runsingle() in case there's no need to */
+ multi_runsingle() in case there is no need to */
now = Curl_now(); /* get a newer time since the multi_runsingle() loop
may have taken some time */
}
@@ -3286,7 +3289,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
}
}
- /* Check if there's one (more) expired timer to deal with! This function
+ /* Check if there is one (more) expired timer to deal with! This function
extracts a matching node if there is one */
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
@@ -3299,7 +3302,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
if(first)
sigpipe_restore(&pipe_st);
- *running_handles = multi->num_alive;
+ *running_handles = (int)multi->num_alive;
return result;
}
@@ -3351,6 +3354,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
break;
case CURLMOPT_MAX_TOTAL_CONNECTIONS:
multi->max_total_connections = va_arg(param, long);
+ /* for now, let this also decide the max number of connections
+ * in shutdown handling */
+ multi->max_shutdown_connections = va_arg(param, long);
break;
/* options formerly used for pipelining */
case CURLMOPT_MAX_PIPELINE_LENGTH:
@@ -3437,7 +3443,7 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
/* some time left before expiration */
timediff_t diff = Curl_timediff_ceil(multi->timetree->key, now);
- /* this should be safe even on 32 bit archs, as we don't use that
+ /* this should be safe even on 32-bit archs, as we do not use that
overly long timeouts */
*timeout_ms = (long)diff;
}
@@ -3482,7 +3488,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi)
static const struct curltime none = {0, 0};
if(Curl_splaycomparekeys(none, multi->timer_lastcall)) {
multi->timer_lastcall = none;
- /* there's no timeout now but there was one previously, tell the app to
+ /* there is no timeout now but there was one previously, tell the app to
disable it */
set_in_callback(multi, TRUE);
rc = multi->timer_cb(multi, -1, multi->timer_userp);
@@ -3603,8 +3609,8 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
DEBUGASSERT(id < EXPIRE_LAST);
set = Curl_now();
- set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */
- set.tv_usec += (unsigned int)(milli%1000)*1000;
+ set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bits conversion */
+ set.tv_usec += (int)(milli%1000)*1000;
if(set.tv_usec >= 1000000) {
set.tv_sec++;
@@ -3614,7 +3620,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
/* Remove any timer with the same id just in case. */
multi_deltimeout(data, id);
- /* Add it to the timer list. It must stay in the list until it has expired
+ /* Add it to the timer list. It must stay in the list until it has expired
in case we need to recompute the minimum timer later. */
multi_addtimeout(data, &set, id);
@@ -3627,7 +3633,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
if(diff > 0) {
/* The current splay tree entry is sooner than this new expiry time.
- We don't need to update our splay tree entry. */
+ We do not need to update our splay tree entry. */
return;
}
@@ -3718,12 +3724,12 @@ CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
size_t Curl_multi_max_host_connections(struct Curl_multi *multi)
{
- return multi ? multi->max_host_connections : 0;
+ return multi ? (size_t)multi->max_host_connections : 0;
}
size_t Curl_multi_max_total_connections(struct Curl_multi *multi)
{
- return multi ? multi->max_total_connections : 0;
+ return multi ? (size_t)multi->max_total_connections : 0;
}
/*