multi: reduce Win32 API calls to improve performance

1. Consolidate pre-checks into a single Curl_poll call:

This is an attempt to restructure the code in Curl_multi_wait
in such a way that less syscalls are made by removing individual
calls to Curl_socket_check via SOCKET_READABLE/SOCKET_WRITABLE.

2. Avoid resetting the WinSock event multiple times:

We finally call WSAResetEvent anyway, so specifying it as
an optional parameter to WSAEnumNetworkEvents is redundant.

3. Wakeup directly in case no sockets are being monitoring:

Fix the WinSock based implementation to skip extra waiting by
not sleeping in case no sockets are to be waited on and just
the WinSock event is being monitored for wakeup functionality.

Assisted-by: Tommy Odom
Reviewed-by: Jay Satiro
Reviewed-by: Marcel Raad

Bug: #6146
Closes #6245
This commit is contained in:
Marc Hoersken 2020-12-06 17:28:26 +01:00
parent bcca174cfa
commit e92998a312
No known key found for this signature in database
GPG Key ID: 61E03CBED7BC859E

View File

@ -1086,12 +1086,10 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
unsigned int curlfds;
long timeout_internal;
int retcode = 0;
#ifndef USE_WINSOCK
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
struct pollfd *ufds = &a_few_on_stack[0];
bool ufds_malloc = FALSE;
#else
struct pollfd pre_poll;
#ifdef USE_WINSOCK
WSANETWORKEVENTS wsa_events;
DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
#endif
@ -1149,7 +1147,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
}
#endif
#ifndef USE_WINSOCK
if(nfds > NUM_POLLS_ON_STACK) {
/* '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
@ -1160,9 +1157,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
return CURLM_OUT_OF_MEMORY;
ufds_malloc = TRUE;
}
nfds = 0;
#endif
/* only do the second loop if we found descriptors in the first stage run
above */
@ -1180,34 +1175,31 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
#endif
if(bitmap & GETSOCK_READSOCK(i)) {
#ifdef USE_WINSOCK
if(timeout_ms && SOCKET_READABLE(sockbunch[i], 0) > 0)
timeout_ms = 0;
mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
#else
#endif
ufds[nfds].fd = sockbunch[i];
ufds[nfds].events = POLLIN;
++nfds;
#endif
s = sockbunch[i];
}
if(bitmap & GETSOCK_WRITESOCK(i)) {
#ifdef USE_WINSOCK
if(timeout_ms && SOCKET_WRITABLE(sockbunch[i], 0) > 0)
timeout_ms = 0;
mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
#else
#endif
ufds[nfds].fd = sockbunch[i];
ufds[nfds].events = POLLOUT;
++nfds;
#endif
s = sockbunch[i];
}
if(s == CURL_SOCKET_BAD) {
break;
}
#ifdef USE_WINSOCK
if(WSAEventSelect(s, multi->wsa_event, mask) != 0)
if(WSAEventSelect(s, multi->wsa_event, mask) != 0) {
if(ufds_malloc)
free(ufds);
return CURLM_INTERNAL_ERROR;
}
#endif
}
@ -1219,35 +1211,18 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
for(i = 0; i < extra_nfds; i++) {
#ifdef USE_WINSOCK
long mask = 0;
extra_fds[i].revents = 0;
pre_poll.fd = extra_fds[i].fd;
pre_poll.events = 0;
pre_poll.revents = 0;
if(extra_fds[i].events & CURL_WAIT_POLLIN) {
if(extra_fds[i].events & CURL_WAIT_POLLIN)
mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
pre_poll.events |= POLLIN;
}
if(extra_fds[i].events & CURL_WAIT_POLLPRI) {
if(extra_fds[i].events & CURL_WAIT_POLLPRI)
mask |= FD_OOB;
pre_poll.events |= POLLPRI;
}
if(extra_fds[i].events & CURL_WAIT_POLLOUT) {
if(extra_fds[i].events & CURL_WAIT_POLLOUT)
mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
pre_poll.events |= POLLOUT;
}
if(Curl_poll(&pre_poll, 1, 0) > 0) {
if(pre_poll.revents & POLLIN)
extra_fds[i].revents |= CURL_WAIT_POLLIN;
if(pre_poll.revents & POLLPRI)
extra_fds[i].revents |= CURL_WAIT_POLLPRI;
if(pre_poll.revents & POLLOUT)
extra_fds[i].revents |= CURL_WAIT_POLLOUT;
if(extra_fds[i].revents)
timeout_ms = 0;
}
if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0)
if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) {
if(ufds_malloc)
free(ufds);
return CURLM_INTERNAL_ERROR;
#else
}
#endif
ufds[nfds].fd = extra_fds[i].fd;
ufds[nfds].events = 0;
if(extra_fds[i].events & CURL_WAIT_POLLIN)
@ -1257,7 +1232,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
if(extra_fds[i].events & CURL_WAIT_POLLOUT)
ufds[nfds].events |= POLLOUT;
++nfds;
#endif
}
#ifdef ENABLE_WAKEUP
@ -1270,53 +1244,59 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
#endif
#endif
if(nfds) {
/* wait... */
#ifdef USE_WINSOCK
WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, timeout_ms, FALSE);
#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
if(nfds || use_wakeup) {
#else
int pollrc = Curl_poll(ufds, nfds, timeout_ms);
if(nfds) {
#endif
int pollrc;
#ifdef USE_WINSOCK
if(nfds)
pollrc = Curl_poll(ufds, nfds, 0); /* just pre-check with WinSock */
else
pollrc = 0;
if(pollrc <= 0) /* now wait... if not ready during the pre-check above */
WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, timeout_ms, FALSE);
#else
pollrc = Curl_poll(ufds, nfds, timeout_ms); /* wait... */
#endif
#ifdef USE_WINSOCK
/* With Winsock, we have to run this unconditionally to call
WSAEventSelect(fd, event, 0) on all the sockets */
{
retcode = 0;
#else
if(pollrc > 0) {
retcode = pollrc;
#ifdef USE_WINSOCK
}
/* With WinSock, we have to run the following section unconditionally
to call WSAEventSelect(fd, event, 0) on all the sockets */
{
#endif
/* copy revents results from the poll to the curl_multi_wait poll
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[curlfds + i].revents;
unsigned short mask = 0;
#ifdef USE_WINSOCK
wsa_events.lNetworkEvents = 0;
mask = extra_fds[i].revents;
if(WSAEnumNetworkEvents(extra_fds[i].fd, multi->wsa_event,
&wsa_events) == 0) {
if(WSAEnumNetworkEvents(extra_fds[i].fd, NULL, &wsa_events) == 0) {
if(wsa_events.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE))
mask |= CURL_WAIT_POLLIN;
if(wsa_events.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE))
mask |= CURL_WAIT_POLLOUT;
if(wsa_events.lNetworkEvents & FD_OOB)
mask |= CURL_WAIT_POLLPRI;
if(ret && wsa_events.lNetworkEvents != 0)
if(ret && pollrc <= 0 && wsa_events.lNetworkEvents != 0)
retcode++;
}
WSAEventSelect(extra_fds[i].fd, multi->wsa_event, 0);
#else
unsigned r = ufds[curlfds + i].revents;
if(pollrc <= 0)
continue;
#endif
if(r & POLLIN)
mask |= CURL_WAIT_POLLIN;
if(r & POLLOUT)
mask |= CURL_WAIT_POLLOUT;
if(r & POLLPRI)
mask |= CURL_WAIT_POLLPRI;
#endif
extra_fds[i].revents = mask;
}
@ -1331,17 +1311,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
wsa_events.lNetworkEvents = 0;
if(WSAEnumNetworkEvents(sockbunch[i], multi->wsa_event,
&wsa_events) == 0) {
if(ret && wsa_events.lNetworkEvents != 0)
retcode++;
}
if(ret && !timeout_ms && wsa_events.lNetworkEvents == 0) {
if((bitmap & GETSOCK_READSOCK(i)) &&
SOCKET_READABLE(sockbunch[i], 0) > 0)
retcode++;
else if((bitmap & GETSOCK_WRITESOCK(i)) &&
SOCKET_WRITABLE(sockbunch[i], 0) > 0)
if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) {
if(ret && pollrc <= 0 && wsa_events.lNetworkEvents != 0)
retcode++;
}
WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
@ -1382,16 +1353,15 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
}
}
#ifndef USE_WINSOCK
if(ufds_malloc)
free(ufds);
#endif
if(ret)
*ret = retcode;
if(!extrawait || nfds)
/* if any socket was checked */
;
else {
#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
if(extrawait && !nfds && !use_wakeup) {
#else
if(extrawait && !nfds) {
#endif
long sleep_ms = 0;
/* Avoid busy-looping when there's nothing particular to wait for */