[vlc-devel] [PATCH] win32: use the same poll code as libcurl
Steve Lhomme
robux4 at videolabs.io
Tue Oct 6 16:13:06 CEST 2015
the current code was occasionally not seeing some sockets available for reading
--
deprecates https://patches.videolan.org/patch/10242/
---
compat/poll.c | 250 +++++++++++++++++++++++++++++++---------------------------
1 file changed, 132 insertions(+), 118 deletions(-)
diff --git a/compat/poll.c b/compat/poll.c
index fa94c93..89015bb 100644
--- a/compat/poll.c
+++ b/compat/poll.c
@@ -109,146 +109,160 @@ int (poll) (struct pollfd *fds, unsigned nfds, int timeout)
# include <windows.h>
# include <winsock2.h>
+static int wait_ms(int timeout_ms);
+static long tvdiff(struct timeval newer, struct timeval older);
+static struct timeval tvnow(void);
+
int poll(struct pollfd *fds, unsigned nfds, int timeout)
{
- DWORD to = (timeout >= 0) ? (DWORD)timeout : INFINITE;
+ struct timeval pending_tv;
+ struct timeval *ptimeout;
+ fd_set fds_read;
+ fd_set fds_write;
+ fd_set fds_err;
+ SOCKET maxfd;
- if (nfds == 0)
- { /* WSAWaitForMultipleEvents() does not allow zero events */
- if (SleepEx(to, TRUE))
- {
- errno = EINTR;
- return -1;
+ struct timeval initial_tv = {0, 0};
+ BOOL fds_none = TRUE;
+ unsigned int i;
+ int pending_ms = 0;
+ int error;
+ int r;
+
+ if (fds) {
+ for (i = 0; i < nfds; i++) {
+ if (fds[i].fd != INVALID_SOCKET) {
+ fds_none = FALSE;
+ break;
+ }
}
- return 0;
+ }
+ if (fds_none) {
+ return wait_ms(timeout);
}
- WSAEVENT *evts = malloc(nfds * sizeof (WSAEVENT));
- if (evts == NULL)
- return -1; /* ENOMEM */
-
- DWORD ret = WSA_WAIT_FAILED;
- for (unsigned i = 0; i < nfds; i++)
- {
- SOCKET fd = fds[i].fd;
- long mask = FD_CLOSE;
- fd_set rdset, wrset, exset;
-
- FD_ZERO(&rdset);
- FD_ZERO(&wrset);
- FD_ZERO(&exset);
- FD_SET(fd, &exset);
+ if (timeout > 0) {
+ pending_ms = timeout;
+ initial_tv = tvnow();
+ }
- if (fds[i].events & POLLRDNORM)
- {
- mask |= FD_READ | FD_ACCEPT;
- FD_SET(fd, &rdset);
- }
- if (fds[i].events & POLLWRNORM)
- {
- mask |= FD_WRITE | FD_CONNECT;
- FD_SET(fd, &wrset);
- }
- if (fds[i].events & POLLPRI)
- mask |= FD_OOB;
+ FD_ZERO(&fds_read);
+ FD_ZERO(&fds_write);
+ FD_ZERO(&fds_err);
+ maxfd = INVALID_SOCKET;
+ for (i = 0; i < nfds; i++) {
fds[i].revents = 0;
-
- evts[i] = WSACreateEvent();
- if (evts[i] == WSA_INVALID_EVENT)
- {
- while (i > 0)
- WSACloseEvent(evts[--i]);
- free(evts);
- errno = ENOMEM;
- return -1;
+ if (fds[i].fd == INVALID_SOCKET)
+ continue;
+ if (fds[i].events & (POLLIN|POLLOUT|POLLPRI|POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
+ if (fds[i].fd > maxfd)
+ maxfd = fds[i].fd;
+ if (fds[i].events & (POLLRDNORM|POLLIN))
+ FD_SET(fds[i].fd, &fds_read);
+ if (fds[i].events & (POLLWRNORM|POLLOUT))
+ FD_SET(fds[i].fd, &fds_write);
+ if (fds[i].events & (POLLRDBAND|POLLPRI))
+ FD_SET(fds[i].fd, &fds_err);
}
-
- if (WSAEventSelect(fds[i].fd, evts[i], mask)
- && WSAGetLastError() == WSAENOTSOCK)
- fds[i].revents |= POLLNVAL;
-
- struct timeval tv = { 0, 0 };
- /* By its horrible design, WSAEnumNetworkEvents() only enumerates
- * events that were not already signaled (i.e. it is edge-triggered).
- * WSAPoll() would be better in this respect, but worse in others.
- * So use WSAEnumNetworkEvents() after manually checking for pending
- * events. */
- if (select(0, &rdset, &wrset, &exset, &tv) > 0)
- {
- if (FD_ISSET(fd, &rdset))
- fds[i].revents |= fds[i].events & POLLRDNORM;
- if (FD_ISSET(fd, &wrset))
- fds[i].revents |= fds[i].events & POLLWRNORM;
- if (FD_ISSET(fd, &exset))
- /* To add pain to injury, POLLERR and POLLPRI cannot be
- * distinguished here. */
- fds[i].revents |= POLLERR | (fds[i].events & POLLPRI);
- }
-
- if (fds[i].revents != 0 && ret == WSA_WAIT_FAILED)
- ret = WSA_WAIT_EVENT_0 + i;
}
- if (ret == WSA_WAIT_FAILED)
- ret = WSAWaitForMultipleEvents(nfds, evts, FALSE, to, TRUE);
-
- unsigned count = 0;
- for (unsigned i = 0; i < nfds; i++)
- {
- WSANETWORKEVENTS ne;
+ /* WinSock select() can't handle zero events. */
+ if (!fds_read.fd_count && !fds_write.fd_count && !fds_err.fd_count) {
+ return wait_ms(timeout);
+ }
- if (WSAEnumNetworkEvents(fds[i].fd, evts[i], &ne))
- memset(&ne, 0, sizeof (ne));
- WSAEventSelect(fds[i].fd, evts[i], 0);
- WSACloseEvent(evts[i]);
+ ptimeout = (timeout < 0) ? NULL : &pending_tv;
- if (ne.lNetworkEvents & FD_CONNECT)
- {
- fds[i].revents |= POLLWRNORM;
- if (ne.iErrorCode[FD_CONNECT_BIT] != 0)
- fds[i].revents |= POLLERR;
+ do {
+ if (timeout > 0) {
+ pending_tv.tv_sec = pending_ms / 1000;
+ pending_tv.tv_usec = (pending_ms % 1000) * 1000;
}
- if (ne.lNetworkEvents & FD_CLOSE)
- {
- fds[i].revents |= (fds[i].events & POLLRDNORM) | POLLHUP;
- if (ne.iErrorCode[FD_CLOSE_BIT] != 0)
- fds[i].revents |= POLLERR;
+ else if (!timeout) {
+ pending_tv.tv_sec = 0;
+ pending_tv.tv_usec = 0;
}
- if (ne.lNetworkEvents & FD_ACCEPT)
- {
- fds[i].revents |= POLLRDNORM;
- if (ne.iErrorCode[FD_ACCEPT_BIT] != 0)
- fds[i].revents |= POLLERR;
+ r = select(maxfd + 1,
+ /* WinSock select() can't handle fd_sets with zero bits set, so
+ don't give it such arguments.
+ */
+ fds_read.fd_count ? &fds_read : NULL,
+ fds_write.fd_count ? &fds_write : NULL,
+ fds_err.fd_count ? &fds_err : NULL,
+ ptimeout);
+ if (r != -1)
+ break;
+ error = WSAGetLastError();
+ if (error && error != EINTR)
+ break;
+ if (timeout > 0) {
+ pending_ms = timeout - (int)tvdiff(tvnow(), initial_tv);
+ if (pending_ms <= 0) {
+ r = 0; /* Simulate a "call timed out" case */
+ break;
+ }
}
- if (ne.lNetworkEvents & FD_OOB)
- {
+ } while (r == -1);
+
+ if (r < 0)
+ return -1;
+ if (r == 0)
+ return 0;
+
+ r = 0;
+ for (i = 0; i < nfds; i++) {
+ fds[i].revents = 0;
+ if (fds[i].fd == INVALID_SOCKET)
+ continue;
+ if (FD_ISSET(fds[i].fd, &fds_read))
+ fds[i].revents |= POLLIN;
+ if (FD_ISSET(fds[i].fd, &fds_write))
+ fds[i].revents |= POLLOUT;
+ if (FD_ISSET(fds[i].fd, &fds_err))
fds[i].revents |= POLLPRI;
- if (ne.iErrorCode[FD_OOB_BIT] != 0)
- fds[i].revents |= POLLERR;
- }
- if (ne.lNetworkEvents & FD_READ)
- {
- fds[i].revents |= POLLRDNORM;
- if (ne.iErrorCode[FD_READ_BIT] != 0)
- fds[i].revents |= POLLERR;
- }
- if (ne.lNetworkEvents & FD_WRITE)
- {
- fds[i].revents |= POLLWRNORM;
- if (ne.iErrorCode[FD_WRITE_BIT] != 0)
- fds[i].revents |= POLLERR;
- }
- count += fds[i].revents != 0;
+ if (fds[i].revents != 0)
+ r++;
}
- free(evts);
+ return r;
+}
- if (count == 0 && ret == WSA_WAIT_IO_COMPLETION)
+static int wait_ms(int timeout)
+{
+ int r = 0;
+ if (timeout < 0)
{
- errno = EINTR;
- return -1;
+ WSASetLastError( EINVAL );
+ r = -1;
}
- return count;
+ else if (timeout > 0 && SleepEx( timeout, TRUE ))
+ {
+ WSASetLastError( EINTR );
+ r = -1;
+ }
+ return r;
+}
+
+static long tvdiff(struct timeval newer, struct timeval older)
+{
+ return (newer.tv_sec-older.tv_sec)*1000 + (long)(newer.tv_usec-older.tv_usec)/1000;
+}
+
+static struct timeval tvnow(void)
+{
+ struct timeval now;
+#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
+ (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+ DWORD milliseconds = GetTickCount();
+ now.tv_sec = milliseconds / 1000;
+ now.tv_usec = (milliseconds % 1000) * 1000;
+#else
+ ULONGLONG milliseconds = GetTickCount64();
+ now.tv_sec = (long) (milliseconds / 1000);
+ now.tv_usec = (long) (milliseconds % 1000) * 1000;
+#endif
+
+ return now;
}
#endif
--
2.5.2
More information about the vlc-devel
mailing list