[vlc-devel] [PATCH 2/2] win32: use select() to get the basic poll status/events
Steve Lhomme
robux4 at videolabs.io
Fri Jan 15 17:16:22 CET 2016
WSAEnumNetworkEvents/WSAEventSelect are still used to get extra errors or
events not reported by select()
--
relying on WSAEnumNetworkEvents() with a volatile WSAEventSelect() loses events
once in a while, resulting in stalled connections.
---
compat/poll.c | 148 ++++++++++++++++++++++++++++++++++------------------------
1 file changed, 88 insertions(+), 60 deletions(-)
diff --git a/compat/poll.c b/compat/poll.c
index fa94c93..43cc80d 100644
--- a/compat/poll.c
+++ b/compat/poll.c
@@ -109,13 +109,40 @@ int (poll) (struct pollfd *fds, unsigned nfds, int timeout)
# include <windows.h>
# include <winsock2.h>
+#include "../include/vlc_common.h"
+#include "../include/vlc_threads.h"
+
+#undef poll
+
+static inline void cancel_Select(void *p_data)
+{
+ SOCKET *p_fd_cancel = p_data;
+ if ( *p_fd_cancel != INVALID_SOCKET )
+ {
+ closesocket( *p_fd_cancel );
+ *p_fd_cancel = INVALID_SOCKET;
+ }
+}
+
int poll(struct pollfd *fds, unsigned nfds, int timeout)
{
- DWORD to = (timeout >= 0) ? (DWORD)timeout : INFINITE;
+ struct timeval pending_tv;
+ fd_set rdset;
+ fd_set wrset;
+ fd_set fds_err;
+ SOCKET maxfd = INVALID_SOCKET;
+ SOCKET fd_cancel = INVALID_SOCKET;
+ SOCKET fd_cleanup = INVALID_SOCKET;
+ int count;
if (nfds == 0)
{ /* WSAWaitForMultipleEvents() does not allow zero events */
- if (SleepEx(to, TRUE))
+ if (timeout < 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (timeout > 0 && SleepEx(timeout, TRUE))
{
errno = EINTR;
return -1;
@@ -127,17 +154,18 @@ int poll(struct pollfd *fds, unsigned nfds, int timeout)
if (evts == NULL)
return -1; /* ENOMEM */
- DWORD ret = WSA_WAIT_FAILED;
+ FD_ZERO(&rdset);
+ FD_ZERO(&wrset);
+ FD_ZERO(&fds_err);
+
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);
+ fds[i].revents = 0;
+ if (maxfd == INVALID_SOCKET)
+ maxfd = fd;
if (fds[i].events & POLLRDNORM)
{
@@ -151,8 +179,7 @@ int poll(struct pollfd *fds, unsigned nfds, int timeout)
}
if (fds[i].events & POLLPRI)
mask |= FD_OOB;
-
- fds[i].revents = 0;
+ FD_SET(fd, &fds_err);
evts[i] = WSACreateEvent();
if (evts[i] == WSA_INVALID_EVENT)
@@ -167,88 +194,89 @@ int poll(struct pollfd *fds, unsigned nfds, int timeout)
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 (timeout != 0)
+ {
+ /* use a dummy UDP socket to cancel interrupt infinite select() calls */
+ fd_cleanup = fd_cancel = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ if (fd_cancel != INVALID_SOCKET)
+ FD_SET(fd_cancel, &rdset);
+ }
- if (fds[i].revents != 0 && ret == WSA_WAIT_FAILED)
- ret = WSA_WAIT_EVENT_0 + i;
+ if (timeout >= 0)
+ {
+ pending_tv.tv_sec = timeout / 1000;
+ pending_tv.tv_usec = (timeout % 1000) * 1000;
}
- if (ret == WSA_WAIT_FAILED)
- ret = WSAWaitForMultipleEvents(nfds, evts, FALSE, to, TRUE);
+ /* make the select interruptible */
+ vlc_cancel_push(cancel_Select, &fd_cancel);
+ count = select(0,
+ /* WinSock select() can't handle fd_sets with zero bits set, so
+ don't give it such arguments.
+ */
+ rdset.fd_count ? &rdset : NULL,
+ wrset.fd_count ? &wrset : NULL,
+ fds_err.fd_count ? &fds_err : NULL,
+ timeout >= 0 ? &pending_tv : NULL);
+ vlc_cancel_pop();
- unsigned count = 0;
+ if (fd_cleanup != INVALID_SOCKET)
+ {
+ if (fd_cancel != INVALID_SOCKET)
+ closesocket( fd_cancel );
+ if (FD_ISSET(fd_cleanup, &rdset))
+ {
+ /* the select() was canceled by an APC interrupt */
+ while (nfds > 0)
+ WSACloseEvent(evts[--nfds]);
+ free(evts);
+ errno = EINTR;
+ return -1;
+ }
+ }
+
+ count = 0;
for (unsigned i = 0; i < nfds; i++)
{
+ /* also check extra events that may be available */
WSANETWORKEVENTS ne;
if (WSAEnumNetworkEvents(fds[i].fd, evts[i], &ne))
+ {
memset(&ne, 0, sizeof (ne));
+ if (WSAGetLastError() == WSAENOTSOCK)
+ fds[i].revents |= POLLNVAL;
+ }
WSAEventSelect(fds[i].fd, evts[i], 0);
WSACloseEvent(evts[i]);
- if (ne.lNetworkEvents & FD_CONNECT)
- {
- fds[i].revents |= POLLWRNORM;
- if (ne.iErrorCode[FD_CONNECT_BIT] != 0)
- fds[i].revents |= POLLERR;
- }
if (ne.lNetworkEvents & FD_CLOSE)
{
- fds[i].revents |= (fds[i].events & POLLRDNORM) | POLLHUP;
+ fds[i].revents |= POLLRDNORM | POLLHUP;
if (ne.iErrorCode[FD_CLOSE_BIT] != 0)
fds[i].revents |= POLLERR;
}
- if (ne.lNetworkEvents & FD_ACCEPT)
- {
- fds[i].revents |= POLLRDNORM;
- if (ne.iErrorCode[FD_ACCEPT_BIT] != 0)
- fds[i].revents |= POLLERR;
- }
if (ne.lNetworkEvents & FD_OOB)
{
fds[i].revents |= POLLPRI;
if (ne.iErrorCode[FD_OOB_BIT] != 0)
fds[i].revents |= POLLERR;
}
- if (ne.lNetworkEvents & FD_READ)
- {
+ if (FD_ISSET(fds[i].fd, &rdset))
fds[i].revents |= POLLRDNORM;
- if (ne.iErrorCode[FD_READ_BIT] != 0)
- fds[i].revents |= POLLERR;
- }
- if (ne.lNetworkEvents & FD_WRITE)
- {
+ if (FD_ISSET(fds[i].fd, &wrset))
fds[i].revents |= POLLWRNORM;
- if (ne.iErrorCode[FD_WRITE_BIT] != 0)
- fds[i].revents |= POLLERR;
- }
+ if (FD_ISSET(fds[i].fd, &fds_err))
+ fds[i].revents |= POLLERR;
+
+ fds[i].revents &= fds[i].events | POLLERR | POLLHUP | POLLNVAL;
count += fds[i].revents != 0;
}
free(evts);
- if (count == 0 && ret == WSA_WAIT_IO_COMPLETION)
- {
- errno = EINTR;
- return -1;
- }
return count;
}
#endif
--
2.6.0.windows.1
More information about the vlc-devel
mailing list