[vlc-commits] compat: reimplement and make poll() alertable on Windows
Rémi Denis-Courmont
git at videolan.org
Sun May 17 13:50:19 CEST 2015
vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Sat May 9 14:50:11 2015 +0300| [74337dd2f20a6ca2786eea67f73232ce87d94026] | committer: Rémi Denis-Courmont
compat: reimplement and make poll() alertable on Windows
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=74337dd2f20a6ca2786eea67f73232ce87d94026
---
compat/poll.c | 201 ++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 148 insertions(+), 53 deletions(-)
diff --git a/compat/poll.c b/compat/poll.c
index 825713f..3fa4e7a 100644
--- a/compat/poll.c
+++ b/compat/poll.c
@@ -26,41 +26,14 @@
#include <string.h>
#include <errno.h>
-#ifdef _WIN32
-# ifdef FD_SETSIZE
-/* Too late for #undef FD_SETSIZE to work: fd_set is already defined. */
-# error Header inclusion order compromised!
-# endif
-# define FD_SETSIZE 0
-# include <winsock2.h>
-#else
+#ifndef _WIN32
# include <sys/time.h>
# include <sys/select.h>
# include <fcntl.h>
-#endif
int (poll) (struct pollfd *fds, unsigned nfds, int timeout)
{
-#ifdef _WIN32
- size_t setsize = sizeof (fd_set) + nfds * sizeof (SOCKET);
- fd_set *rdset = malloc (setsize);
- fd_set *wrset = malloc (setsize);
- fd_set *exset = malloc (setsize);
-
- if (rdset == NULL || wrset == NULL || exset == NULL)
- {
- free (rdset);
- free (wrset);
- free (exset);
- errno = ENOMEM;
- return -1;
- }
-/* Winsock FD_SET uses FD_SETSIZE in its expansion */
-# undef FD_SETSIZE
-# define FD_SETSIZE (nfds)
-#else
fd_set rdset[1], wrset[1], exset[1];
-#endif
struct timeval tv = { 0, 0 };
int val = -1;
@@ -81,22 +54,12 @@ int (poll) (struct pollfd *fds, unsigned nfds, int timeout)
* default, such that it is quite feasible to get fd >= FD_SETSIZE.
* The next instructions will result in a buffer overflow if run on
* a POSIX system, and the later FD_ISSET would perform an undefined
- * memory read.
- *
- * With Winsock, fd_set is a table of integers. This is awfully slow.
- * However, FD_SET and FD_ISSET silently and safely discard excess
- * entries. Here, overflow cannot happen anyway: fd_set of adequate
- * size are allocated.
- * Note that Vista has a much nicer WSAPoll(), but Mingw does not
- * support it yet.
- */
-#ifndef _WIN32
+ * memory read. */
if ((unsigned)fd >= FD_SETSIZE)
{
errno = EINVAL;
return -1;
}
-#endif
if (fds[i].events & POLLRDNORM)
FD_SET (fd, rdset);
if (fds[i].events & POLLWRNORM)
@@ -116,22 +79,13 @@ int (poll) (struct pollfd *fds, unsigned nfds, int timeout)
(timeout >= 0) ? &tv : NULL);
if (val == -1)
{
-#ifndef _WIN32
if (errno != EBADF)
-#else
- if (WSAGetLastError () != WSAENOTSOCK)
-#endif
return -1;
val = 0;
for (unsigned i = 0; i < nfds; i++)
-#ifndef _WIN32
if (fcntl (fds[i].fd, F_GETFD) == -1)
-#else
- if (getsockopt (fds[i].fd, SOL_SOCKET, SO_REUSEADDR,
- &(DWORD){ 0 }, &(int){ sizeof (DWORD) }) != 0)
-#endif
{
fds[i].revents = POLLNVAL;
val++;
@@ -149,10 +103,151 @@ int (poll) (struct pollfd *fds, unsigned nfds, int timeout)
| (FD_ISSET (fd, wrset) ? POLLWRNORM : 0)
| (FD_ISSET (fd, exset) ? POLLPRI : 0);
}
-#ifdef _WIN32
- free (exset);
- free (wrset);
- free (rdset);
-#endif
return val;
}
+#else
+# include <windows.h>
+# include <winsock2.h>
+
+int poll(struct pollfd *fds, unsigned nfds, int timeout)
+{
+ DWORD to = (timeout >= 0) ? (DWORD)timeout : INFINITE;
+
+ if (nfds == 0)
+ { /* WSAWaitForMultipleEvents() does not allow zero events */
+ if (SleepEx(to, TRUE))
+ {
+ errno = EINTR;
+ return -1;
+ }
+ return 0;
+ }
+
+ 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 (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;
+
+ 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 (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;
+
+ if (WSAEnumNetworkEvents(fds[i].fd, evts[i], &ne))
+ memset(&ne, 0, sizeof (ne));
+ 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;
+ 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)
+ {
+ 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;
+ }
+
+ free(evts);
+
+ if (count == 0 && ret == WSA_WAIT_IO_COMPLETION)
+ {
+ errno = EINTR;
+ return -1;
+ }
+ return count;
+}
+#endif
More information about the vlc-commits
mailing list