mirror of
https://github.com/moparisthebest/curl
synced 2024-12-24 09:08:49 -05:00
multi: implement wait using winsock events
This avoids using a pair of TCP ports to provide wakeup functionality for every multi instance on Windows, where socketpair() is emulated using a TCP socket on loopback which could in turn lead to socket resource exhaustion. Reviewed-by: Gergely Nagy Reviewed-by: Marc Hörsken Closes #5397
This commit is contained in:
parent
477a4e31d7
commit
8bc25c590e
122
lib/multi.c
122
lib/multi.c
@ -374,6 +374,11 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
|
|||||||
multi->max_concurrent_streams = 100;
|
multi->max_concurrent_streams = 100;
|
||||||
multi->ipv6_works = Curl_ipv6works(NULL);
|
multi->ipv6_works = Curl_ipv6works(NULL);
|
||||||
|
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
multi->wsa_event = WSACreateEvent();
|
||||||
|
if(multi->wsa_event == WSA_INVALID_EVENT)
|
||||||
|
goto error;
|
||||||
|
#else
|
||||||
#ifdef ENABLE_WAKEUP
|
#ifdef ENABLE_WAKEUP
|
||||||
if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
|
if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
|
||||||
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
|
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
|
||||||
@ -386,6 +391,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
|
|||||||
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
|
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
|
||||||
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
|
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return multi;
|
return multi;
|
||||||
@ -1067,11 +1073,15 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
unsigned int nfds = 0;
|
unsigned int nfds = 0;
|
||||||
unsigned int curlfds;
|
unsigned int curlfds;
|
||||||
bool ufds_malloc = FALSE;
|
|
||||||
long timeout_internal;
|
long timeout_internal;
|
||||||
int retcode = 0;
|
int retcode = 0;
|
||||||
|
#ifndef USE_WINSOCK
|
||||||
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
|
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
|
||||||
struct pollfd *ufds = &a_few_on_stack[0];
|
struct pollfd *ufds = &a_few_on_stack[0];
|
||||||
|
bool ufds_malloc = FALSE;
|
||||||
|
#else
|
||||||
|
DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(!GOOD_MULTI_HANDLE(multi))
|
if(!GOOD_MULTI_HANDLE(multi))
|
||||||
return CURLM_BAD_HANDLE;
|
return CURLM_BAD_HANDLE;
|
||||||
@ -1117,11 +1127,16 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
nfds += extra_nfds; /* add the externally provided ones */
|
nfds += extra_nfds; /* add the externally provided ones */
|
||||||
|
|
||||||
#ifdef ENABLE_WAKEUP
|
#ifdef ENABLE_WAKEUP
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
if(use_wakeup) {
|
||||||
|
#else
|
||||||
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
||||||
|
#endif
|
||||||
++nfds;
|
++nfds;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_WINSOCK
|
||||||
if(nfds > NUM_POLLS_ON_STACK) {
|
if(nfds > NUM_POLLS_ON_STACK) {
|
||||||
/* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
|
/* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
|
||||||
big, so at 2^29 sockets this value might wrap. When a process gets
|
big, so at 2^29 sockets this value might wrap. When a process gets
|
||||||
@ -1132,7 +1147,9 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
return CURLM_OUT_OF_MEMORY;
|
return CURLM_OUT_OF_MEMORY;
|
||||||
ufds_malloc = TRUE;
|
ufds_malloc = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
nfds = 0;
|
nfds = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* only do the second loop if we found descriptors in the first stage run
|
/* only do the second loop if we found descriptors in the first stage run
|
||||||
above */
|
above */
|
||||||
@ -1145,22 +1162,36 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
|
|
||||||
for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
|
for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
|
||||||
curl_socket_t s = CURL_SOCKET_BAD;
|
curl_socket_t s = CURL_SOCKET_BAD;
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
long mask = 0;
|
||||||
|
#endif
|
||||||
if(bitmap & GETSOCK_READSOCK(i)) {
|
if(bitmap & GETSOCK_READSOCK(i)) {
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
mask |= FD_READ;
|
||||||
|
#else
|
||||||
ufds[nfds].fd = sockbunch[i];
|
ufds[nfds].fd = sockbunch[i];
|
||||||
ufds[nfds].events = POLLIN;
|
ufds[nfds].events = POLLIN;
|
||||||
++nfds;
|
++nfds;
|
||||||
|
#endif
|
||||||
s = sockbunch[i];
|
s = sockbunch[i];
|
||||||
}
|
}
|
||||||
if(bitmap & GETSOCK_WRITESOCK(i)) {
|
if(bitmap & GETSOCK_WRITESOCK(i)) {
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
mask |= FD_WRITE;
|
||||||
|
#else
|
||||||
ufds[nfds].fd = sockbunch[i];
|
ufds[nfds].fd = sockbunch[i];
|
||||||
ufds[nfds].events = POLLOUT;
|
ufds[nfds].events = POLLOUT;
|
||||||
++nfds;
|
++nfds;
|
||||||
|
#endif
|
||||||
s = sockbunch[i];
|
s = sockbunch[i];
|
||||||
}
|
}
|
||||||
if(s == CURL_SOCKET_BAD) {
|
if(s == CURL_SOCKET_BAD) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
if(WSAEventSelect(s, multi->wsa_event, mask) != 0)
|
||||||
|
return CURLM_INTERNAL_ERROR;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
data = data->next; /* check next handle */
|
data = data->next; /* check next handle */
|
||||||
@ -1169,6 +1200,17 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
|
|
||||||
/* Add external file descriptions from poll-like struct curl_waitfd */
|
/* Add external file descriptions from poll-like struct curl_waitfd */
|
||||||
for(i = 0; i < extra_nfds; i++) {
|
for(i = 0; i < extra_nfds; i++) {
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
long events = 0;
|
||||||
|
if(extra_fds[i].events & CURL_WAIT_POLLIN)
|
||||||
|
events |= FD_READ;
|
||||||
|
if(extra_fds[i].events & CURL_WAIT_POLLPRI)
|
||||||
|
events |= FD_OOB;
|
||||||
|
if(extra_fds[i].events & CURL_WAIT_POLLOUT)
|
||||||
|
events |= FD_WRITE;
|
||||||
|
if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, events) != 0)
|
||||||
|
return CURLM_INTERNAL_ERROR;
|
||||||
|
#else
|
||||||
ufds[nfds].fd = extra_fds[i].fd;
|
ufds[nfds].fd = extra_fds[i].fd;
|
||||||
ufds[nfds].events = 0;
|
ufds[nfds].events = 0;
|
||||||
if(extra_fds[i].events & CURL_WAIT_POLLIN)
|
if(extra_fds[i].events & CURL_WAIT_POLLIN)
|
||||||
@ -1178,28 +1220,61 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
if(extra_fds[i].events & CURL_WAIT_POLLOUT)
|
if(extra_fds[i].events & CURL_WAIT_POLLOUT)
|
||||||
ufds[nfds].events |= POLLOUT;
|
ufds[nfds].events |= POLLOUT;
|
||||||
++nfds;
|
++nfds;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_WAKEUP
|
#ifdef ENABLE_WAKEUP
|
||||||
|
#ifndef USE_WINSOCK
|
||||||
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
||||||
ufds[nfds].fd = multi->wakeup_pair[0];
|
ufds[nfds].fd = multi->wakeup_pair[0];
|
||||||
ufds[nfds].events = POLLIN;
|
ufds[nfds].events = POLLIN;
|
||||||
++nfds;
|
++nfds;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(nfds) {
|
if(nfds) {
|
||||||
int pollrc;
|
int pollrc;
|
||||||
/* wait... */
|
/* wait... */
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
DWORD waitrc = WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE,
|
||||||
|
timeout_ms, FALSE);
|
||||||
|
/* WSA_WAIT_EVENT_0 is 0, so waitrc >= WSA_WAIT_EVENT_0 warns */
|
||||||
|
if(waitrc == WSA_WAIT_EVENT_0)
|
||||||
|
pollrc = 1;
|
||||||
|
else
|
||||||
|
pollrc = -1;
|
||||||
|
#else
|
||||||
pollrc = Curl_poll(ufds, nfds, timeout_ms);
|
pollrc = Curl_poll(ufds, nfds, timeout_ms);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(pollrc > 0) {
|
if(pollrc > 0) {
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
retcode = 0;
|
||||||
|
#else
|
||||||
retcode = pollrc;
|
retcode = pollrc;
|
||||||
|
#endif
|
||||||
/* copy revents results from the poll to the curl_multi_wait poll
|
/* copy revents results from the poll to the curl_multi_wait poll
|
||||||
struct, the bit values of the actual underlying poll() implementation
|
struct, the bit values of the actual underlying poll() implementation
|
||||||
may not be the same as the ones in the public libcurl API! */
|
may not be the same as the ones in the public libcurl API! */
|
||||||
for(i = 0; i < extra_nfds; i++) {
|
for(i = 0; i < extra_nfds; i++) {
|
||||||
unsigned short mask = 0;
|
unsigned short mask = 0;
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
WSANETWORKEVENTS events = {0};
|
||||||
|
if(WSAEnumNetworkEvents(extra_fds[i].fd, multi->wsa_event,
|
||||||
|
&events) == 0) {
|
||||||
|
if(events.lNetworkEvents & FD_READ)
|
||||||
|
mask |= CURL_WAIT_POLLIN;
|
||||||
|
if(events.lNetworkEvents & FD_WRITE)
|
||||||
|
mask |= CURL_WAIT_POLLOUT;
|
||||||
|
if(events.lNetworkEvents & FD_OOB)
|
||||||
|
mask |= CURL_WAIT_POLLPRI;
|
||||||
|
|
||||||
|
if(events.lNetworkEvents != 0)
|
||||||
|
retcode++;
|
||||||
|
}
|
||||||
|
WSAEventSelect(extra_fds[i].fd, multi->wsa_event, 0);
|
||||||
|
#else
|
||||||
unsigned r = ufds[curlfds + i].revents;
|
unsigned r = ufds[curlfds + i].revents;
|
||||||
|
|
||||||
if(r & POLLIN)
|
if(r & POLLIN)
|
||||||
@ -1208,10 +1283,39 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
mask |= CURL_WAIT_POLLOUT;
|
mask |= CURL_WAIT_POLLOUT;
|
||||||
if(r & POLLPRI)
|
if(r & POLLPRI)
|
||||||
mask |= CURL_WAIT_POLLPRI;
|
mask |= CURL_WAIT_POLLPRI;
|
||||||
|
#endif
|
||||||
|
|
||||||
extra_fds[i].revents = mask;
|
extra_fds[i].revents = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
/* Count up all our own sockets that had activity,
|
||||||
|
and remove them from the event. */
|
||||||
|
if(curlfds) {
|
||||||
|
data = multi->easyp;
|
||||||
|
while(data) {
|
||||||
|
bitmap = multi_getsock(data, sockbunch);
|
||||||
|
|
||||||
|
for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
|
||||||
|
if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
|
||||||
|
WSANETWORKEVENTS events = {0};
|
||||||
|
if(WSAEnumNetworkEvents(sockbunch[i], multi->wsa_event,
|
||||||
|
&events) == 0) {
|
||||||
|
if(events.lNetworkEvents != 0)
|
||||||
|
retcode++;
|
||||||
|
}
|
||||||
|
WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = data->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WSAResetEvent(multi->wsa_event);
|
||||||
|
#else
|
||||||
#ifdef ENABLE_WAKEUP
|
#ifdef ENABLE_WAKEUP
|
||||||
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
|
||||||
if(ufds[curlfds + extra_nfds].revents & POLLIN) {
|
if(ufds[curlfds + extra_nfds].revents & POLLIN) {
|
||||||
@ -1224,10 +1328,8 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
when there is no more data, breaking the loop. */
|
when there is no more data, breaking the loop. */
|
||||||
nread = sread(multi->wakeup_pair[0], buf, sizeof(buf));
|
nread = sread(multi->wakeup_pair[0], buf, sizeof(buf));
|
||||||
if(nread <= 0) {
|
if(nread <= 0) {
|
||||||
#ifndef USE_WINSOCK
|
|
||||||
if(nread < 0 && EINTR == SOCKERRNO)
|
if(nread < 0 && EINTR == SOCKERRNO)
|
||||||
continue;
|
continue;
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1235,12 +1337,15 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
|
|||||||
retcode--;
|
retcode--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef USE_WINSOCK
|
||||||
if(ufds_malloc)
|
if(ufds_malloc)
|
||||||
free(ufds);
|
free(ufds);
|
||||||
|
#endif
|
||||||
if(ret)
|
if(ret)
|
||||||
*ret = retcode;
|
*ret = retcode;
|
||||||
if(!extrawait || nfds)
|
if(!extrawait || nfds)
|
||||||
@ -1295,6 +1400,10 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
|
|||||||
return CURLM_BAD_HANDLE;
|
return CURLM_BAD_HANDLE;
|
||||||
|
|
||||||
#ifdef ENABLE_WAKEUP
|
#ifdef ENABLE_WAKEUP
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
if(WSASetEvent(multi->wsa_event))
|
||||||
|
return CURLM_OK;
|
||||||
|
#else
|
||||||
/* the wakeup_pair variable is only written during init and cleanup,
|
/* the wakeup_pair variable is only written during init and cleanup,
|
||||||
making it safe to access from another thread after the init part
|
making it safe to access from another thread after the init part
|
||||||
and before cleanup */
|
and before cleanup */
|
||||||
@ -1327,6 +1436,7 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
|
|||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return CURLM_WAKEUP_FAILURE;
|
return CURLM_WAKEUP_FAILURE;
|
||||||
}
|
}
|
||||||
@ -2485,9 +2595,13 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
|
|||||||
Curl_hash_destroy(&multi->hostcache);
|
Curl_hash_destroy(&multi->hostcache);
|
||||||
Curl_psl_destroy(&multi->psl);
|
Curl_psl_destroy(&multi->psl);
|
||||||
|
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
WSACloseEvent(multi->wsa_event);
|
||||||
|
#else
|
||||||
#ifdef ENABLE_WAKEUP
|
#ifdef ENABLE_WAKEUP
|
||||||
sclose(multi->wakeup_pair[0]);
|
sclose(multi->wakeup_pair[0]);
|
||||||
sclose(multi->wakeup_pair[1]);
|
sclose(multi->wakeup_pair[1]);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
free(multi);
|
free(multi);
|
||||||
|
|
||||||
|
@ -138,9 +138,13 @@ struct Curl_multi {
|
|||||||
previous callback */
|
previous callback */
|
||||||
unsigned int max_concurrent_streams;
|
unsigned int max_concurrent_streams;
|
||||||
|
|
||||||
|
#ifdef USE_WINSOCK
|
||||||
|
WSAEVENT wsa_event; /* winsock event used for waits */
|
||||||
|
#else
|
||||||
#ifdef ENABLE_WAKEUP
|
#ifdef ENABLE_WAKEUP
|
||||||
curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
|
curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
|
||||||
0 is used for read, 1 is used for write */
|
0 is used for read, 1 is used for write */
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
/* multiplexing wanted */
|
/* multiplexing wanted */
|
||||||
bool multiplexing;
|
bool multiplexing;
|
||||||
|
Loading…
Reference in New Issue
Block a user