At 02:34 PM 6/16/2003, Juan Rivera wrote: >Instead of using Select, use WSACreateEvent and WSAEventSelect to assign an >event to the socket. Then use WaitOnMultipleObjects to wait on sockets and >pipes at the same time.
Juan, This theory is good - if you look at the attached apr/poll/win32/poll.c you will see I have just such an implementation for you to hack on. It still needs one more scrubbing, a few tests still fail. Just switch from poll/unix to poll/win32 in your apr.dsp and libapr.dsp files, and you can start experimenting with file handles. However, calling wait on a file HANDLE isn't the same as polling it for specific events such as POLLIN, POLLOUT, POLLPRI etc. I discovered this trying to poll the pipe handle in httpd-2.0/server/mpm/winnt's nt_eventlog.c source. The Platform SDK documentation implies that pipe handles will raise events, when data on the pipe is ready to be read. This wasn't how I discovered they actually work when I attempted to use an event driven wait loop. Files and pipes are 'signaled' when requests, e.g. async read or write calls, are completed. They aren't signaled for the presence of data to read or some low threshold for write. It seems right now like the only method will be some sleep for 100ms and poll each pipe by hand with PeekNamedPipe(). I'm open to better ideas that work. But *don't* let me discourage further research! The code is attached, please feel free to experiment. Bill Bill
/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ #include "apr.h" #include "apr_poll.h" #include "apr_time.h" #include "apr_portable.h" #include "apr_arch_networkio.h" #include "apr_arch_file_io.h" #if HAVE_POLL_H #include <poll.h> #endif #if HAVE_SYS_POLL_H #include <sys/poll.h> #endif #if HAVE_ALLOCA_H #include <alloca.h> #endif static int get_event(apr_int16_t event) { apr_int16_t rv = 0; if (event & APR_POLLIN) rv |= FD_READ | FD_CONNECT | FD_CLOSE; if (event & APR_POLLOUT) rv |= FD_WRITE | FD_CONNECT; /* for mskb 199434; avoid hang on 9x */ if (event & APR_POLLPRI) rv |= FD_OOB; /* if (event & APR_POLLHUP) * rv |= FD_CLOSE; * if (event & APR_POLLERR) * rv |= not applicable; * if (event & APR_POLLNVAL) * rv |= not applicable; */ return rv; } static apr_int16_t get_revent(int event) { apr_int16_t rv = 0; if (event & (FD_READ | FD_CONNECT | FD_CLOSE)) rv |= APR_POLLIN; if (event & FD_WRITE) rv |= APR_POLLOUT; if (event & FD_OOB) rv |= APR_POLLPRI; if (event & FD_CLOSE) rv |= APR_POLLHUP; /* if (event & ) * rv |= APR_POLLERR; * if (event & ) * rv |= APR_POLLNVAL; */ return rv; } #define SMALL_POLLSET_LIMIT 8 APR_DECLARE(apr_status_t) apr_poll(apr_pollfd_t *aprset, apr_int32_t num, apr_int32_t *nsds, apr_interval_time_t timeout) { int i; DWORD res; HANDLE *hevents = _alloca(sizeof(HANDLE) * num); apr_status_t rv; for (i = 0; i < num; i++) { aprset[i].rtnevents = 0; if (aprset[i].desc_type == APR_POLL_SOCKET) { if (hevents[i] = CreateEvent(NULL, TRUE, FALSE, NULL)) { if (!WSAEventSelect(aprset[i].desc.s->socketdes, hevents[i], get_event(aprset[i].reqevents))) aprset[i].rtnevents = 0; else { rv = apr_get_netos_error(); aprset[i].rtnevents = APR_POLLNVAL; } } else aprset[i].rtnevents = APR_POLLNVAL; } else if (aprset[i].desc_type == APR_POLL_FILE) { if (!(hevents[i] = aprset[i].desc.f->filedes)) { aprset[i].rtnevents = APR_POLLNVAL; } /* Nothing to do for reqevents, we have to try the raw handle */ /* pollset[i].events = get_event(aprset[i].reqevents); */ } else { num = i; break; } } if (timeout > 0) { timeout /= 1000; /* convert microseconds to milliseconds */ } res = WaitForMultipleObjects(num, hevents, FALSE, (DWORD)timeout); if (res == WAIT_FAILED) { (*nsds) = -1; rv = apr_get_os_error(); goto apr_poll_destructor; } (*nsds) = 0; if (res == WAIT_TIMEOUT) { rv = APR_TIMEUP; goto apr_poll_destructor; } if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + num) { rv = APR_SUCCESS; } else { rv = apr_get_os_error(); } for (i = 0; i < num; i++) { if (aprset[i].rtnevents == APR_POLLNVAL) { /* noop - addressed this in the setup */ } else if (aprset[i].desc_type == APR_POLL_SOCKET) { WSANETWORKEVENTS netevents; WSAEnumNetworkEvents(aprset[i].desc.s->socketdes, hevents[i], &netevents); if (netevents.lNetworkEvents) { unsigned long bits = (unsigned long) netevents.lNetworkEvents; int j = 0; aprset[i].rtnevents = get_revent(netevents.lNetworkEvents); /* Find our netevents.iErrorCode[FD_BIT] error. * Win32 only resets corresponding error values, * We must walk through the lNetworkEvents bits. * What sorta stinks is that only one error can * be recorded... are we choosing the 'right one'? */ while (bits) { if ((bits & 1) && netevents.iErrorCode[j]) { aprset[i].desc.s->next_error = APR_FROM_OS_ERROR(netevents.iErrorCode[j]); aprset[i].rtnevents |= APR_POLLERR; break; } bits >>= 1; --j; } } } else /* (aprset[i].desc_type == APR_POLL_FILE) */ { aprset[i].rtnevents = aprset[i].reqevents & (APR_POLLIN | APR_POLLOUT); /* Nothing to do here, we have to try the raw handle */ /* pollset[i].events = get_event(aprset[i].reqevents); */ } /* Count any bits set, even APR_POLLNVAL from earlier */ if (aprset[i].rtnevents) ++(*nsds); } apr_poll_destructor: for (i = 0; i < num; i++) { if (aprset[i].desc_type == APR_POLL_SOCKET) { if (aprset[i].rtnevents == APR_POLLNVAL) { /* avoid MS KB168349 by omitting hevents */ WSAEventSelect(aprset[i].desc.s->socketdes, NULL, 0); CloseHandle(hevents[i]); } } } return rv; } struct apr_pollset_t { int nelts; int nalloc; HANDLE *hevents; apr_pollfd_t *query_set; apr_pollfd_t *result_set; apr_pool_t *pool; }; APR_DECLARE(apr_status_t) apr_pollset_destroy(apr_pollset_t *pollset) { int i; for (i = 0; i < pollset->nelts; i++) { if (pollset->query_set[i].desc_type == APR_POLL_SOCKET) { /* avoid MS KB168349 by omitting hevents */ WSAEventSelect(pollset->query_set[i].desc.s->socketdes, NULL, 0); CloseHandle(pollset->hevents[i]); } } return APR_SUCCESS; } apr_status_t pollset_cleanup(void *data) { return apr_pollset_destroy((apr_pollset_t*) data); } APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset, apr_uint32_t size, apr_pool_t *p, apr_uint32_t flags) { *pollset = apr_palloc(p, sizeof(**pollset)); (*pollset)->nelts = 0; (*pollset)->nalloc = size; (*pollset)->hevents = apr_palloc(p, size * sizeof(HANDLE)); (*pollset)->query_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); (*pollset)->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); (*pollset)->pool = p; apr_pool_cleanup_register(p, *pollset, pollset_cleanup, apr_pool_cleanup_null); return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_pollset_add(apr_pollset_t *pollset, const apr_pollfd_t *descriptor) { int i = pollset->nelts; if (pollset->nelts == pollset->nalloc) { return APR_ENOMEM; } pollset->query_set[i] = *descriptor; if (pollset->query_set[i].desc_type == APR_POLL_SOCKET) { if (pollset->hevents[i] = CreateEvent(NULL, TRUE, FALSE, NULL)) { if (!WSAEventSelect(pollset->query_set[i].desc.s->socketdes, pollset->hevents[i], get_event(pollset->query_set[i].reqevents))) pollset->query_set[i].rtnevents = 0; else { CloseHandle(pollset->hevents[i]); return apr_get_netos_error(); } } else return apr_get_os_error(); } else if (pollset->query_set[i].desc_type == APR_POLL_FILE) { if (!(pollset->hevents[i] = pollset->query_set[i].desc.f->filedes)) { return apr_get_os_error(); } /* Nothing to do for reqevents, we have to try the raw handle */ /* pollset[i].events = get_event(pollset->query_set[i].reqevents); */ } pollset->nelts++; return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_pollset_remove(apr_pollset_t *pollset, const apr_pollfd_t *descriptor) { int i; for (i = 0; i < pollset->nelts; i++) { if (descriptor->desc.s == pollset->query_set[i].desc.s) { /* Found an instance of the fd: remove this */ int dst = i; int old_nelts = pollset->nelts; /* avoid MS KB168349 by omitting hevents */ WSAEventSelect(pollset->query_set[i].desc.s->socketdes, NULL, 0); CloseHandle(pollset->hevents[i]); pollset->nelts--; for (i++; i < old_nelts; i++) { pollset->query_set[dst] = pollset->query_set[i]; pollset->hevents[dst] = pollset->hevents[i]; dst++; } return APR_SUCCESS; } } return APR_NOTFOUND; } APR_DECLARE(apr_status_t) apr_pollset_poll(apr_pollset_t *pollset, apr_interval_time_t timeout, apr_int32_t *num, const apr_pollfd_t **descriptors) { int i; DWORD res; apr_status_t rv; if (timeout > 0) { timeout /= 1000; /* convert microseconds to milliseconds */ } res = WaitForMultipleObjects(pollset->nelts, pollset->hevents, FALSE, (DWORD)timeout); if (res == WAIT_FAILED) { (*num) = -1; return apr_get_os_error(); } (*num) = 0; if (res == WAIT_TIMEOUT) { return APR_TIMEUP; } if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + pollset->nelts) { rv = APR_SUCCESS; } else { rv = apr_get_os_error(); } for (i = 0; i < pollset->nelts; i++) { if (pollset->query_set[i].desc_type == APR_POLL_SOCKET) { WSANETWORKEVENTS netevents; WSAEnumNetworkEvents(pollset->query_set[i].desc.s->socketdes, pollset->hevents[i], &netevents); if (pollset->query_set[i].rtnevents = get_revent(netevents.lNetworkEvents)) { pollset->result_set[*num] = pollset->query_set[i]; (*num)++; } /* what to do with netevents.iErrorCode[FD_BIT]??? */ } else /* (aprset[i].desc_type == APR_POLL_FILE) */ { pollset->query_set[i].rtnevents = pollset->query_set[i].reqevents & (APR_POLLIN | APR_POLLOUT); pollset->result_set[*num] = pollset->query_set[i]; (*num)++; /* Nothing to do here, we have to try the raw handle */ /* pollset[i].events = get_event(aprset[i].reqevents); */ } } *descriptors = pollset->result_set; return APR_SUCCESS; }
