Currently, Windows versions of pacat and friends fail because the current poll emulation is not sufficient (it only works for socket fds). Luckily Gnulib has a much better emulation that seems to work good enough. The implementation has been largely copied (except a few bug fix regarding timeout handling, to be pushed upstream) and works on pipes and files as well. The copy has been obtained through their gnulib-tool utility, which gives a LGPLv2.1+ licensed file. This fixes the "Assertion (!e->dead) failed" error coming and lets pacat and friends stream happily to/from a server (I didn't actually test parec). --- src/Makefile.am | 7 +- src/pulse/mainloop.c | 18 -- src/pulsecore/poll-posix.c | 237 ++++++++++++++++ src/pulsecore/poll-win32.c | 639 ++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/poll.c | 237 ---------------- 5 files changed, 882 insertions(+), 256 deletions(-) create mode 100644 src/pulsecore/poll-posix.c create mode 100644 src/pulsecore/poll-win32.c delete mode 100644 src/pulsecore/poll.c diff --git a/src/Makefile.am b/src/Makefile.am index 88054cb..ed6f8e2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -604,7 +604,6 @@ libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES = \ pulsecore/pdispatch.c pulsecore/pdispatch.h \ pulsecore/pid.c pulsecore/pid.h \ pulsecore/pipe.c pulsecore/pipe.h \ - pulsecore/poll.c pulsecore/poll.h \ pulsecore/memtrap.c pulsecore/memtrap.h \ pulsecore/aupdate.c pulsecore/aupdate.h \ pulsecore/proplist-util.c pulsecore/proplist-util.h \ @@ -627,6 +626,12 @@ libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES = \ pulsecore/sndfile-util.c pulsecore/sndfile-util.h \ pulsecore/socket.h +if OS_IS_WIN32 +libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/poll-win32.c pulsecore/poll.h +else +libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/poll-posix.c pulsecore/poll.h +endif + libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version libpulsecommon_ at PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSNDFILE_LIBS) diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 5c0345e..725bdb4 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -175,24 +175,6 @@ static pa_io_event* mainloop_io_new( e->callback = callback; e->userdata = userdata; -#ifdef OS_IS_WIN32 - { - fd_set xset; - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = 0; - - FD_ZERO (&xset); - FD_SET (fd, &xset); - - if ((select(fd, NULL, NULL, &xset, &tv) == -1) && (WSAGetLastError() == WSAENOTSOCK)) { - pa_log_warn("Cannot monitor non-socket file descriptors."); - e->dead = TRUE; - } - } -#endif - PA_LLIST_PREPEND(pa_io_event, m->io_events, e); m->rebuild_pollfds = TRUE; m->n_io_events ++; diff --git a/src/pulsecore/poll-posix.c b/src/pulsecore/poll-posix.c new file mode 100644 index 0000000..cd888b5 --- /dev/null +++ b/src/pulsecore/poll-posix.c @@ -0,0 +1,237 @@ + +/*** + This file is part of PulseAudio. + + Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/*** + Based on work for the GNU C Library. + Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc. +***/ + +/* Poll the file descriptors described by the NFDS structures starting at + FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for + an event to occur; if TIMEOUT is -1, block until an event occurs. + Returns the number of file descriptors with events, zero if timed out, + or -1 for errors. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include <errno.h> +#include <fcntl.h> + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#include <pulsecore/socket.h> +#include <pulsecore/core-util.h> +#include <pulse/util.h> + +#include "poll.h" + +/* Mac OSX fails to implement poll() in a working way since 10.4. IOW, for + * several years. We need to enable a dirty workaround and emulate that call + * with select(), just like for Windows. sic! */ + +#if !defined(HAVE_POLL_H) || defined(OS_IS_DARWIN) + +int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) { + struct timeval tv; + fd_set rset, wset, xset; + struct pollfd *f; + int ready; + int maxfd = 0; +#ifdef OS_IS_WIN32 + char data[64]; +#endif + + FD_ZERO (&rset); + FD_ZERO (&wset); + FD_ZERO (&xset); + + if (nfds == 0) { + if (timeout >= 0) { + pa_msleep(timeout); + return 0; + } + +#ifdef OS_IS_WIN32 + /* + * Windows does not support signals properly so waiting for them would + * mean a deadlock. + */ + pa_msleep(100); + return 0; +#else + return select(0, NULL, NULL, NULL, NULL); +#endif + } + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd != -1) { + if (f->events & POLLIN) + FD_SET (f->fd, &rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &xset); + if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) + maxfd = f->fd; + } + } + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + ready = select(maxfd + 1, &rset, &wset, &xset, (timeout == -1 ? NULL : &tv)); + + if ((ready == -1) && (errno == EBADF)) { + ready = 0; + maxfd = -1; + +#ifdef OS_IS_WIN32 + /* + * Windows has no fcntl(), so we have to trick around with more + * select() calls to find out what went wrong + */ + + FD_ZERO (&rset); + FD_ZERO (&wset); + FD_ZERO (&xset); + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd != -1) { + fd_set sngl_rset, sngl_wset, sngl_xset; + + FD_ZERO (&sngl_rset); + FD_ZERO (&sngl_wset); + FD_ZERO (&sngl_xset); + + if (f->events & POLLIN) + FD_SET (f->fd, &sngl_rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &sngl_wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &sngl_xset); + if (f->events & (POLLIN|POLLOUT|POLLPRI)) { + struct timeval singl_tv; + + singl_tv.tv_sec = 0; + singl_tv.tv_usec = 0; + + if (select(f->fd, &rset, &wset, &xset, &singl_tv) != -1) { + if (f->events & POLLIN) + FD_SET (f->fd, &rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &xset); + if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) + maxfd = f->fd; + ++ready; + } else if (errno == EBADF) + f->revents |= POLLNVAL; + } + } + } + +#else /* !OS_IS_WIN32 */ + + for (f = fds; f < &fds[nfds]; f++) + if (f->fd != -1) { + /* use fcntl() to find out whether the descriptor is valid */ + if (fcntl(f->fd, F_GETFL) != -1) { + if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) { + maxfd = f->fd; + ready++; + } + } else { + FD_CLR(f->fd, &rset); + FD_CLR(f->fd, &wset); + FD_CLR(f->fd, &xset); + } + } + +#endif + + if (ready) { + /* Linux alters the tv struct... but it shouldn't matter here ... + * as we're going to be a little bit out anyway as we've just eaten + * more than a couple of cpu cycles above */ + ready = select(maxfd + 1, &rset, &wset, &xset, (timeout == -1 ? NULL : &tv)); + } + } + +#ifdef OS_IS_WIN32 + errno = WSAGetLastError(); +#endif + + if (ready > 0) { + ready = 0; + for (f = fds; f < &fds[nfds]; ++f) { + f->revents = 0; + if (f->fd != -1) { + if (FD_ISSET (f->fd, &rset)) { + /* support for POLLHUP. An hung up descriptor does not + increase the return value! */ +#ifdef OS_IS_DARWIN + /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK + * for some kinds of descriptors. Detect if this descriptor is a + * connected socket, a server socket, or something else using a + * 0-byte recv, and use ioctl(2) to detect POLLHUP. */ + int r = recv(f->fd, NULL, 0, MSG_PEEK); + if (r == 0 || (r < 0 && errno == ENOTSOCK)) + ioctl(f->fd, FIONREAD, &r); + + if (r == 0) + f->revents |= POLLHUP; +#else /* !OS_IS_DARWIN */ + if (recv (f->fd, data, 64, MSG_PEEK) == -1) { + if (errno == ESHUTDOWN || errno == ECONNRESET || + errno == ECONNABORTED || errno == ENETRESET) { + fprintf(stderr, "Hangup\n"); + f->revents |= POLLHUP; + } + } +#endif + + if (f->revents == 0) + f->revents |= POLLIN; + } + if (FD_ISSET (f->fd, &wset)) + f->revents |= POLLOUT; + if (FD_ISSET (f->fd, &xset)) + f->revents |= POLLPRI; + } + if (f->revents) + ready++; + } + } + + return ready; +} + +#endif /* HAVE_SYS_POLL_H */ diff --git a/src/pulsecore/poll-win32.c b/src/pulsecore/poll-win32.c new file mode 100644 index 0000000..4256ed7 --- /dev/null +++ b/src/pulsecore/poll-win32.c @@ -0,0 +1,639 @@ + +/* Emulation for poll(2) + Contributed by Paolo Bonzini. + + Copyright 2001-2003, 2006-2012 Free Software Foundation, Inc. + + This file is part of gnulib. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, see <http://www.gnu.org/licenses/>. */ + +/* Tell gcc not to warn about the (nfd < 0) tests, below. */ +#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__ +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <malloc.h> + +#include <sys/types.h> + +/* Specification. */ +#include "poll.h" +typedef unsigned long nfds_t; + +#include <errno.h> +#include <limits.h> +#include <assert.h> + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# define WINDOWS_NATIVE +# include <winsock2.h> +# include <windows.h> +# include <io.h> +# include <stdio.h> +# include <conio.h> +# if 0 +# include "msvc-nothrow.h" +# endif +#else +# include <sys/time.h> +# include <sys/socket.h> +# include <sys/select.h> +# include <unistd.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_FILIO_H +# include <sys/filio.h> +#endif + +#include <time.h> + +#ifndef INFTIM +# define INFTIM (-1) +#endif + +/* BeOS does not have MSG_PEEK. */ +#ifndef MSG_PEEK +# define MSG_PEEK 0 +#endif + +#ifndef POLLRDNORM +# define POLLRDNORM 0 +# define POLLRDBAND 0 +# define POLLWRNORM 0 +# define POLLWRBAND 0 +#endif + +#ifdef WINDOWS_NATIVE + +/* Optimized test whether a HANDLE refers to a console. + See <http://lists.gnu.org/archive/html/bug-gnulib/2009-08/msg00065.html>. */ +#define IsConsoleHandle(h) (((intptr_t) (h) & 3) == 3) + +static BOOL +IsSocketHandle (HANDLE h) +{ + WSANETWORKEVENTS ev; + + if (IsConsoleHandle (h)) + return FALSE; + + /* Under Wine, it seems that getsockopt returns 0 for pipes too. + WSAEnumNetworkEvents instead distinguishes the two correctly. */ + ev.lNetworkEvents = 0xDEADBEEFl; + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + return ev.lNetworkEvents != 0xDEADBEEFl; +} + +static HANDLE +HandleFromFd (int fd) +{ + /* since socket() returns a HANDLE already, try that first */ + if (IsSocketHandle((HANDLE) fd)) + return ((HANDLE) fd); + + return ((HANDLE) _get_osfhandle(fd)); +} + +/* Declare data structures for ntdll functions. */ +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _IO_STATUS_BLOCK +{ + union { + DWORD Status; + PVOID Pointer; + } u; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef enum _FILE_INFORMATION_CLASS { + FilePipeLocalInformation = 24 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef DWORD (WINAPI *PNtQueryInformationFile) + (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); + +# ifndef PIPE_BUF +# define PIPE_BUF 512 +# endif + +/* Compute revents values for file handle H. If some events cannot happen + for the handle, eliminate them from *P_SOUGHT. */ + +static int +windows_compute_revents (HANDLE h, int *p_sought) +{ + int i, ret, happened; + INPUT_RECORD *irbuffer; + DWORD avail, nbuffer; + BOOL bRet; + IO_STATUS_BLOCK iosb; + FILE_PIPE_LOCAL_INFORMATION fpli; + static PNtQueryInformationFile NtQueryInformationFile; + static BOOL once_only; + + switch (GetFileType (h)) + { + case FILE_TYPE_PIPE: + if (!once_only) + { + NtQueryInformationFile = (PNtQueryInformationFile) + GetProcAddress (GetModuleHandle ("ntdll.dll"), + "NtQueryInformationFile"); + once_only = TRUE; + } + + happened = 0; + if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) + { + if (avail) + happened |= *p_sought & (POLLIN | POLLRDNORM); + } + else if (GetLastError () == ERROR_BROKEN_PIPE) + happened |= POLLHUP; + + else + { + /* It was the write-end of the pipe. Check if it is writable. + If NtQueryInformationFile fails, optimistically assume the pipe is + writable. This could happen on Windows 9x, where + NtQueryInformationFile is not available, or if we inherit a pipe + that doesn't permit FILE_READ_ATTRIBUTES access on the write end + (I think this should not happen since Windows XP SP2; WINE seems + fine too). Otherwise, ensure that enough space is available for + atomic writes. */ + memset (&iosb, 0, sizeof (iosb)); + memset (&fpli, 0, sizeof (fpli)); + + if (!NtQueryInformationFile + || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), + FilePipeLocalInformation) + || fpli.WriteQuotaAvailable >= PIPE_BUF + || (fpli.OutboundQuota < PIPE_BUF && + fpli.WriteQuotaAvailable == fpli.OutboundQuota)) + happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); + } + return happened; + + case FILE_TYPE_CHAR: + ret = WaitForSingleObject (h, 0); + if (!IsConsoleHandle (h)) + return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0; + + nbuffer = avail = 0; + bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); + if (bRet) + { + /* Input buffer. */ + *p_sought &= POLLIN | POLLRDNORM; + if (nbuffer == 0) + return POLLHUP; + if (!*p_sought) + return 0; + + irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); + bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); + if (!bRet || avail == 0) + return POLLHUP; + + for (i = 0; i < avail; i++) + if (irbuffer[i].EventType == KEY_EVENT) + return *p_sought; + return 0; + } + else + { + /* Screen buffer. */ + *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND; + return *p_sought; + } + + default: + ret = WaitForSingleObject (h, 0); + if (ret == WAIT_OBJECT_0) + return *p_sought & ~(POLLPRI | POLLRDBAND); + + return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); + } +} + +/* Convert fd_sets returned by select into revents values. */ + +static int +windows_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents) +{ + int happened = 0; + + if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT) + happened |= (POLLIN | POLLRDNORM) & sought; + + else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) + { + int r, error; + + char data[64]; + WSASetLastError (0); + r = recv (h, data, sizeof (data), MSG_PEEK); + error = WSAGetLastError (); + WSASetLastError (0); + + if (r > 0 || error == WSAENOTCONN) + happened |= (POLLIN | POLLRDNORM) & sought; + + /* Distinguish hung-up sockets from other errors. */ + else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET + || error == WSAECONNABORTED || error == WSAENETRESET) + happened |= POLLHUP; + + else + happened |= POLLERR; + } + + if (lNetworkEvents & (FD_WRITE | FD_CONNECT)) + happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; + + if (lNetworkEvents & FD_OOB) + happened |= (POLLPRI | POLLRDBAND) & sought; + + return happened; +} + +#else /* !MinGW */ + +/* Convert select(2) returned fd_sets into poll(2) revents values. */ +static int +compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds) +{ + int happened = 0; + if (FD_ISSET (fd, rfds)) + { + int r; + int socket_errno; + +# if defined __MACH__ && defined __APPLE__ + /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK + for some kinds of descriptors. Detect if this descriptor is a + connected socket, a server socket, or something else using a + 0-byte recv, and use ioctl(2) to detect POLLHUP. */ + r = recv (fd, NULL, 0, MSG_PEEK); + socket_errno = (r < 0) ? errno : 0; + if (r == 0 || socket_errno == ENOTSOCK) + ioctl (fd, FIONREAD, &r); +# else + char data[64]; + r = recv (fd, data, sizeof (data), MSG_PEEK); + socket_errno = (r < 0) ? errno : 0; +# endif + if (r == 0) + happened |= POLLHUP; + + /* If the event happened on an unconnected server socket, + that's fine. */ + else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN)) + happened |= (POLLIN | POLLRDNORM) & sought; + + /* Distinguish hung-up sockets from other errors. */ + else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET + || socket_errno == ECONNABORTED || socket_errno == ENETRESET) + happened |= POLLHUP; + + /* some systems can't use recv() on non-socket, including HP NonStop */ + else if (socket_errno == ENOTSOCK) + happened |= (POLLIN | POLLRDNORM) & sought; + + else + happened |= POLLERR; + } + + if (FD_ISSET (fd, wfds)) + happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; + + if (FD_ISSET (fd, efds)) + happened |= (POLLPRI | POLLRDBAND) & sought; + + return happened; +} +#endif /* !MinGW */ + +int +pa_poll (struct pollfd *pfd, nfds_t nfd, int timeout) +{ + struct timeval tv; + struct timeval *ptv; +#ifndef WINDOWS_NATIVE + fd_set rfds, wfds, efds; + int maxfd, rc; + nfds_t i; + +# ifdef _SC_OPEN_MAX + static int sc_open_max = -1; + + if (nfd < 0 + || (nfd > sc_open_max + && (sc_open_max != -1 + || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX))))) + { + errno = EINVAL; + return -1; + } +# else /* !_SC_OPEN_MAX */ +# ifdef OPEN_MAX + if (nfd < 0 || nfd > OPEN_MAX) + { + errno = EINVAL; + return -1; + } +# endif /* OPEN_MAX -- else, no check is needed */ +# endif /* !_SC_OPEN_MAX */ +#else /* WINDOWS_NATIVE*/ + static HANDLE hEvent; + WSANETWORKEVENTS ev; + HANDLE h, handle_array[FD_SETSIZE + 2]; + DWORD ret, wait_timeout, nhandles; + fd_set rfds, wfds, xfds; + BOOL poll_again; + MSG msg; + int rc = 0; + nfds_t i; +#endif + + /* EFAULT is not necessary to implement, but let's do it in the + simplest case. */ + if (!pfd && nfd) + { + errno = EFAULT; + return -1; + } + + /* convert timeout number into a timeval structure */ + if (timeout == 0) + { + ptv = &tv; + ptv->tv_sec = 0; + ptv->tv_usec = 0; + } + else if (timeout > 0) + { + ptv = &tv; + ptv->tv_sec = timeout / 1000; + ptv->tv_usec = (timeout % 1000) * 1000; + } + else if (timeout == INFTIM) + /* wait forever */ + ptv = NULL; + else + { + errno = EINVAL; + return -1; + } + +#ifndef WINDOWS_NATIVE + /* create fd sets and determine max fd */ + maxfd = -1; + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&efds); + for (i = 0; i < nfd; i++) + { + if (pfd[i].fd < 0) + continue; + + if (pfd[i].events & (POLLIN | POLLRDNORM)) + FD_SET (pfd[i].fd, &rfds); + + /* see select(2): "the only exceptional condition detectable + is out-of-band data received on a socket", hence we push + POLLWRBAND events onto wfds instead of efds. */ + if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) + FD_SET (pfd[i].fd, &wfds); + if (pfd[i].events & (POLLPRI | POLLRDBAND)) + FD_SET (pfd[i].fd, &efds); + if (pfd[i].fd >= maxfd + && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI + | POLLRDNORM | POLLRDBAND + | POLLWRNORM | POLLWRBAND))) + { + maxfd = pfd[i].fd; + if (maxfd > FD_SETSIZE) + { + errno = EOVERFLOW; + return -1; + } + } + } + + /* examine fd sets */ + rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv); + if (rc < 0) + return rc; + + /* establish results */ + rc = 0; + for (i = 0; i < nfd; i++) + if (pfd[i].fd < 0) + pfd[i].revents = 0; + else + { + int happened = compute_revents (pfd[i].fd, pfd[i].events, + &rfds, &wfds, &efds); + if (happened) + { + pfd[i].revents = happened; + rc++; + } + } + + return rc; +#else + + if (!hEvent) + hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + +restart: + handle_array[0] = hEvent; + nhandles = 1; + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&xfds); + + /* Classify socket handles and create fd sets. */ + for (i = 0; i < nfd; i++) + { + int sought = pfd[i].events; + pfd[i].revents = 0; + if (pfd[i].fd < 0) + continue; + if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND + | POLLPRI | POLLRDBAND))) + continue; + + h = HandleFromFd (pfd[i].fd); + assert (h != NULL && h != INVALID_HANDLE_VALUE); + if (IsSocketHandle (h)) + { + int requested = FD_CLOSE; + + /* see above; socket handles are mapped onto select. */ + if (sought & (POLLIN | POLLRDNORM)) + { + requested |= FD_READ | FD_ACCEPT; + FD_SET ((SOCKET) h, &rfds); + } + if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND)) + { + requested |= FD_WRITE | FD_CONNECT; + FD_SET ((SOCKET) h, &wfds); + } + if (sought & (POLLPRI | POLLRDBAND)) + { + requested |= FD_OOB; + FD_SET ((SOCKET) h, &xfds); + } + + if (requested) + WSAEventSelect ((SOCKET) h, hEvent, requested); + } + else + { + /* Poll now. If we get an event, do not poll again. Also, + screen buffer handles are waitable, and they'll block until + a character is available. windows_compute_revents eliminates + bits for the "wrong" direction. */ + pfd[i].revents = windows_compute_revents (h, &sought); + if (sought) + handle_array[nhandles++] = h; + if (pfd[i].revents) + timeout = 0; + else + { + if (!ptv) + ptv = &tv; + /* tune down to 0.25s. But don't touch smaller timeouts */ + if (ptv->tv_usec > 250*1000 || ptv->tv_sec > 0) + ptv->tv_usec = 250*1000; + ptv->tv_sec = 0; + } + } + } + + if (select (0, &rfds, &wfds, &xfds, ptv) > 0) + { + /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but + no need to call select again. */ + poll_again = FALSE; + wait_timeout = 0; + } + else + { + poll_again = TRUE; + if (timeout == INFTIM) + wait_timeout = INFINITE; + else + wait_timeout = timeout; + } + + for (;;) + { + ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, + wait_timeout, QS_ALLINPUT); + + if (ret == WAIT_OBJECT_0 + nhandles) + { + /* new input of some other kind */ + BOOL bRet; + while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + else + break; + } + + if (poll_again) + select (0, &rfds, &wfds, &xfds, ptv); + + /* Place a sentinel at the end of the array. */ + handle_array[nhandles] = NULL; + nhandles = 1; + for (i = 0; i < nfd; i++) + { + int happened; + + if (pfd[i].fd < 0) + continue; + if (!(pfd[i].events & (POLLIN | POLLRDNORM | + POLLOUT | POLLWRNORM | POLLWRBAND))) + continue; + + h = (HANDLE) HandleFromFd (pfd[i].fd); + if (h != handle_array[nhandles]) + { + /* It's a socket. */ + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + WSAEventSelect ((SOCKET) h, 0, 0); + + /* If we're lucky, WSAEnumNetworkEvents already provided a way + to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */ + if (FD_ISSET ((SOCKET) h, &rfds) + && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT))) + ev.lNetworkEvents |= FD_READ | FD_ACCEPT; + if (FD_ISSET ((SOCKET) h, &wfds)) + ev.lNetworkEvents |= FD_WRITE | FD_CONNECT; + if (FD_ISSET ((SOCKET) h, &xfds)) + ev.lNetworkEvents |= FD_OOB; + + happened = windows_compute_revents_socket ((SOCKET) h, pfd[i].events, + ev.lNetworkEvents); + } + else + { + /* Not a socket. */ + int sought = pfd[i].events; + happened = windows_compute_revents (h, &sought); + nhandles++; + } + + if ((pfd[i].revents |= happened) != 0) + rc++; + } + + if (!rc && timeout == INFTIM) + { + SleepEx (1, TRUE); + goto restart; + } + + return rc; +#endif +} diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c deleted file mode 100644 index cd888b5..0000000 --- a/src/pulsecore/poll.c +++ /dev/null @@ -1,237 +0,0 @@ - -/*** - This file is part of PulseAudio. - - Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/*** - Based on work for the GNU C Library. - Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc. -***/ - -/* Poll the file descriptors described by the NFDS structures starting at - FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for - an event to occur; if TIMEOUT is -1, block until an event occurs. - Returns the number of file descriptors with events, zero if timed out, - or -1 for errors. */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#include <errno.h> -#include <fcntl.h> - -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - -#include <pulsecore/socket.h> -#include <pulsecore/core-util.h> -#include <pulse/util.h> - -#include "poll.h" - -/* Mac OSX fails to implement poll() in a working way since 10.4. IOW, for - * several years. We need to enable a dirty workaround and emulate that call - * with select(), just like for Windows. sic! */ - -#if !defined(HAVE_POLL_H) || defined(OS_IS_DARWIN) - -int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) { - struct timeval tv; - fd_set rset, wset, xset; - struct pollfd *f; - int ready; - int maxfd = 0; -#ifdef OS_IS_WIN32 - char data[64]; -#endif - - FD_ZERO (&rset); - FD_ZERO (&wset); - FD_ZERO (&xset); - - if (nfds == 0) { - if (timeout >= 0) { - pa_msleep(timeout); - return 0; - } - -#ifdef OS_IS_WIN32 - /* - * Windows does not support signals properly so waiting for them would - * mean a deadlock. - */ - pa_msleep(100); - return 0; -#else - return select(0, NULL, NULL, NULL, NULL); -#endif - } - - for (f = fds; f < &fds[nfds]; ++f) { - if (f->fd != -1) { - if (f->events & POLLIN) - FD_SET (f->fd, &rset); - if (f->events & POLLOUT) - FD_SET (f->fd, &wset); - if (f->events & POLLPRI) - FD_SET (f->fd, &xset); - if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) - maxfd = f->fd; - } - } - - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - ready = select(maxfd + 1, &rset, &wset, &xset, (timeout == -1 ? NULL : &tv)); - - if ((ready == -1) && (errno == EBADF)) { - ready = 0; - maxfd = -1; - -#ifdef OS_IS_WIN32 - /* - * Windows has no fcntl(), so we have to trick around with more - * select() calls to find out what went wrong - */ - - FD_ZERO (&rset); - FD_ZERO (&wset); - FD_ZERO (&xset); - - for (f = fds; f < &fds[nfds]; ++f) { - if (f->fd != -1) { - fd_set sngl_rset, sngl_wset, sngl_xset; - - FD_ZERO (&sngl_rset); - FD_ZERO (&sngl_wset); - FD_ZERO (&sngl_xset); - - if (f->events & POLLIN) - FD_SET (f->fd, &sngl_rset); - if (f->events & POLLOUT) - FD_SET (f->fd, &sngl_wset); - if (f->events & POLLPRI) - FD_SET (f->fd, &sngl_xset); - if (f->events & (POLLIN|POLLOUT|POLLPRI)) { - struct timeval singl_tv; - - singl_tv.tv_sec = 0; - singl_tv.tv_usec = 0; - - if (select(f->fd, &rset, &wset, &xset, &singl_tv) != -1) { - if (f->events & POLLIN) - FD_SET (f->fd, &rset); - if (f->events & POLLOUT) - FD_SET (f->fd, &wset); - if (f->events & POLLPRI) - FD_SET (f->fd, &xset); - if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) - maxfd = f->fd; - ++ready; - } else if (errno == EBADF) - f->revents |= POLLNVAL; - } - } - } - -#else /* !OS_IS_WIN32 */ - - for (f = fds; f < &fds[nfds]; f++) - if (f->fd != -1) { - /* use fcntl() to find out whether the descriptor is valid */ - if (fcntl(f->fd, F_GETFL) != -1) { - if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) { - maxfd = f->fd; - ready++; - } - } else { - FD_CLR(f->fd, &rset); - FD_CLR(f->fd, &wset); - FD_CLR(f->fd, &xset); - } - } - -#endif - - if (ready) { - /* Linux alters the tv struct... but it shouldn't matter here ... - * as we're going to be a little bit out anyway as we've just eaten - * more than a couple of cpu cycles above */ - ready = select(maxfd + 1, &rset, &wset, &xset, (timeout == -1 ? NULL : &tv)); - } - } - -#ifdef OS_IS_WIN32 - errno = WSAGetLastError(); -#endif - - if (ready > 0) { - ready = 0; - for (f = fds; f < &fds[nfds]; ++f) { - f->revents = 0; - if (f->fd != -1) { - if (FD_ISSET (f->fd, &rset)) { - /* support for POLLHUP. An hung up descriptor does not - increase the return value! */ -#ifdef OS_IS_DARWIN - /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK - * for some kinds of descriptors. Detect if this descriptor is a - * connected socket, a server socket, or something else using a - * 0-byte recv, and use ioctl(2) to detect POLLHUP. */ - int r = recv(f->fd, NULL, 0, MSG_PEEK); - if (r == 0 || (r < 0 && errno == ENOTSOCK)) - ioctl(f->fd, FIONREAD, &r); - - if (r == 0) - f->revents |= POLLHUP; -#else /* !OS_IS_DARWIN */ - if (recv (f->fd, data, 64, MSG_PEEK) == -1) { - if (errno == ESHUTDOWN || errno == ECONNRESET || - errno == ECONNABORTED || errno == ENETRESET) { - fprintf(stderr, "Hangup\n"); - f->revents |= POLLHUP; - } - } -#endif - - if (f->revents == 0) - f->revents |= POLLIN; - } - if (FD_ISSET (f->fd, &wset)) - f->revents |= POLLOUT; - if (FD_ISSET (f->fd, &xset)) - f->revents |= POLLPRI; - } - if (f->revents) - ready++; - } - } - - return ready; -} - -#endif /* HAVE_SYS_POLL_H */ -- 1.7.10.4