X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fpoll.c;h=da0484482b54aad20b602df44e7480d7b9693482;hb=671c9011cb6944a63f0d15f60252c31bf0ee8e9b;hp=ffa6ab0b115a29dcf00deeda4bb915c7355cf9f8;hpb=96ee65902e40c618aa07b6652152ecd5674815f2;p=gnulib.git diff --git a/lib/poll.c b/lib/poll.c index ffa6ab0b1..da0484482 100644 --- a/lib/poll.c +++ b/lib/poll.c @@ -20,19 +20,23 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include +#include #include #include "poll.h" #include #include +#include -#ifdef __MSVCRT__ -#include +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +#define WIN32_NATIVE #include +#include #include #include #include #else +#include #include #include #include @@ -45,7 +49,6 @@ #include #endif -#include #include #ifndef INFTIM @@ -57,7 +60,7 @@ #define MSG_PEEK 0 #endif -#ifdef __MSVCRT__ +#ifdef WIN32_NATIVE /* Declare data structures for ntdll functions. */ typedef struct _FILE_PIPE_LOCAL_INFORMATION { @@ -75,16 +78,18 @@ typedef struct _FILE_PIPE_LOCAL_INFORMATION { typedef struct _IO_STATUS_BLOCK { - union u { - NTSTATUS Status; + union { + DWORD Status; PVOID Pointer; - }; + } u; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; -#define FilePipeLocalInformation 24 +typedef enum _FILE_INFORMATION_CLASS { + FilePipeLocalInformation = 24 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; -typedef NTSTATUS (NTAPI *PNtQueryInformationFile) +typedef DWORD (WINAPI *PNtQueryInformationFile) (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); #ifndef PIPE_BUF @@ -99,78 +104,94 @@ win32_compute_revents (HANDLE h, int 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; - - ret = WaitForSingleObject (h, 0); - if (ret != WAIT_OBJECT_0) - return sought & (POLLOUT | POLLWRNORM | POLLWRBAND); + static BOOL once_only; switch (GetFileType (h)) { case FILE_TYPE_PIPE: - if (!NtQueryInformationFile) - NtQueryInformationFile = (PNtQueryInformationFile) - GetProcAddress (GetModuleHandle ("ntdll.dll"), - "NtQueryInformationFile"); + if (!once_only) + { + NtQueryInformationFile = (PNtQueryInformationFile) + GetProcAddress (GetModuleHandle ("ntdll.dll"), + "NtQueryInformationFile"); + once_only = TRUE; + } 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); + if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) + { + if (avail) + happened |= sought & (POLLIN | POLLRDNORM); + } + 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 Win9x, 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 WinXP 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 |= 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); + ret = WaitForSingleObject (h, 0); + if (ret == WAIT_OBJECT_0) + { + 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); + } + break; default: - return sought & ~(POLLPRI | POLLRDBAND); + ret = WaitForSingleObject (h, 0); + if (ret == WAIT_OBJECT_0) + return sought & ~(POLLPRI | POLLRDBAND); + + break; } + + return sought & (POLLOUT | POLLWRNORM | POLLWRBAND); } /* 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) +win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents) { int happened = 0; - if (FD_ISSET (h, rfds)) + 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; @@ -180,27 +201,22 @@ win32_compute_revents_socket (SOCKET h, int sought, 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)) + if (r > 0 || error == WSAENOTCONN) happened |= (POLLIN | POLLRDNORM) & sought; /* Distinguish hung-up sockets from other errors. */ - else if (error == WSAESHUTDOWN || error == WSAECONNRESET - || error == WSAECONNABORTED || error == WSAENETRESET) + else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET + || error == WSAECONNABORTED || error == WSAENETRESET) happened |= POLLHUP; else happened |= POLLERR; } - if (FD_ISSET (h, wfds)) + if (lNetworkEvents & (FD_WRITE | FD_CONNECT)) happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; - if (FD_ISSET (h, efds)) + if (lNetworkEvents & FD_OOB) happened |= (POLLPRI | POLLRDBAND) & sought; return happened; @@ -212,7 +228,7 @@ win32_compute_revents_socket (SOCKET h, int sought, static int compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds) { - int happened; + int happened = 0; if (FD_ISSET (fd, rfds)) { int r; @@ -265,7 +281,7 @@ poll (pfd, nfd, timeout) nfds_t nfd; int timeout; { -#ifndef __MSVCRT__ +#ifndef WIN32_NATIVE fd_set rfds, wfds, efds; struct timeval tv; struct timeval *ptv; @@ -349,16 +365,11 @@ poll (pfd, nfd, timeout) | 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 } } @@ -385,21 +396,19 @@ poll (pfd, nfd, timeout) 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]; + WSANETWORKEVENTS ev; + HANDLE h, handle_array[FD_SETSIZE + 2]; DWORD ret, wait_timeout, nhandles; - int nsock; - BOOL bRet; + fd_set rfds, wfds, xfds; + BOOL poll_again; MSG msg; char sockbuf[256]; int rc; nfds_t i; - if (nfd < 0 || nfd > FD_SET_SIZE || timeout < 0) + if (nfd < 0 || timeout < -1) { errno = EINVAL; return -1; @@ -410,60 +419,78 @@ poll (pfd, nfd, timeout) handle_array[0] = hEvent; nhandles = 1; - nsock = 0; - - /* Classify socket handles and create fd sets. */ FD_ZERO (&rfds); FD_ZERO (&wfds); - FD_ZERO (&efds); + FD_ZERO (&xfds); + + /* Classify socket handles and create fd sets. */ for (i = 0; i < nfd; i++) { + size_t optlen = sizeof(sockbuf); + pfd[i].revents = 0; if (pfd[i].fd < 0) continue; + if (!(pfd[i].events & (POLLIN | POLLRDNORM | + POLLOUT | POLLWRNORM | POLLWRBAND))) + continue; - h = (HANDLE) _get_osfhandle (i); + h = (HANDLE) _get_osfhandle (pfd[i].fd); assert (h != NULL); - optlen = sizeof(sockbuf); - if ((getsockopt ((SOCKET) h, SOL_SOCKET, SO_TYPE, sockbuf, &optlen) - != SOCKET_ERROR) - || WSAGetLastError() != WSAENOTSOCK) + + /* Under Wine, it seems that getsockopt returns 0 for pipes too. + WSAEnumNetworkEvents instead distinguishes the two correctly. */ + ev.lNetworkEvents = 0xDEADBEEF; + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + if (ev.lNetworkEvents != 0xDEADBEEF) { - int ev = 0; + int requested = FD_CLOSE; /* 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; - } + { + requested |= FD_READ | FD_ACCEPT; + FD_SET ((SOCKET) h, &rfds); + } if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) - { - FD_SET (pfd[i].fd, &wfds); - ev |= FD_WRITE | FD_CONNECT; - } + { + requested |= FD_WRITE | FD_CONNECT; + FD_SET ((SOCKET) h, &wfds); + } if (pfd[i].events & (POLLPRI | POLLRDBAND)) - { - FD_SET (pfd[i].fd, &efds); - ev |= FD_OOB; - } - if (ev) - { - WSAEventSelect ((SOCKET) h, hEvent, ev); - nsock++; - } + { + requested |= FD_OOB; + FD_SET ((SOCKET) h, &xfds); + } + + if (requested) + WSAEventSelect ((SOCKET) h, hEvent, requested); } else { - if (pfd[i].events & (POLLIN | POLLRDNORM | - POLLOUT | POLLWRNORM | POLLWRBAND)) - handle_array[nhandles++] = h; + handle_array[nhandles++] = h; + + /* Poll now. If we get an event, do not poll again. */ + pfd[i].revents = win32_compute_revents (h, pfd[i].events); + if (pfd[i].revents) + wait_timeout = 0; } } - if (timeout == INFTIM) - wait_timeout = INFINITE; + if (select (0, &rfds, &wfds, &xfds, &tv0) > 0) + { + /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but + no need to call select again. */ + poll_again = FALSE; + wait_timeout = 0; + } else - wait_timeout = timeout; + { + poll_again = TRUE; + if (timeout == INFTIM) + wait_timeout = INFINITE; + else + wait_timeout = timeout; + } for (;;) { @@ -473,6 +500,7 @@ poll (pfd, nfd, timeout) 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); @@ -483,8 +511,8 @@ poll (pfd, nfd, timeout) break; } - /* Now check if the sockets have some event set. */ - select (nsock + 1, rfds, wfds, efds, &tv0); + if (poll_again) + select (0, &rfds, &wfds, &xfds, &tv0); /* Place a sentinel at the end of the array. */ handle_array[nhandles] = NULL; @@ -494,18 +522,30 @@ poll (pfd, nfd, timeout) int happened; if (pfd[i].fd < 0) - { - pfd[i].revents = 0; - continue; - } + continue; + if (!(pfd[i].events & (POLLIN | POLLRDNORM | + POLLOUT | POLLWRNORM | POLLWRBAND))) + continue; - h = (HANDLE) _get_osfhandle (i); + h = (HANDLE) _get_osfhandle (pfd[i].fd); if (h != handle_array[nhandles]) { /* It's a socket. */ - WSAEventSelect (h, 0, 0); + 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 = win32_compute_revents_socket ((SOCKET) h, pfd[i].events, - &rfds, &wfds, &efds); + ev.lNetworkEvents); } else { @@ -514,11 +554,8 @@ poll (pfd, nfd, timeout) happened = win32_compute_revents (h, pfd[i].events); } - if (happened) - { - pfd[i].revents = happened; - rc++; - } + if ((pfd[i].revents |= happened) != 0) + rc++; } return rc;