mirror of https://code.videolan.org/videolan/vlc
compat: reimplement and make poll() alertable on Windows
This commit is contained in:
parent
80170ba796
commit
74337dd2f2
201
compat/poll.c
201
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
|
||||
|
|
Loading…
Reference in New Issue