Mercurial > gnulib
view lib/poll.c @ 10334:640791e12ea5
poll-win32
author | Paolo Bonzini <bonzini@gnu.org> |
---|---|
date | Tue, 19 Aug 2008 07:59:49 +0200 |
parents | b48c3c82f4d9 |
children | e38a464bfa5f |
line wrap: on
line source
/* Emulation for poll(2) Contributed by Paolo Bonzini. Copyright 2001, 2002, 2003, 2006, 2007, 2008 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 General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <config.h> #include <sys/types.h> #include "poll.h" #include <errno.h> #include <limits.h> #ifdef __MSVCRT__ #include <windows.h> #include <winsock2.h> #include <io.h> #include <stdio.h> #include <conio.h> #else #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 <sys/time.h> #include <time.h> #ifndef INFTIM #define INFTIM (-1) #endif /* BeOS does not have MSG_PEEK. */ #ifndef MSG_PEEK #define MSG_PEEK 0 #endif #ifdef __MSVCRT__ /* 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 u { NTSTATUS Status; PVOID Pointer; }; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; #define FilePipeLocalInformation 24 typedef NTSTATUS (NTAPI *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. */ static int win32_compute_revents (HANDLE h, int sought) { int i, ret, happened; INPUT_RECORD *irbuffer; DWORD avail, nbuffer; IO_STATUS_BLOCK iosb; FILE_PIPE_LOCAL_INFORMATION fpli; static PNtQueryInformationFile NtQueryInformationFile; ret = WaitForSingleObject (h, 0); if (ret != WAIT_OBJECT_0) return sought & (POLLOUT | POLLWRNORM | POLLWRBAND); switch (GetFileType (h)) { case FILE_TYPE_PIPE: if (!NtQueryInformationFile) NtQueryInformationFile = (PNtQueryInformationFile) GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQueryInformationFile"); happened = 0; if (!PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL)) return POLLERR; if (avail) happened |= sought & (POLLIN | POLLRDNORM); memset (&iosb, 0, sizeof (iosb)); memset (&fpli, 0, sizeof (fpli)); /* If NtQueryInformationFile fails, optimistically assume the pipe is writable. This could happen on Win9x, because 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 WinXP SP2; WINE seems fine too). Otherwise, ensure that enough space is available for atomic writes. */ if (NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), FilePipeLocalInformation) || fpli.WriteQuotaAvailable >= PIPE_BUF || (fpli.OutboundQuota < PIPE_BUF && fpli.WriteQuotaAvailable == fpli.OutboundQuota)) happened |= sought & (POLLOUT | POLLWRNORM | POLLWRBAND); return happened; case FILE_TYPE_CHAR: nbuffer = avail = 0; bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); if (!bRet || nbuffer == 0) return POLLHUP; 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 sought & ~(POLLPRI | POLLRDBAND); return sought & (POLLOUT | POLLWRNORM | POLLWRBAND); default: return sought & ~(POLLPRI | POLLRDBAND); } } /* Convert fd_sets returned by select into revents values. */ static int win32_compute_revents_socket (SOCKET h, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds) { int happened = 0; if (FD_ISSET (h, rfds)) { int r, error; char data[64]; WSASetLastError (0); r = recv (h, data, sizeof (data), MSG_PEEK); error = WSAGetLastError (); WSASetLastError (0); if (r == 0) happened |= POLLHUP; /* If the event happened on an unconnected server socket, that's fine. */ else if (r > 0 || ( /* (r == -1) && */ error == ENOTCONN)) happened |= (POLLIN | POLLRDNORM) & sought; /* Distinguish hung-up sockets from other errors. */ else if (error == WSAESHUTDOWN || error == WSAECONNRESET || error == WSAECONNABORTED || error == WSAENETRESET) happened |= POLLHUP; else happened |= POLLERR; } if (FD_ISSET (h, wfds)) happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; if (FD_ISSET (h, efds)) 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; 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; 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 poll (pfd, nfd, timeout) struct pollfd *pfd; nfds_t nfd; int timeout; { #ifndef __MSVCRT__ fd_set rfds, wfds, efds; struct timeval tv; struct timeval *ptv; 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 */ /* EFAULT is not necessary to implement, but let's do it in the simplest case. */ if (!pfd) { 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; } /* 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; /* Windows use a linear array of sockets (of size FD_SETSIZE). The descriptor value is not used to address the array. */ #if defined __CYGWIN__ || (!defined _WIN32 && !defined __WIN32__) if (maxfd > FD_SETSIZE) { errno = EOVERFLOW; return -1; } #endif } } /* 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 fd_set rfds, wfds, efds; static struct timeval tv0; struct timeval tv = { 0, 0 }; struct timeval *ptv; static HANDLE hEvent; HANDLE handle_array[FD_SET_SIZE + 2]; DWORD ret, wait_timeout, nhandles; int nsock; BOOL bRet; MSG msg; char sockbuf[256]; int rc; nfds_t i; if (nfd < 0 || nfd > FD_SET_SIZE || timeout < 0) { errno = EINVAL; return -1; } if (!hEvent) hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); handle_array[0] = hEvent; nhandles = 1; nsock = 0; /* Classify socket handles and create fd sets. */ FD_ZERO (&rfds); FD_ZERO (&wfds); FD_ZERO (&efds); for (i = 0; i < nfd; i++) { if (pfd[i].fd < 0) continue; h = (HANDLE) _get_osfhandle (i); assert (h != NULL); optlen = sizeof(sockbuf); if ((getsockopt ((SOCKET) h, SOL_SOCKET, SO_TYPE, sockbuf, &optlen) != SOCKET_ERROR) || WSAGetLastError() != WSAENOTSOCK) { int ev = 0; /* see above; socket handles are mapped onto select. */ if (pfd[i].events & (POLLIN | POLLRDNORM)) { FD_SET (pfd[i].fd, &rfds); ev |= FD_READ | FD_ACCEPT; } if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { FD_SET (pfd[i].fd, &wfds); ev |= FD_WRITE | FD_CONNECT; } if (pfd[i].events & (POLLPRI | POLLRDBAND)) { FD_SET (pfd[i].fd, &efds); ev |= FD_OOB; } if (ev) { WSAEventSelect ((SOCKET) h, hEvent, ev); nsock++; } } else { if (pfd[i].events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND)) handle_array[nhandles++] = h; } } 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 */ while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) { TranslateMessage (&msg); DispatchMessage (&msg); } } else break; } /* Now check if the sockets have some event set. */ select (nsock + 1, rfds, wfds, efds, &tv0); /* 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) { pfd[i].revents = 0; continue; } h = (HANDLE) _get_osfhandle (i); if (h != handle_array[nhandles]) { /* It's a socket. */ WSAEventSelect (h, 0, 0); happened = win32_compute_revents_socket ((SOCKET) h, pfd[i].events, &rfds, &wfds, &efds); } else { /* Not a socket. */ nhandles++; happened = win32_compute_revents (h, pfd[i].events); } if (happened) { pfd[i].revents = happened; rc++; } } return rc; #endif }