diff options
author | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-28 17:19:35 +0100 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-28 17:19:35 +0100 |
commit | d3c52eb4b4402f0afbebc3f36b9ef8c0994a6c1a (patch) | |
tree | b5ed37757fd9e0f821d103c26692e35ddf2976cb /poll | |
download | libapr-tarball-d3c52eb4b4402f0afbebc3f36b9ef8c0994a6c1a.tar.gz |
Tarball conversion
Diffstat (limited to 'poll')
-rw-r--r-- | poll/os2/poll.c | 105 | ||||
-rw-r--r-- | poll/os2/pollset.c | 230 | ||||
-rw-r--r-- | poll/unix/epoll.c | 462 | ||||
-rw-r--r-- | poll/unix/kqueue.c | 480 | ||||
-rw-r--r-- | poll/unix/poll.c | 440 | ||||
-rw-r--r-- | poll/unix/pollcb.c | 171 | ||||
-rw-r--r-- | poll/unix/pollset.c | 344 | ||||
-rw-r--r-- | poll/unix/port.c | 577 | ||||
-rw-r--r-- | poll/unix/select.c | 449 |
9 files changed, 3258 insertions, 0 deletions
diff --git a/poll/os2/poll.c b/poll/os2/poll.c new file mode 100644 index 0000000..3c36e5e --- /dev/null +++ b/poll/os2/poll.c @@ -0,0 +1,105 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr.h" +#include "apr_poll.h" +#include "apr_arch_networkio.h" + +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 *pollset; + int i; + int num_read = 0, num_write = 0, num_except = 0, num_total; + int pos_read, pos_write, pos_except; + + for (i = 0; i < num; i++) { + if (aprset[i].desc_type == APR_POLL_SOCKET) { + num_read += (aprset[i].reqevents & APR_POLLIN) != 0; + num_write += (aprset[i].reqevents & APR_POLLOUT) != 0; + num_except += (aprset[i].reqevents & APR_POLLPRI) != 0; + } + } + + num_total = num_read + num_write + num_except; + pollset = alloca(sizeof(int) * num_total); + memset(pollset, 0, sizeof(int) * num_total); + + pos_read = 0; + pos_write = num_read; + pos_except = pos_write + num_write; + + for (i = 0; i < num; i++) { + if (aprset[i].desc_type == APR_POLL_SOCKET) { + if (aprset[i].reqevents & APR_POLLIN) { + pollset[pos_read++] = aprset[i].desc.s->socketdes; + } + + if (aprset[i].reqevents & APR_POLLOUT) { + pollset[pos_write++] = aprset[i].desc.s->socketdes; + } + + if (aprset[i].reqevents & APR_POLLPRI) { + pollset[pos_except++] = aprset[i].desc.s->socketdes; + } + + aprset[i].rtnevents = 0; + } + } + + if (timeout > 0) { + timeout /= 1000; /* convert microseconds to milliseconds */ + } + + i = select(pollset, num_read, num_write, num_except, timeout); + (*nsds) = i; + + if ((*nsds) < 0) { + return APR_FROM_OS_ERROR(sock_errno()); + } + + if ((*nsds) == 0) { + return APR_TIMEUP; + } + + pos_read = 0; + pos_write = num_read; + pos_except = pos_write + num_write; + + for (i = 0; i < num; i++) { + if (aprset[i].desc_type == APR_POLL_SOCKET) { + if (aprset[i].reqevents & APR_POLLIN) { + if (pollset[pos_read++] > 0) { + aprset[i].rtnevents |= APR_POLLIN; + } + } + + if (aprset[i].reqevents & APR_POLLOUT) { + if (pollset[pos_write++] > 0) { + aprset[i].rtnevents |= APR_POLLOUT; + } + } + + if (aprset[i].reqevents & APR_POLLPRI) { + if (pollset[pos_except++] > 0) { + aprset[i].rtnevents |= APR_POLLPRI; + } + } + } + } + + return APR_SUCCESS; +} diff --git a/poll/os2/pollset.c b/poll/os2/pollset.c new file mode 100644 index 0000000..e77dc9a --- /dev/null +++ b/poll/os2/pollset.c @@ -0,0 +1,230 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr.h" +#include "apr_poll.h" +#include "apr_arch_networkio.h" + + + +struct apr_pollset_t { + apr_pool_t *pool; + apr_uint32_t nelts; + apr_uint32_t nalloc; + int *pollset; + int num_read; + int num_write; + int num_except; + int num_total; + apr_pollfd_t *query_set; + apr_pollfd_t *result_set; +}; + + + +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)->pool = p; + (*pollset)->nelts = 0; + (*pollset)->nalloc = size; + (*pollset)->pollset = apr_palloc(p, size * sizeof(int) * 3); + (*pollset)->query_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + (*pollset)->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + (*pollset)->num_read = -1; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_pollset_create_ex(apr_pollset_t **pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags, + apr_pollset_method_e method) +{ + return apr_pollset_create(pollset, size, p, flags); +} + +APR_DECLARE(apr_status_t) apr_pollset_destroy(apr_pollset_t *pollset) +{ + /* A no-op function for now. If we later implement /dev/poll + * support, we'll need to close the /dev/poll fd here + */ + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_pollset_add(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + if (pollset->nelts == pollset->nalloc) { + return APR_ENOMEM; + } + + pollset->query_set[pollset->nelts] = *descriptor; + + if (descriptor->desc_type != APR_POLL_SOCKET) { + return APR_EBADF; + } + + pollset->nelts++; + pollset->num_read = -1; + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_pollset_remove(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + apr_uint32_t 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 and any other copies */ + apr_uint32_t dst = i; + apr_uint32_t old_nelts = pollset->nelts; + pollset->nelts--; + + for (i++; i < old_nelts; i++) { + if (descriptor->desc.s == pollset->query_set[i].desc.s) { + pollset->nelts--; + } + else { + pollset->pollset[dst] = pollset->pollset[i]; + pollset->query_set[dst] = pollset->query_set[i]; + dst++; + } + } + + pollset->num_read = -1; + return APR_SUCCESS; + } + } + + return APR_NOTFOUND; +} + + + +static void make_pollset(apr_pollset_t *pollset) +{ + int i; + int pos = 0; + + pollset->num_read = 0; + pollset->num_write = 0; + pollset->num_except = 0; + + for (i = 0; i < pollset->nelts; i++) { + if (pollset->query_set[i].reqevents & APR_POLLIN) { + pollset->pollset[pos++] = pollset->query_set[i].desc.s->socketdes; + pollset->num_read++; + } + } + + for (i = 0; i < pollset->nelts; i++) { + if (pollset->query_set[i].reqevents & APR_POLLOUT) { + pollset->pollset[pos++] = pollset->query_set[i].desc.s->socketdes; + pollset->num_write++; + } + } + + for (i = 0; i < pollset->nelts; i++) { + if (pollset->query_set[i].reqevents & APR_POLLPRI) { + pollset->pollset[pos++] = pollset->query_set[i].desc.s->socketdes; + pollset->num_except++; + } + } + + pollset->num_total = pollset->num_read + pollset->num_write + pollset->num_except; +} + + + +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 rv; + apr_uint32_t i; + int *pollresult; + int read_pos, write_pos, except_pos; + + if (pollset->num_read < 0) { + make_pollset(pollset); + } + + pollresult = alloca(sizeof(int) * pollset->num_total); + memcpy(pollresult, pollset->pollset, sizeof(int) * pollset->num_total); + (*num) = 0; + + if (timeout > 0) { + timeout /= 1000; + } + + rv = select(pollresult, pollset->num_read, pollset->num_write, pollset->num_except, timeout); + + if (rv < 0) { + return APR_FROM_OS_ERROR(sock_errno()); + } + + if (rv == 0) { + return APR_TIMEUP; + } + + read_pos = 0; + write_pos = pollset->num_read; + except_pos = pollset->num_read + pollset->num_write; + + for (i = 0; i < pollset->nelts; i++) { + int rtnevents = 0; + + if (pollset->query_set[i].reqevents & APR_POLLIN) { + if (pollresult[read_pos++] != -1) { + rtnevents |= APR_POLLIN; + } + } + + if (pollset->query_set[i].reqevents & APR_POLLOUT) { + if (pollresult[write_pos++] != -1) { + rtnevents |= APR_POLLOUT; + } + } + + if (pollset->query_set[i].reqevents & APR_POLLPRI) { + if (pollresult[except_pos++] != -1) { + rtnevents |= APR_POLLPRI; + } + } + + if (rtnevents) { + pollset->result_set[*num] = pollset->query_set[i]; + pollset->result_set[*num].rtnevents = rtnevents; + (*num)++; + } + } + + if (descriptors) { + *descriptors = pollset->result_set; + } + + return APR_SUCCESS; +} diff --git a/poll/unix/epoll.c b/poll/unix/epoll.c new file mode 100644 index 0000000..326dac7 --- /dev/null +++ b/poll/unix/epoll.c @@ -0,0 +1,462 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr.h" +#include "apr_poll.h" +#include "apr_time.h" +#include "apr_portable.h" +#include "apr_arch_file_io.h" +#include "apr_arch_networkio.h" +#include "apr_arch_poll_private.h" +#include "apr_arch_inherit.h" + +#if defined(HAVE_EPOLL) + +static apr_int16_t get_epoll_event(apr_int16_t event) +{ + apr_int16_t rv = 0; + + if (event & APR_POLLIN) + rv |= EPOLLIN; + if (event & APR_POLLPRI) + rv |= EPOLLPRI; + if (event & APR_POLLOUT) + rv |= EPOLLOUT; + /* APR_POLLNVAL is not handled by epoll. EPOLLERR and EPOLLHUP are return-only */ + + return rv; +} + +static apr_int16_t get_epoll_revent(apr_int16_t event) +{ + apr_int16_t rv = 0; + + if (event & EPOLLIN) + rv |= APR_POLLIN; + if (event & EPOLLPRI) + rv |= APR_POLLPRI; + if (event & EPOLLOUT) + rv |= APR_POLLOUT; + if (event & EPOLLERR) + rv |= APR_POLLERR; + if (event & EPOLLHUP) + rv |= APR_POLLHUP; + /* APR_POLLNVAL is not handled by epoll. */ + + return rv; +} + +struct apr_pollset_private_t +{ + int epoll_fd; + struct epoll_event *pollset; + apr_pollfd_t *result_set; +#if APR_HAS_THREADS + /* A thread mutex to protect operations on the rings */ + apr_thread_mutex_t *ring_lock; +#endif + /* A ring containing all of the pollfd_t that are active */ + APR_RING_HEAD(pfd_query_ring_t, pfd_elem_t) query_ring; + /* A ring of pollfd_t that have been used, and then _remove()'d */ + APR_RING_HEAD(pfd_free_ring_t, pfd_elem_t) free_ring; + /* A ring of pollfd_t where rings that have been _remove()`ed but + might still be inside a _poll() */ + APR_RING_HEAD(pfd_dead_ring_t, pfd_elem_t) dead_ring; +}; + +static apr_status_t impl_pollset_cleanup(apr_pollset_t *pollset) +{ + close(pollset->p->epoll_fd); + return APR_SUCCESS; +} + + +static apr_status_t impl_pollset_create(apr_pollset_t *pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + apr_status_t rv; + int fd; + +#ifdef HAVE_EPOLL_CREATE1 + fd = epoll_create1(EPOLL_CLOEXEC); +#else + fd = epoll_create(size); +#endif + if (fd < 0) { + pollset->p = NULL; + return apr_get_netos_error(); + } + +#ifndef HAVE_EPOLL_CREATE1 + { + int flags; + + if ((flags = fcntl(fd, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, flags) == -1) + return errno; + } +#endif + + pollset->p = apr_palloc(p, sizeof(apr_pollset_private_t)); +#if APR_HAS_THREADS + if ((flags & APR_POLLSET_THREADSAFE) && + !(flags & APR_POLLSET_NOCOPY) && + ((rv = apr_thread_mutex_create(&pollset->p->ring_lock, + APR_THREAD_MUTEX_DEFAULT, + p)) != APR_SUCCESS)) { + pollset->p = NULL; + return rv; + } +#else + if (flags & APR_POLLSET_THREADSAFE) { + pollset->p = NULL; + return APR_ENOTIMPL; + } +#endif + pollset->p->epoll_fd = fd; + pollset->p->pollset = apr_palloc(p, size * sizeof(struct epoll_event)); + pollset->p->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + + if (!(flags & APR_POLLSET_NOCOPY)) { + APR_RING_INIT(&pollset->p->query_ring, pfd_elem_t, link); + APR_RING_INIT(&pollset->p->free_ring, pfd_elem_t, link); + APR_RING_INIT(&pollset->p->dead_ring, pfd_elem_t, link); + } + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_add(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + struct epoll_event ev = {0}; + int ret = -1; + pfd_elem_t *elem = NULL; + apr_status_t rv = APR_SUCCESS; + + ev.events = get_epoll_event(descriptor->reqevents); + + if (pollset->flags & APR_POLLSET_NOCOPY) { + ev.data.ptr = (void *)descriptor; + } + else { + pollset_lock_rings(); + + if (!APR_RING_EMPTY(&(pollset->p->free_ring), pfd_elem_t, link)) { + elem = APR_RING_FIRST(&(pollset->p->free_ring)); + APR_RING_REMOVE(elem, link); + } + else { + elem = (pfd_elem_t *) apr_palloc(pollset->pool, sizeof(pfd_elem_t)); + APR_RING_ELEM_INIT(elem, link); + } + elem->pfd = *descriptor; + ev.data.ptr = elem; + } + if (descriptor->desc_type == APR_POLL_SOCKET) { + ret = epoll_ctl(pollset->p->epoll_fd, EPOLL_CTL_ADD, + descriptor->desc.s->socketdes, &ev); + } + else { + ret = epoll_ctl(pollset->p->epoll_fd, EPOLL_CTL_ADD, + descriptor->desc.f->filedes, &ev); + } + + if (0 != ret) { + rv = apr_get_netos_error(); + } + + if (!(pollset->flags & APR_POLLSET_NOCOPY)) { + if (rv != APR_SUCCESS) { + APR_RING_INSERT_TAIL(&(pollset->p->free_ring), elem, pfd_elem_t, link); + } + else { + APR_RING_INSERT_TAIL(&(pollset->p->query_ring), elem, pfd_elem_t, link); + } + pollset_unlock_rings(); + } + + return rv; +} + +static apr_status_t impl_pollset_remove(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + pfd_elem_t *ep; + apr_status_t rv = APR_SUCCESS; + struct epoll_event ev = {0}; /* ignored, but must be passed with + * kernel < 2.6.9 + */ + int ret = -1; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + ret = epoll_ctl(pollset->p->epoll_fd, EPOLL_CTL_DEL, + descriptor->desc.s->socketdes, &ev); + } + else { + ret = epoll_ctl(pollset->p->epoll_fd, EPOLL_CTL_DEL, + descriptor->desc.f->filedes, &ev); + } + if (ret < 0) { + rv = APR_NOTFOUND; + } + + if (!(pollset->flags & APR_POLLSET_NOCOPY)) { + pollset_lock_rings(); + + for (ep = APR_RING_FIRST(&(pollset->p->query_ring)); + ep != APR_RING_SENTINEL(&(pollset->p->query_ring), + pfd_elem_t, link); + ep = APR_RING_NEXT(ep, link)) { + + if (descriptor->desc.s == ep->pfd.desc.s) { + APR_RING_REMOVE(ep, link); + APR_RING_INSERT_TAIL(&(pollset->p->dead_ring), + ep, pfd_elem_t, link); + break; + } + } + + pollset_unlock_rings(); + } + + return rv; +} + +static apr_status_t impl_pollset_poll(apr_pollset_t *pollset, + apr_interval_time_t timeout, + apr_int32_t *num, + const apr_pollfd_t **descriptors) +{ + int ret, i, j; + apr_status_t rv = APR_SUCCESS; + apr_pollfd_t *fdptr; + + if (timeout > 0) { + timeout /= 1000; + } + + ret = epoll_wait(pollset->p->epoll_fd, pollset->p->pollset, pollset->nalloc, + timeout); + (*num) = ret; + + if (ret < 0) { + rv = apr_get_netos_error(); + } + else if (ret == 0) { + rv = APR_TIMEUP; + } + else { + for (i = 0, j = 0; i < ret; i++) { + if (pollset->flags & APR_POLLSET_NOCOPY) { + fdptr = (apr_pollfd_t *)(pollset->p->pollset[i].data.ptr); + } + else { + fdptr = &(((pfd_elem_t *) (pollset->p->pollset[i].data.ptr))->pfd); + } + /* Check if the polled descriptor is our + * wakeup pipe. In that case do not put it result set. + */ + if ((pollset->flags & APR_POLLSET_WAKEABLE) && + fdptr->desc_type == APR_POLL_FILE && + fdptr->desc.f == pollset->wakeup_pipe[0]) { + apr_pollset_drain_wakeup_pipe(pollset); + rv = APR_EINTR; + } + else { + pollset->p->result_set[j] = *fdptr; + pollset->p->result_set[j].rtnevents = + get_epoll_revent(pollset->p->pollset[i].events); + j++; + } + } + if (((*num) = j)) { /* any event besides wakeup pipe? */ + rv = APR_SUCCESS; + + if (descriptors) { + *descriptors = pollset->p->result_set; + } + } + } + + if (!(pollset->flags & APR_POLLSET_NOCOPY)) { + pollset_lock_rings(); + + /* Shift all PFDs in the Dead Ring to the Free Ring */ + APR_RING_CONCAT(&(pollset->p->free_ring), &(pollset->p->dead_ring), pfd_elem_t, link); + + pollset_unlock_rings(); + } + + return rv; +} + +static apr_pollset_provider_t impl = { + impl_pollset_create, + impl_pollset_add, + impl_pollset_remove, + impl_pollset_poll, + impl_pollset_cleanup, + "epoll" +}; + +apr_pollset_provider_t *apr_pollset_provider_epoll = &impl; + +static apr_status_t cb_cleanup(void *p_) +{ + apr_pollcb_t *pollcb = (apr_pollcb_t *) p_; + close(pollcb->fd); + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + int fd; + +#ifdef HAVE_EPOLL_CREATE1 + fd = epoll_create1(EPOLL_CLOEXEC); +#else + fd = epoll_create(size); +#endif + + if (fd < 0) { + return apr_get_netos_error(); + } + +#ifndef HAVE_EPOLL_CREATE1 + { + int flags; + + if ((flags = fcntl(fd, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, flags) == -1) + return errno; + } +#endif + + pollcb->fd = fd; + pollcb->pollset.epoll = apr_palloc(p, size * sizeof(struct epoll_event)); + apr_pool_cleanup_register(p, pollcb, cb_cleanup, apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_add(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + struct epoll_event ev; + int ret; + + ev.events = get_epoll_event(descriptor->reqevents); + ev.data.ptr = (void *)descriptor; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + ret = epoll_ctl(pollcb->fd, EPOLL_CTL_ADD, + descriptor->desc.s->socketdes, &ev); + } + else { + ret = epoll_ctl(pollcb->fd, EPOLL_CTL_ADD, + descriptor->desc.f->filedes, &ev); + } + + if (ret == -1) { + return apr_get_netos_error(); + } + + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_remove(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + apr_status_t rv = APR_SUCCESS; + struct epoll_event ev = {0}; /* ignored, but must be passed with + * kernel < 2.6.9 + */ + int ret = -1; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + ret = epoll_ctl(pollcb->fd, EPOLL_CTL_DEL, + descriptor->desc.s->socketdes, &ev); + } + else { + ret = epoll_ctl(pollcb->fd, EPOLL_CTL_DEL, + descriptor->desc.f->filedes, &ev); + } + + if (ret < 0) { + rv = APR_NOTFOUND; + } + + return rv; +} + + +static apr_status_t impl_pollcb_poll(apr_pollcb_t *pollcb, + apr_interval_time_t timeout, + apr_pollcb_cb_t func, + void *baton) +{ + int ret, i; + apr_status_t rv = APR_SUCCESS; + + if (timeout > 0) { + timeout /= 1000; + } + + ret = epoll_wait(pollcb->fd, pollcb->pollset.epoll, pollcb->nalloc, + timeout); + if (ret < 0) { + rv = apr_get_netos_error(); + } + else if (ret == 0) { + rv = APR_TIMEUP; + } + else { + for (i = 0; i < ret; i++) { + apr_pollfd_t *pollfd = (apr_pollfd_t *)(pollcb->pollset.epoll[i].data.ptr); + pollfd->rtnevents = get_epoll_revent(pollcb->pollset.epoll[i].events); + + rv = func(baton, pollfd); + if (rv) { + return rv; + } + } + } + + return rv; +} + +static apr_pollcb_provider_t impl_cb = { + impl_pollcb_create, + impl_pollcb_add, + impl_pollcb_remove, + impl_pollcb_poll, + "epoll" +}; + +apr_pollcb_provider_t *apr_pollcb_provider_epoll = &impl_cb; + +#endif /* HAVE_EPOLL */ diff --git a/poll/unix/kqueue.c b/poll/unix/kqueue.c new file mode 100644 index 0000000..dbe785a --- /dev/null +++ b/poll/unix/kqueue.c @@ -0,0 +1,480 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr.h" +#include "apr_poll.h" +#include "apr_time.h" +#include "apr_portable.h" +#include "apr_arch_file_io.h" +#include "apr_arch_networkio.h" +#include "apr_arch_poll_private.h" +#include "apr_arch_inherit.h" + +#ifdef HAVE_KQUEUE + +static apr_int16_t get_kqueue_revent(apr_int16_t event, apr_int16_t flags) +{ + apr_int16_t rv = 0; + + if (event == EVFILT_READ) + rv |= APR_POLLIN; + else if (event == EVFILT_WRITE) + rv |= APR_POLLOUT; + if (flags & EV_EOF) + rv |= APR_POLLHUP; + /* APR_POLLPRI, APR_POLLERR, and APR_POLLNVAL are not handled by this + * implementation. + * TODO: See if EV_ERROR + certain system errors in the returned data field + * should map to APR_POLLNVAL. + */ + return rv; +} + +struct apr_pollset_private_t +{ + int kqueue_fd; + struct kevent kevent; + apr_uint32_t setsize; + struct kevent *ke_set; + apr_pollfd_t *result_set; +#if APR_HAS_THREADS + /* A thread mutex to protect operations on the rings */ + apr_thread_mutex_t *ring_lock; +#endif + /* A ring containing all of the pollfd_t that are active */ + APR_RING_HEAD(pfd_query_ring_t, pfd_elem_t) query_ring; + /* A ring of pollfd_t that have been used, and then _remove'd */ + APR_RING_HEAD(pfd_free_ring_t, pfd_elem_t) free_ring; + /* A ring of pollfd_t where rings that have been _remove'd but + might still be inside a _poll */ + APR_RING_HEAD(pfd_dead_ring_t, pfd_elem_t) dead_ring; +}; + +static apr_status_t impl_pollset_cleanup(apr_pollset_t *pollset) +{ + close(pollset->p->kqueue_fd); + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_create(apr_pollset_t *pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + apr_status_t rv; + pollset->p = apr_palloc(p, sizeof(apr_pollset_private_t)); +#if APR_HAS_THREADS + if (flags & APR_POLLSET_THREADSAFE && + ((rv = apr_thread_mutex_create(&pollset->p->ring_lock, + APR_THREAD_MUTEX_DEFAULT, + p)) != APR_SUCCESS)) { + pollset->p = NULL; + return rv; + } +#else + if (flags & APR_POLLSET_THREADSAFE) { + pollset->p = NULL; + return APR_ENOTIMPL; + } +#endif + + /* POLLIN and POLLOUT are represented in different returned + * events, so we need 2 entries per descriptor in the result set, + * both for what is returned by kevent() and what is returned to + * the caller of apr_pollset_poll() (since it doesn't spend the + * CPU to coalesce separate APR_POLLIN and APR_POLLOUT events + * for the same descriptor) + */ + pollset->p->setsize = 2 * size; + + pollset->p->ke_set = + (struct kevent *) apr_palloc(p, pollset->p->setsize * sizeof(struct kevent)); + + memset(pollset->p->ke_set, 0, pollset->p->setsize * sizeof(struct kevent)); + + pollset->p->kqueue_fd = kqueue(); + + if (pollset->p->kqueue_fd == -1) { + pollset->p = NULL; + return apr_get_netos_error(); + } + + { + int flags; + + if ((flags = fcntl(pollset->p->kqueue_fd, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(pollset->p->kqueue_fd, F_SETFD, flags) == -1) + return errno; + } + + pollset->p->result_set = apr_palloc(p, pollset->p->setsize * sizeof(apr_pollfd_t)); + + APR_RING_INIT(&pollset->p->query_ring, pfd_elem_t, link); + APR_RING_INIT(&pollset->p->free_ring, pfd_elem_t, link); + APR_RING_INIT(&pollset->p->dead_ring, pfd_elem_t, link); + + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_add(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + apr_os_sock_t fd; + pfd_elem_t *elem; + apr_status_t rv = APR_SUCCESS; + + pollset_lock_rings(); + + if (!APR_RING_EMPTY(&(pollset->p->free_ring), pfd_elem_t, link)) { + elem = APR_RING_FIRST(&(pollset->p->free_ring)); + APR_RING_REMOVE(elem, link); + } + else { + elem = (pfd_elem_t *) apr_palloc(pollset->pool, sizeof(pfd_elem_t)); + APR_RING_ELEM_INIT(elem, link); + } + elem->pfd = *descriptor; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + if (descriptor->reqevents & APR_POLLIN) { + EV_SET(&pollset->p->kevent, fd, EVFILT_READ, EV_ADD, 0, 0, elem); + + if (kevent(pollset->p->kqueue_fd, &pollset->p->kevent, 1, NULL, 0, + NULL) == -1) { + rv = apr_get_netos_error(); + } + } + + if (descriptor->reqevents & APR_POLLOUT && rv == APR_SUCCESS) { + EV_SET(&pollset->p->kevent, fd, EVFILT_WRITE, EV_ADD, 0, 0, elem); + + if (kevent(pollset->p->kqueue_fd, &pollset->p->kevent, 1, NULL, 0, + NULL) == -1) { + rv = apr_get_netos_error(); + } + } + + if (rv == APR_SUCCESS) { + APR_RING_INSERT_TAIL(&(pollset->p->query_ring), elem, pfd_elem_t, link); + } + else { + APR_RING_INSERT_TAIL(&(pollset->p->free_ring), elem, pfd_elem_t, link); + } + + pollset_unlock_rings(); + + return rv; +} + +static apr_status_t impl_pollset_remove(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + pfd_elem_t *ep; + apr_status_t rv; + apr_os_sock_t fd; + + pollset_lock_rings(); + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + rv = APR_NOTFOUND; /* unless at least one of the specified conditions is */ + if (descriptor->reqevents & APR_POLLIN) { + EV_SET(&pollset->p->kevent, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + + if (kevent(pollset->p->kqueue_fd, &pollset->p->kevent, 1, NULL, 0, + NULL) != -1) { + rv = APR_SUCCESS; + } + } + + if (descriptor->reqevents & APR_POLLOUT) { + EV_SET(&pollset->p->kevent, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + + if (kevent(pollset->p->kqueue_fd, &pollset->p->kevent, 1, NULL, 0, + NULL) != -1) { + rv = APR_SUCCESS; + } + } + + for (ep = APR_RING_FIRST(&(pollset->p->query_ring)); + ep != APR_RING_SENTINEL(&(pollset->p->query_ring), + pfd_elem_t, link); + ep = APR_RING_NEXT(ep, link)) { + + if (descriptor->desc.s == ep->pfd.desc.s) { + APR_RING_REMOVE(ep, link); + APR_RING_INSERT_TAIL(&(pollset->p->dead_ring), + ep, pfd_elem_t, link); + break; + } + } + + pollset_unlock_rings(); + + return rv; +} + +static apr_status_t impl_pollset_poll(apr_pollset_t *pollset, + apr_interval_time_t timeout, + apr_int32_t *num, + const apr_pollfd_t **descriptors) +{ + int ret, i, j; + struct timespec tv, *tvptr; + apr_status_t rv = APR_SUCCESS; + apr_pollfd_t fd; + + if (timeout < 0) { + tvptr = NULL; + } + else { + tv.tv_sec = (long) apr_time_sec(timeout); + tv.tv_nsec = (long) apr_time_usec(timeout) * 1000; + tvptr = &tv; + } + + ret = kevent(pollset->p->kqueue_fd, NULL, 0, pollset->p->ke_set, + pollset->p->setsize, tvptr); + (*num) = ret; + if (ret < 0) { + rv = apr_get_netos_error(); + } + else if (ret == 0) { + rv = APR_TIMEUP; + } + else { + for (i = 0, j = 0; i < ret; i++) { + fd = (((pfd_elem_t*)(pollset->p->ke_set[i].udata))->pfd); + if ((pollset->flags & APR_POLLSET_WAKEABLE) && + fd.desc_type == APR_POLL_FILE && + fd.desc.f == pollset->wakeup_pipe[0]) { + apr_pollset_drain_wakeup_pipe(pollset); + rv = APR_EINTR; + } + else { + pollset->p->result_set[j] = fd; + pollset->p->result_set[j].rtnevents = + get_kqueue_revent(pollset->p->ke_set[i].filter, + pollset->p->ke_set[i].flags); + j++; + } + } + if ((*num = j)) { /* any event besides wakeup pipe? */ + rv = APR_SUCCESS; + if (descriptors) { + *descriptors = pollset->p->result_set; + } + } + } + + + pollset_lock_rings(); + + /* Shift all PFDs in the Dead Ring to the Free Ring */ + APR_RING_CONCAT(&(pollset->p->free_ring), &(pollset->p->dead_ring), + pfd_elem_t, link); + + pollset_unlock_rings(); + + return rv; +} + +static apr_pollset_provider_t impl = { + impl_pollset_create, + impl_pollset_add, + impl_pollset_remove, + impl_pollset_poll, + impl_pollset_cleanup, + "kqueue" +}; + +apr_pollset_provider_t *apr_pollset_provider_kqueue = &impl; + +static apr_status_t cb_cleanup(void *b_) +{ + apr_pollcb_t *pollcb = (apr_pollcb_t *) b_; + close(pollcb->fd); + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + int fd; + + fd = kqueue(); + if (fd < 0) { + return apr_get_netos_error(); + } + + { + int flags; + + if ((flags = fcntl(fd, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, flags) == -1) + return errno; + } + + pollcb->fd = fd; + pollcb->pollset.ke = (struct kevent *)apr_pcalloc(p, 2 * size * sizeof(struct kevent)); + apr_pool_cleanup_register(p, pollcb, cb_cleanup, apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_add(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + apr_os_sock_t fd; + struct kevent ev; + apr_status_t rv = APR_SUCCESS; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + if (descriptor->reqevents & APR_POLLIN) { + EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, descriptor); + + if (kevent(pollcb->fd, &ev, 1, NULL, 0, NULL) == -1) { + rv = apr_get_netos_error(); + } + } + + if (descriptor->reqevents & APR_POLLOUT && rv == APR_SUCCESS) { + EV_SET(&ev, fd, EVFILT_WRITE, EV_ADD, 0, 0, descriptor); + + if (kevent(pollcb->fd, &ev, 1, NULL, 0, NULL) == -1) { + rv = apr_get_netos_error(); + } + } + + return rv; +} + +static apr_status_t impl_pollcb_remove(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + apr_status_t rv; + struct kevent ev; + apr_os_sock_t fd; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + rv = APR_NOTFOUND; /* unless at least one of the specified conditions is */ + if (descriptor->reqevents & APR_POLLIN) { + EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + + if (kevent(pollcb->fd, &ev, 1, NULL, 0, NULL) != -1) { + rv = APR_SUCCESS; + } + } + + if (descriptor->reqevents & APR_POLLOUT) { + EV_SET(&ev, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + + if (kevent(pollcb->fd, &ev, 1, NULL, 0, NULL) != -1) { + rv = APR_SUCCESS; + } + } + + return rv; +} + + +static apr_status_t impl_pollcb_poll(apr_pollcb_t *pollcb, + apr_interval_time_t timeout, + apr_pollcb_cb_t func, + void *baton) +{ + int ret, i; + struct timespec tv, *tvptr; + apr_status_t rv = APR_SUCCESS; + + if (timeout < 0) { + tvptr = NULL; + } + else { + tv.tv_sec = (long) apr_time_sec(timeout); + tv.tv_nsec = (long) apr_time_usec(timeout) * 1000; + tvptr = &tv; + } + + ret = kevent(pollcb->fd, NULL, 0, pollcb->pollset.ke, 2 * pollcb->nalloc, + tvptr); + + if (ret < 0) { + rv = apr_get_netos_error(); + } + else if (ret == 0) { + rv = APR_TIMEUP; + } + else { + for (i = 0; i < ret; i++) { + apr_pollfd_t *pollfd = (apr_pollfd_t *)(pollcb->pollset.ke[i].udata); + + pollfd->rtnevents = get_kqueue_revent(pollcb->pollset.ke[i].filter, + pollcb->pollset.ke[i].flags); + + rv = func(baton, pollfd); + + if (rv) { + return rv; + } + } + } + + return rv; +} + +static apr_pollcb_provider_t impl_cb = { + impl_pollcb_create, + impl_pollcb_add, + impl_pollcb_remove, + impl_pollcb_poll, + "kqueue" +}; + +apr_pollcb_provider_t *apr_pollcb_provider_kqueue = &impl_cb; + +#endif /* HAVE_KQUEUE */ diff --git a/poll/unix/poll.c b/poll/unix/poll.c new file mode 100644 index 0000000..7d15736 --- /dev/null +++ b/poll/unix/poll.c @@ -0,0 +1,440 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr.h" +#include "apr_poll.h" +#include "apr_time.h" +#include "apr_portable.h" +#include "apr_arch_file_io.h" +#include "apr_arch_networkio.h" +#include "apr_arch_misc.h" +#include "apr_arch_poll_private.h" + +#if defined(HAVE_POLL) + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif + +static apr_int16_t get_event(apr_int16_t event) +{ + apr_int16_t rv = 0; + + if (event & APR_POLLIN) + rv |= POLLIN; + if (event & APR_POLLPRI) + rv |= POLLPRI; + if (event & APR_POLLOUT) + rv |= POLLOUT; + /* POLLERR, POLLHUP, and POLLNVAL aren't valid as requested events */ + + return rv; +} + +static apr_int16_t get_revent(apr_int16_t event) +{ + apr_int16_t rv = 0; + + if (event & POLLIN) + rv |= APR_POLLIN; + if (event & POLLPRI) + rv |= APR_POLLPRI; + if (event & POLLOUT) + rv |= APR_POLLOUT; + if (event & POLLERR) + rv |= APR_POLLERR; + if (event & POLLHUP) + rv |= APR_POLLHUP; + if (event & POLLNVAL) + rv |= APR_POLLNVAL; + + return rv; +} + +#ifdef POLL_USES_POLL + +#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, num_to_poll; +#ifdef HAVE_VLA + /* XXX: I trust that this is a segv when insufficient stack exists? */ + struct pollfd pollset[num]; +#elif defined(HAVE_ALLOCA) + struct pollfd *pollset = alloca(sizeof(struct pollfd) * num); + if (!pollset) + return APR_ENOMEM; +#else + struct pollfd tmp_pollset[SMALL_POLLSET_LIMIT]; + struct pollfd *pollset; + + if (num <= SMALL_POLLSET_LIMIT) { + pollset = tmp_pollset; + } + else { + /* This does require O(n) to copy the descriptors to the internal + * mapping. + */ + pollset = malloc(sizeof(struct pollfd) * num); + /* The other option is adding an apr_pool_abort() fn to invoke + * the pool's out of memory handler + */ + if (!pollset) + return APR_ENOMEM; + } +#endif + for (i = 0; i < num; i++) { + if (aprset[i].desc_type == APR_POLL_SOCKET) { + pollset[i].fd = aprset[i].desc.s->socketdes; + } + else if (aprset[i].desc_type == APR_POLL_FILE) { + pollset[i].fd = aprset[i].desc.f->filedes; + } + else { + break; + } + pollset[i].events = get_event(aprset[i].reqevents); + } + num_to_poll = i; + + if (timeout > 0) { + timeout /= 1000; /* convert microseconds to milliseconds */ + } + + i = poll(pollset, num_to_poll, timeout); + (*nsds) = i; + + if (i > 0) { /* poll() sets revents only if an event was signalled; + * we don't promise to set rtnevents unless an event + * was signalled + */ + for (i = 0; i < num; i++) { + aprset[i].rtnevents = get_revent(pollset[i].revents); + } + } + +#if !defined(HAVE_VLA) && !defined(HAVE_ALLOCA) + if (num > SMALL_POLLSET_LIMIT) { + free(pollset); + } +#endif + + if ((*nsds) < 0) { + return apr_get_netos_error(); + } + if ((*nsds) == 0) { + return APR_TIMEUP; + } + return APR_SUCCESS; +} + + +#endif /* POLL_USES_POLL */ + +struct apr_pollset_private_t +{ + struct pollfd *pollset; + apr_pollfd_t *query_set; + apr_pollfd_t *result_set; +}; + +static apr_status_t impl_pollset_create(apr_pollset_t *pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + if (flags & APR_POLLSET_THREADSAFE) { + return APR_ENOTIMPL; + } +#ifdef WIN32 + if (!APR_HAVE_LATE_DLL_FUNC(WSAPoll)) { + return APR_ENOTIMPL; + } +#endif + pollset->p = apr_palloc(p, sizeof(apr_pollset_private_t)); + pollset->p->pollset = apr_palloc(p, size * sizeof(struct pollfd)); + pollset->p->query_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + pollset->p->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_add(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + if (pollset->nelts == pollset->nalloc) { + return APR_ENOMEM; + } + + pollset->p->query_set[pollset->nelts] = *descriptor; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + pollset->p->pollset[pollset->nelts].fd = descriptor->desc.s->socketdes; + } + else { +#if APR_FILES_AS_SOCKETS + pollset->p->pollset[pollset->nelts].fd = descriptor->desc.f->filedes; +#else + if ((pollset->flags & APR_POLLSET_WAKEABLE) && + descriptor->desc.f == pollset->wakeup_pipe[0]) + pollset->p->pollset[pollset->nelts].fd = (SOCKET)descriptor->desc.f->filedes; + else + return APR_EBADF; +#endif + } + pollset->p->pollset[pollset->nelts].events = + get_event(descriptor->reqevents); + pollset->nelts++; + + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_remove(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + apr_uint32_t i; + + for (i = 0; i < pollset->nelts; i++) { + if (descriptor->desc.s == pollset->p->query_set[i].desc.s) { + /* Found an instance of the fd: remove this and any other copies */ + apr_uint32_t dst = i; + apr_uint32_t old_nelts = pollset->nelts; + pollset->nelts--; + for (i++; i < old_nelts; i++) { + if (descriptor->desc.s == pollset->p->query_set[i].desc.s) { + pollset->nelts--; + } + else { + pollset->p->pollset[dst] = pollset->p->pollset[i]; + pollset->p->query_set[dst] = pollset->p->query_set[i]; + dst++; + } + } + return APR_SUCCESS; + } + } + + return APR_NOTFOUND; +} + +static apr_status_t impl_pollset_poll(apr_pollset_t *pollset, + apr_interval_time_t timeout, + apr_int32_t *num, + const apr_pollfd_t **descriptors) +{ + int ret; + apr_status_t rv = APR_SUCCESS; +#ifdef WIN32 + apr_interval_time_t orig_timeout = timeout; +#endif + + if (timeout > 0) { + timeout /= 1000; + } +#ifdef WIN32 + /* WSAPoll() requires at least one socket. */ + if (pollset->nelts == 0) { + *num = 0; + if (orig_timeout > 0) { + apr_sleep(orig_timeout); + return APR_TIMEUP; + } + return APR_SUCCESS; + } + + ret = WSAPoll(pollset->p->pollset, pollset->nelts, (int)timeout); +#else + ret = poll(pollset->p->pollset, pollset->nelts, timeout); +#endif + (*num) = ret; + if (ret < 0) { + return apr_get_netos_error(); + } + else if (ret == 0) { + return APR_TIMEUP; + } + else { + apr_uint32_t i, j; + + for (i = 0, j = 0; i < pollset->nelts; i++) { + if (pollset->p->pollset[i].revents != 0) { + /* Check if the polled descriptor is our + * wakeup pipe. In that case do not put it result set. + */ + if ((pollset->flags & APR_POLLSET_WAKEABLE) && + pollset->p->query_set[i].desc_type == APR_POLL_FILE && + pollset->p->query_set[i].desc.f == pollset->wakeup_pipe[0]) { + apr_pollset_drain_wakeup_pipe(pollset); + rv = APR_EINTR; + } + else { + pollset->p->result_set[j] = pollset->p->query_set[i]; + pollset->p->result_set[j].rtnevents = + get_revent(pollset->p->pollset[i].revents); + j++; + } + } + } + if (((*num) = j) > 0) + rv = APR_SUCCESS; + } + if (descriptors && (*num)) + *descriptors = pollset->p->result_set; + return rv; +} + +static apr_pollset_provider_t impl = { + impl_pollset_create, + impl_pollset_add, + impl_pollset_remove, + impl_pollset_poll, + NULL, + "poll" +}; + +apr_pollset_provider_t *apr_pollset_provider_poll = &impl; + +/* Poll method pollcb. + * This is probably usable only for WIN32 having WSAPoll + */ +static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ +#if APR_HAS_THREADS + return APR_ENOTIMPL; +#else + pollcb->fd = -1; +#ifdef WIN32 + if (!APR_HAVE_LATE_DLL_FUNC(WSAPoll)) { + return APR_ENOTIMPL; + } +#endif + + pollcb->pollset.ps = apr_palloc(p, size * sizeof(struct pollfd)); + pollcb->copyset = apr_palloc(p, size * sizeof(apr_pollfd_t *)); + + return APR_SUCCESS; +#endif +} + +static apr_status_t impl_pollcb_add(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + if (pollcb->nelts == pollcb->nalloc) { + return APR_ENOMEM; + } + + if (descriptor->desc_type == APR_POLL_SOCKET) { + pollcb->pollset.ps[pollcb->nelts].fd = descriptor->desc.s->socketdes; + } + else { +#if APR_FILES_AS_SOCKETS + pollcb->pollset.ps[pollcb->nelts].fd = descriptor->desc.f->filedes; +#else + return APR_EBADF; +#endif + } + + pollcb->pollset.ps[pollcb->nelts].events = + get_event(descriptor->reqevents); + pollcb->copyset[pollcb->nelts] = descriptor; + pollcb->nelts++; + + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_remove(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + apr_uint32_t i; + + for (i = 0; i < pollcb->nelts; i++) { + if (descriptor->desc.s == pollcb->copyset[i]->desc.s) { + /* Found an instance of the fd: remove this and any other copies */ + apr_uint32_t dst = i; + apr_uint32_t old_nelts = pollcb->nelts; + pollcb->nelts--; + for (i++; i < old_nelts; i++) { + if (descriptor->desc.s == pollcb->copyset[i]->desc.s) { + pollcb->nelts--; + } + else { + pollcb->pollset.ps[dst] = pollcb->pollset.ps[i]; + pollcb->copyset[dst] = pollcb->copyset[i]; + dst++; + } + } + return APR_SUCCESS; + } + } + + return APR_NOTFOUND; +} + +static apr_status_t impl_pollcb_poll(apr_pollcb_t *pollcb, + apr_interval_time_t timeout, + apr_pollcb_cb_t func, + void *baton) +{ + int ret; + apr_status_t rv = APR_SUCCESS; + apr_uint32_t i; + + if (timeout > 0) { + timeout /= 1000; + } +#ifdef WIN32 + ret = WSAPoll(pollcb->pollset.ps, pollcb->nelts, (int)timeout); +#else + ret = poll(pollcb->pollset.ps, pollcb->nelts, timeout); +#endif + if (ret < 0) { + return apr_get_netos_error(); + } + else if (ret == 0) { + return APR_TIMEUP; + } + else { + for (i = 0; i < pollcb->nelts; i++) { + if (pollcb->pollset.ps[i].revents != 0) { + apr_pollfd_t *pollfd = pollcb->copyset[i]; + pollfd->rtnevents = get_revent(pollcb->pollset.ps[i].revents); + rv = func(baton, pollfd); + if (rv) { + return rv; + } + } + } + } + return rv; +} + +static apr_pollcb_provider_t impl_cb = { + impl_pollcb_create, + impl_pollcb_add, + impl_pollcb_remove, + impl_pollcb_poll, + "poll" +}; + +apr_pollcb_provider_t *apr_pollcb_provider_poll = &impl_cb; + +#endif /* HAVE_POLL */ diff --git a/poll/unix/pollcb.c b/poll/unix/pollcb.c new file mode 100644 index 0000000..2971b02 --- /dev/null +++ b/poll/unix/pollcb.c @@ -0,0 +1,171 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 +/* POSIX defines 1024 for the FD_SETSIZE */ +#define FD_SETSIZE 1024 +#endif + +#include "apr.h" +#include "apr_poll.h" +#include "apr_time.h" +#include "apr_portable.h" +#include "apr_arch_file_io.h" +#include "apr_arch_networkio.h" +#include "apr_arch_poll_private.h" + +static apr_pollset_method_e pollset_default_method = POLLSET_DEFAULT_METHOD; +#if defined(HAVE_KQUEUE) +extern apr_pollcb_provider_t *apr_pollcb_provider_kqueue; +#endif +#if defined(HAVE_PORT_CREATE) +extern apr_pollcb_provider_t *apr_pollcb_provider_port; +#endif +#if defined(HAVE_EPOLL) +extern apr_pollcb_provider_t *apr_pollcb_provider_epoll; +#endif +#if defined(HAVE_POLL) +extern apr_pollcb_provider_t *apr_pollcb_provider_poll; +#endif + +static apr_pollcb_provider_t *pollcb_provider(apr_pollset_method_e method) +{ + apr_pollcb_provider_t *provider = NULL; + switch (method) { + case APR_POLLSET_KQUEUE: +#if defined(HAVE_KQUEUE) + provider = apr_pollcb_provider_kqueue; +#endif + break; + case APR_POLLSET_PORT: +#if defined(HAVE_PORT_CREATE) + provider = apr_pollcb_provider_port; +#endif + break; + case APR_POLLSET_EPOLL: +#if defined(HAVE_EPOLL) + provider = apr_pollcb_provider_epoll; +#endif + break; + case APR_POLLSET_POLL: +#if defined(HAVE_POLL) + provider = apr_pollcb_provider_poll; +#endif + break; + case APR_POLLSET_SELECT: + case APR_POLLSET_DEFAULT: + break; + } + return provider; +} + +APR_DECLARE(apr_status_t) apr_pollcb_create_ex(apr_pollcb_t **ret_pollcb, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags, + apr_pollset_method_e method) +{ + apr_status_t rv; + apr_pollcb_t *pollcb; + apr_pollcb_provider_t *provider = NULL; + + *ret_pollcb = NULL; + + #ifdef WIN32 + /* This will work only if ws2_32.dll has WSAPoll funtion. + * We could check the presence of the function here, + * but someone might implement other pollcb method in + * the future. + */ + if (method == APR_POLLSET_DEFAULT) { + method = APR_POLLSET_POLL; + } + #endif + + if (method == APR_POLLSET_DEFAULT) + method = pollset_default_method; + while (provider == NULL) { + provider = pollcb_provider(method); + if (!provider) { + if ((flags & APR_POLLSET_NODEFAULT) == APR_POLLSET_NODEFAULT) + return APR_ENOTIMPL; + if (method == pollset_default_method) + return APR_ENOTIMPL; + method = pollset_default_method; + } + } + + pollcb = apr_palloc(p, sizeof(*pollcb)); + pollcb->nelts = 0; + pollcb->nalloc = size; + pollcb->pool = p; + pollcb->provider = provider; + + rv = (*provider->create)(pollcb, size, p, flags); + if (rv == APR_ENOTIMPL) { + if (method == pollset_default_method) { + return rv; + } + + if ((flags & APR_POLLSET_NODEFAULT) == APR_POLLSET_NODEFAULT) { + return rv; + } + + /* Try with default provider */ + provider = pollcb_provider(pollset_default_method); + if (!provider) { + return APR_ENOTIMPL; + } + rv = (*provider->create)(pollcb, size, p, flags); + if (rv != APR_SUCCESS) { + return rv; + } + pollcb->provider = provider; + } + + *ret_pollcb = pollcb; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_pollcb_create(apr_pollcb_t **pollcb, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + apr_pollset_method_e method = APR_POLLSET_DEFAULT; + return apr_pollcb_create_ex(pollcb, size, p, flags, method); +} + +APR_DECLARE(apr_status_t) apr_pollcb_add(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + return (*pollcb->provider->add)(pollcb, descriptor); +} + +APR_DECLARE(apr_status_t) apr_pollcb_remove(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + return (*pollcb->provider->remove)(pollcb, descriptor); +} + + +APR_DECLARE(apr_status_t) apr_pollcb_poll(apr_pollcb_t *pollcb, + apr_interval_time_t timeout, + apr_pollcb_cb_t func, + void *baton) +{ + return (*pollcb->provider->poll)(pollcb, timeout, func, baton); +} diff --git a/poll/unix/pollset.c b/poll/unix/pollset.c new file mode 100644 index 0000000..1a7396b --- /dev/null +++ b/poll/unix/pollset.c @@ -0,0 +1,344 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 +/* POSIX defines 1024 for the FD_SETSIZE */ +#define FD_SETSIZE 1024 +#endif + +#include "apr.h" +#include "apr_poll.h" +#include "apr_time.h" +#include "apr_portable.h" +#include "apr_arch_file_io.h" +#include "apr_arch_networkio.h" +#include "apr_arch_poll_private.h" +#include "apr_arch_inherit.h" + +static apr_pollset_method_e pollset_default_method = POLLSET_DEFAULT_METHOD; + +#if !APR_FILES_AS_SOCKETS +#if defined (WIN32) + +/* Create a dummy wakeup socket pipe for interrupting the poller + */ +static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset) +{ + apr_status_t rv; + + if ((rv = apr_file_socket_pipe_create(&pollset->wakeup_pipe[0], + &pollset->wakeup_pipe[1], + pollset->pool)) != APR_SUCCESS) + return rv; + + pollset->wakeup_pfd.p = pollset->pool; + pollset->wakeup_pfd.reqevents = APR_POLLIN; + pollset->wakeup_pfd.desc_type = APR_POLL_FILE; + pollset->wakeup_pfd.desc.f = pollset->wakeup_pipe[0]; + + return apr_pollset_add(pollset, &pollset->wakeup_pfd); +} + +#else /* !WIN32 */ +static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset) +{ + return APR_ENOTIMPL; +} + +static apr_status_t apr_file_socket_pipe_close(apr_file_t *file) +{ + return APR_ENOTIMPL; +} + +#endif /* WIN32 */ +#else /* APR_FILES_AS_SOCKETS */ + +/* Create a dummy wakeup pipe for interrupting the poller + */ +static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset) +{ + apr_status_t rv; + + if ((rv = apr_file_pipe_create(&pollset->wakeup_pipe[0], + &pollset->wakeup_pipe[1], + pollset->pool)) != APR_SUCCESS) + return rv; + + pollset->wakeup_pfd.p = pollset->pool; + pollset->wakeup_pfd.reqevents = APR_POLLIN; + pollset->wakeup_pfd.desc_type = APR_POLL_FILE; + pollset->wakeup_pfd.desc.f = pollset->wakeup_pipe[0]; + + { + int flags; + + if ((flags = fcntl(pollset->wakeup_pipe[0]->filedes, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(pollset->wakeup_pipe[0]->filedes, F_SETFD, flags) == -1) + return errno; + } + { + int flags; + + if ((flags = fcntl(pollset->wakeup_pipe[1]->filedes, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(pollset->wakeup_pipe[1]->filedes, F_SETFD, flags) == -1) + return errno; + } + + return apr_pollset_add(pollset, &pollset->wakeup_pfd); +} +#endif /* !APR_FILES_AS_SOCKETS */ + +/* Read and discard what's ever in the wakeup pipe. + */ +void apr_pollset_drain_wakeup_pipe(apr_pollset_t *pollset) +{ + char rb[512]; + apr_size_t nr = sizeof(rb); + + while (apr_file_read(pollset->wakeup_pipe[0], rb, &nr) == APR_SUCCESS) { + /* Although we write just one byte to the other end of the pipe + * during wakeup, multiple threads could call the wakeup. + * So simply drain out from the input side of the pipe all + * the data. + */ + if (nr != sizeof(rb)) + break; + } +} + +static apr_status_t pollset_cleanup(void *p) +{ + apr_pollset_t *pollset = (apr_pollset_t *) p; + if (pollset->provider->cleanup) { + (*pollset->provider->cleanup)(pollset); + } + if (pollset->flags & APR_POLLSET_WAKEABLE) { + /* Close both sides of the wakeup pipe */ + if (pollset->wakeup_pipe[0]) { +#if APR_FILES_AS_SOCKETS + apr_file_close(pollset->wakeup_pipe[0]); +#else + apr_file_socket_pipe_close(pollset->wakeup_pipe[0]); +#endif + pollset->wakeup_pipe[0] = NULL; + } + if (pollset->wakeup_pipe[1]) { +#if APR_FILES_AS_SOCKETS + apr_file_close(pollset->wakeup_pipe[1]); +#else + apr_file_socket_pipe_close(pollset->wakeup_pipe[1]); +#endif + pollset->wakeup_pipe[1] = NULL; + } + } + + return APR_SUCCESS; +} + +#if defined(HAVE_KQUEUE) +extern apr_pollset_provider_t *apr_pollset_provider_kqueue; +#endif +#if defined(HAVE_PORT_CREATE) +extern apr_pollset_provider_t *apr_pollset_provider_port; +#endif +#if defined(HAVE_EPOLL) +extern apr_pollset_provider_t *apr_pollset_provider_epoll; +#endif +#if defined(HAVE_POLL) +extern apr_pollset_provider_t *apr_pollset_provider_poll; +#endif +extern apr_pollset_provider_t *apr_pollset_provider_select; + +static apr_pollset_provider_t *pollset_provider(apr_pollset_method_e method) +{ + apr_pollset_provider_t *provider = NULL; + switch (method) { + case APR_POLLSET_KQUEUE: +#if defined(HAVE_KQUEUE) + provider = apr_pollset_provider_kqueue; +#endif + break; + case APR_POLLSET_PORT: +#if defined(HAVE_PORT_CREATE) + provider = apr_pollset_provider_port; +#endif + break; + case APR_POLLSET_EPOLL: +#if defined(HAVE_EPOLL) + provider = apr_pollset_provider_epoll; +#endif + break; + case APR_POLLSET_POLL: +#if defined(HAVE_POLL) + provider = apr_pollset_provider_poll; +#endif + break; + case APR_POLLSET_SELECT: + provider = apr_pollset_provider_select; + break; + case APR_POLLSET_DEFAULT: + break; + } + return provider; +} + +APR_DECLARE(apr_status_t) apr_pollset_create_ex(apr_pollset_t **ret_pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags, + apr_pollset_method_e method) +{ + apr_status_t rv; + apr_pollset_t *pollset; + apr_pollset_provider_t *provider = NULL; + + *ret_pollset = NULL; + + #ifdef WIN32 + /* Favor WSAPoll if supported. + * This will work only if ws2_32.dll has WSAPoll funtion. + * In other cases it will fall back to select() method unless + * the APR_POLLSET_NODEFAULT is added to the flags. + */ + if (method == APR_POLLSET_DEFAULT) { + method = APR_POLLSET_POLL; + } + #endif + + if (method == APR_POLLSET_DEFAULT) + method = pollset_default_method; + while (provider == NULL) { + provider = pollset_provider(method); + if (!provider) { + if ((flags & APR_POLLSET_NODEFAULT) == APR_POLLSET_NODEFAULT) + return APR_ENOTIMPL; + if (method == pollset_default_method) + return APR_ENOTIMPL; + method = pollset_default_method; + } + } + if (flags & APR_POLLSET_WAKEABLE) { + /* Add room for wakeup descriptor */ + size++; + } + + pollset = apr_palloc(p, sizeof(*pollset)); + pollset->nelts = 0; + pollset->nalloc = size; + pollset->pool = p; + pollset->flags = flags; + pollset->provider = provider; + + rv = (*provider->create)(pollset, size, p, flags); + if (rv == APR_ENOTIMPL) { + if (method == pollset_default_method) { + return rv; + } + provider = pollset_provider(pollset_default_method); + if (!provider) { + return APR_ENOTIMPL; + } + rv = (*provider->create)(pollset, size, p, flags); + if (rv != APR_SUCCESS) { + return rv; + } + pollset->provider = provider; + } + else if (rv != APR_SUCCESS) { + return rv; + } + if (flags & APR_POLLSET_WAKEABLE) { + /* Create wakeup pipe */ + if ((rv = create_wakeup_pipe(pollset)) != APR_SUCCESS) { + return rv; + } + } + if ((flags & APR_POLLSET_WAKEABLE) || provider->cleanup) + apr_pool_cleanup_register(p, pollset, pollset_cleanup, + apr_pool_cleanup_null); + + *ret_pollset = pollset; + return APR_SUCCESS; +} + +APR_DECLARE(const char *) apr_pollset_method_name(apr_pollset_t *pollset) +{ + return pollset->provider->name; +} + +APR_DECLARE(const char *) apr_poll_method_defname() +{ + apr_pollset_provider_t *provider = NULL; + + provider = pollset_provider(pollset_default_method); + if (provider) + return provider->name; + else + return "unknown"; +} + +APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + apr_pollset_method_e method = APR_POLLSET_DEFAULT; + return apr_pollset_create_ex(pollset, size, p, flags, method); +} + +APR_DECLARE(apr_status_t) apr_pollset_destroy(apr_pollset_t * pollset) +{ + if (pollset->flags & APR_POLLSET_WAKEABLE || + pollset->provider->cleanup) + return apr_pool_cleanup_run(pollset->pool, pollset, + pollset_cleanup); + else + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_pollset_wakeup(apr_pollset_t *pollset) +{ + if (pollset->flags & APR_POLLSET_WAKEABLE) + return apr_file_putc(1, pollset->wakeup_pipe[1]); + else + return APR_EINIT; +} + +APR_DECLARE(apr_status_t) apr_pollset_add(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + return (*pollset->provider->add)(pollset, descriptor); +} + +APR_DECLARE(apr_status_t) apr_pollset_remove(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + return (*pollset->provider->remove)(pollset, descriptor); +} + +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) +{ + return (*pollset->provider->poll)(pollset, timeout, num, descriptors); +} diff --git a/poll/unix/port.c b/poll/unix/port.c new file mode 100644 index 0000000..7a31c46 --- /dev/null +++ b/poll/unix/port.c @@ -0,0 +1,577 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr.h" +#include "apr_poll.h" +#include "apr_time.h" +#include "apr_portable.h" +#include "apr_atomic.h" +#include "apr_arch_file_io.h" +#include "apr_arch_networkio.h" +#include "apr_arch_poll_private.h" +#include "apr_arch_inherit.h" + +#if defined(HAVE_PORT_CREATE) + +static apr_int16_t get_event(apr_int16_t event) +{ + apr_int16_t rv = 0; + + if (event & APR_POLLIN) + rv |= POLLIN; + if (event & APR_POLLPRI) + rv |= POLLPRI; + if (event & APR_POLLOUT) + rv |= POLLOUT; + /* POLLERR, POLLHUP, and POLLNVAL aren't valid as requested events */ + + return rv; +} + +static apr_int16_t get_revent(apr_int16_t event) +{ + apr_int16_t rv = 0; + + if (event & POLLIN) + rv |= APR_POLLIN; + if (event & POLLPRI) + rv |= APR_POLLPRI; + if (event & POLLOUT) + rv |= APR_POLLOUT; + if (event & POLLERR) + rv |= APR_POLLERR; + if (event & POLLHUP) + rv |= APR_POLLHUP; + if (event & POLLNVAL) + rv |= APR_POLLNVAL; + + return rv; +} + + +struct apr_pollset_private_t +{ + int port_fd; + port_event_t *port_set; + apr_pollfd_t *result_set; +#if APR_HAS_THREADS + /* A thread mutex to protect operations on the rings */ + apr_thread_mutex_t *ring_lock; +#endif + /* A ring containing all of the pollfd_t that are active */ + APR_RING_HEAD(pfd_query_ring_t, pfd_elem_t) query_ring; + /* A ring containing the pollfd_t that will be added on the + * next call to apr_pollset_poll(). + */ + APR_RING_HEAD(pfd_add_ring_t, pfd_elem_t) add_ring; + /* A ring of pollfd_t that have been used, and then _remove'd */ + APR_RING_HEAD(pfd_free_ring_t, pfd_elem_t) free_ring; + /* A ring of pollfd_t where rings that have been _remove'd but + might still be inside a _poll */ + APR_RING_HEAD(pfd_dead_ring_t, pfd_elem_t) dead_ring; + /* number of threads in poll */ + volatile apr_uint32_t waiting; +}; + +static apr_status_t call_port_getn(int port, port_event_t list[], + unsigned int max, unsigned int *nget, + apr_interval_time_t timeout) +{ + struct timespec tv, *tvptr; + int ret; + apr_status_t rv = APR_SUCCESS; + + if (timeout < 0) { + tvptr = NULL; + } + else { + tv.tv_sec = (long) apr_time_sec(timeout); + tv.tv_nsec = (long) apr_time_usec(timeout) * 1000; + tvptr = &tv; + } + + list[0].portev_user = (void *)-1; /* so we can double check that an + * event was returned + */ + + ret = port_getn(port, list, max, nget, tvptr); + /* Note: 32-bit port_getn() on Solaris 10 x86 returns large negative + * values instead of 0 when returning immediately. + */ + + if (ret == -1) { + rv = apr_get_netos_error(); + + switch(rv) { + case EINTR: + case ETIME: + if (*nget > 0 && list[0].portev_user != (void *)-1) { + /* This confusing API can return an event at the same time + * that it reports EINTR or ETIME. If that occurs, just + * report the event. With EINTR, nget can be > 0 without + * any event, so check that portev_user was filled in. + * + * (Maybe it will be simplified; see thread + * http://mail.opensolaris.org + * /pipermail/networking-discuss/2009-August/011979.html + * This code will still work afterwards.) + */ + rv = APR_SUCCESS; + break; + } + if (rv == ETIME) { + rv = APR_TIMEUP; + } + /* fall-through */ + default: + *nget = 0; + } + } + else if (*nget == 0) { + rv = APR_TIMEUP; + } + + return rv; +} + +static apr_status_t impl_pollset_cleanup(apr_pollset_t *pollset) +{ + close(pollset->p->port_fd); + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_create(apr_pollset_t *pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + apr_status_t rv = APR_SUCCESS; + pollset->p = apr_palloc(p, sizeof(apr_pollset_private_t)); +#if APR_HAS_THREADS + if (flags & APR_POLLSET_THREADSAFE && + ((rv = apr_thread_mutex_create(&pollset->p->ring_lock, + APR_THREAD_MUTEX_DEFAULT, + p)) != APR_SUCCESS)) { + pollset->p = NULL; + return rv; + } +#else + if (flags & APR_POLLSET_THREADSAFE) { + pollset->p = NULL; + return APR_ENOTIMPL; + } +#endif + pollset->p->waiting = 0; + + pollset->p->port_set = apr_palloc(p, size * sizeof(port_event_t)); + + pollset->p->port_fd = port_create(); + + if (pollset->p->port_fd < 0) { + pollset->p = NULL; + return apr_get_netos_error(); + } + + { + int flags; + + if ((flags = fcntl(pollset->p->port_fd, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(pollset->p->port_fd, F_SETFD, flags) == -1) + return errno; + } + + pollset->p->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + + APR_RING_INIT(&pollset->p->query_ring, pfd_elem_t, link); + APR_RING_INIT(&pollset->p->add_ring, pfd_elem_t, link); + APR_RING_INIT(&pollset->p->free_ring, pfd_elem_t, link); + APR_RING_INIT(&pollset->p->dead_ring, pfd_elem_t, link); + + return rv; +} + +static apr_status_t impl_pollset_add(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + apr_os_sock_t fd; + pfd_elem_t *elem; + int res; + apr_status_t rv = APR_SUCCESS; + + pollset_lock_rings(); + + if (!APR_RING_EMPTY(&(pollset->p->free_ring), pfd_elem_t, link)) { + elem = APR_RING_FIRST(&(pollset->p->free_ring)); + APR_RING_REMOVE(elem, link); + } + else { + elem = (pfd_elem_t *) apr_palloc(pollset->pool, sizeof(pfd_elem_t)); + APR_RING_ELEM_INIT(elem, link); + elem->on_query_ring = 0; + } + elem->pfd = *descriptor; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + /* If another thread is polling, notify the kernel immediately; otherwise, + * wait until the next call to apr_pollset_poll(). + */ + if (apr_atomic_read32(&pollset->p->waiting)) { + res = port_associate(pollset->p->port_fd, PORT_SOURCE_FD, fd, + get_event(descriptor->reqevents), (void *)elem); + + if (res < 0) { + rv = apr_get_netos_error(); + APR_RING_INSERT_TAIL(&(pollset->p->free_ring), elem, pfd_elem_t, link); + } + else { + elem->on_query_ring = 1; + APR_RING_INSERT_TAIL(&(pollset->p->query_ring), elem, pfd_elem_t, link); + } + } + else { + APR_RING_INSERT_TAIL(&(pollset->p->add_ring), elem, pfd_elem_t, link); + } + + pollset_unlock_rings(); + + return rv; +} + +static apr_status_t impl_pollset_remove(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + apr_os_sock_t fd; + pfd_elem_t *ep; + apr_status_t rv = APR_SUCCESS; + int res; + int err = 0; + int found; + + pollset_lock_rings(); + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + /* Search the add ring first. This ring is often shorter, + * and it often contains the descriptor being removed. + * (For the common scenario where apr_pollset_poll() + * returns activity for the descriptor and the descriptor + * is then removed from the pollset, it will have just + * been moved to the add ring by apr_pollset_poll().) + * + * If it is on the add ring, it isn't associated with the + * event port yet/anymore. + */ + found = 0; + for (ep = APR_RING_FIRST(&(pollset->p->add_ring)); + ep != APR_RING_SENTINEL(&(pollset->p->add_ring), + pfd_elem_t, link); + ep = APR_RING_NEXT(ep, link)) { + + if (descriptor->desc.s == ep->pfd.desc.s) { + found = 1; + APR_RING_REMOVE(ep, link); + APR_RING_INSERT_TAIL(&(pollset->p->free_ring), + ep, pfd_elem_t, link); + break; + } + } + + if (!found) { + res = port_dissociate(pollset->p->port_fd, PORT_SOURCE_FD, fd); + + if (res < 0) { + /* The expected case for this failure is that another + * thread's call to port_getn() returned this fd and + * disassociated the fd from the event port, and + * impl_pollset_poll() is blocked on the ring lock, + * which this thread holds. + */ + err = errno; + rv = APR_NOTFOUND; + } + + for (ep = APR_RING_FIRST(&(pollset->p->query_ring)); + ep != APR_RING_SENTINEL(&(pollset->p->query_ring), + pfd_elem_t, link); + ep = APR_RING_NEXT(ep, link)) { + + if (descriptor->desc.s == ep->pfd.desc.s) { + APR_RING_REMOVE(ep, link); + ep->on_query_ring = 0; + APR_RING_INSERT_TAIL(&(pollset->p->dead_ring), + ep, pfd_elem_t, link); + if (ENOENT == err) { + rv = APR_SUCCESS; + } + break; + } + } + } + + pollset_unlock_rings(); + + return rv; +} + +static apr_status_t impl_pollset_poll(apr_pollset_t *pollset, + apr_interval_time_t timeout, + apr_int32_t *num, + const apr_pollfd_t **descriptors) +{ + apr_os_sock_t fd; + int ret, i, j; + unsigned int nget; + pfd_elem_t *ep; + apr_status_t rv = APR_SUCCESS; + apr_pollfd_t fp; + + nget = 1; + + pollset_lock_rings(); + + apr_atomic_inc32(&pollset->p->waiting); + + while (!APR_RING_EMPTY(&(pollset->p->add_ring), pfd_elem_t, link)) { + ep = APR_RING_FIRST(&(pollset->p->add_ring)); + APR_RING_REMOVE(ep, link); + + if (ep->pfd.desc_type == APR_POLL_SOCKET) { + fd = ep->pfd.desc.s->socketdes; + } + else { + fd = ep->pfd.desc.f->filedes; + } + + ret = port_associate(pollset->p->port_fd, PORT_SOURCE_FD, + fd, get_event(ep->pfd.reqevents), ep); + if (ret < 0) { + rv = apr_get_netos_error(); + APR_RING_INSERT_TAIL(&(pollset->p->free_ring), ep, pfd_elem_t, link); + break; + } + + ep->on_query_ring = 1; + APR_RING_INSERT_TAIL(&(pollset->p->query_ring), ep, pfd_elem_t, link); + } + + pollset_unlock_rings(); + + if (rv != APR_SUCCESS) { + apr_atomic_dec32(&pollset->p->waiting); + return rv; + } + + rv = call_port_getn(pollset->p->port_fd, pollset->p->port_set, + pollset->nalloc, &nget, timeout); + + /* decrease the waiting ASAP to reduce the window for calling + port_associate within apr_pollset_add() */ + apr_atomic_dec32(&pollset->p->waiting); + + (*num) = nget; + if (nget) { + + pollset_lock_rings(); + + for (i = 0, j = 0; i < nget; i++) { + fp = (((pfd_elem_t*)(pollset->p->port_set[i].portev_user))->pfd); + if ((pollset->flags & APR_POLLSET_WAKEABLE) && + fp.desc_type == APR_POLL_FILE && + fp.desc.f == pollset->wakeup_pipe[0]) { + apr_pollset_drain_wakeup_pipe(pollset); + rv = APR_EINTR; + } + else { + pollset->p->result_set[j] = fp; + pollset->p->result_set[j].rtnevents = + get_revent(pollset->p->port_set[i].portev_events); + + /* If the ring element is still on the query ring, move it + * to the add ring for re-association with the event port + * later. (It may have already been moved to the dead ring + * by a call to pollset_remove on another thread.) + */ + ep = (pfd_elem_t *)pollset->p->port_set[i].portev_user; + if (ep->on_query_ring) { + APR_RING_REMOVE(ep, link); + ep->on_query_ring = 0; + APR_RING_INSERT_TAIL(&(pollset->p->add_ring), ep, + pfd_elem_t, link); + } + j++; + } + } + pollset_unlock_rings(); + if ((*num = j)) { /* any event besides wakeup pipe? */ + rv = APR_SUCCESS; + if (descriptors) { + *descriptors = pollset->p->result_set; + } + } + } + + pollset_lock_rings(); + + /* Shift all PFDs in the Dead Ring to the Free Ring */ + APR_RING_CONCAT(&(pollset->p->free_ring), &(pollset->p->dead_ring), pfd_elem_t, link); + + pollset_unlock_rings(); + + return rv; +} + +static apr_pollset_provider_t impl = { + impl_pollset_create, + impl_pollset_add, + impl_pollset_remove, + impl_pollset_poll, + impl_pollset_cleanup, + "port" +}; + +apr_pollset_provider_t *apr_pollset_provider_port = &impl; + +static apr_status_t cb_cleanup(void *p_) +{ + apr_pollcb_t *pollcb = (apr_pollcb_t *) p_; + close(pollcb->fd); + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + pollcb->fd = port_create(); + + if (pollcb->fd < 0) { + return apr_get_netos_error(); + } + + { + int flags; + + if ((flags = fcntl(pollcb->fd, F_GETFD)) == -1) + return errno; + + flags |= FD_CLOEXEC; + if (fcntl(pollcb->fd, F_SETFD, flags) == -1) + return errno; + } + + pollcb->pollset.port = apr_palloc(p, size * sizeof(port_event_t)); + apr_pool_cleanup_register(p, pollcb, cb_cleanup, apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_add(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + int ret, fd; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + ret = port_associate(pollcb->fd, PORT_SOURCE_FD, fd, + get_event(descriptor->reqevents), descriptor); + + if (ret == -1) { + return apr_get_netos_error(); + } + + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_remove(apr_pollcb_t *pollcb, + apr_pollfd_t *descriptor) +{ + int fd, ret; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { + fd = descriptor->desc.f->filedes; + } + + ret = port_dissociate(pollcb->fd, PORT_SOURCE_FD, fd); + + if (ret < 0) { + return APR_NOTFOUND; + } + + return APR_SUCCESS; +} + +static apr_status_t impl_pollcb_poll(apr_pollcb_t *pollcb, + apr_interval_time_t timeout, + apr_pollcb_cb_t func, + void *baton) +{ + apr_pollfd_t *pollfd; + apr_status_t rv; + unsigned int i, nget = 1; + + rv = call_port_getn(pollcb->fd, pollcb->pollset.port, pollcb->nalloc, + &nget, timeout); + + if (nget) { + for (i = 0; i < nget; i++) { + pollfd = (apr_pollfd_t *)(pollcb->pollset.port[i].portev_user); + pollfd->rtnevents = get_revent(pollcb->pollset.port[i].portev_events); + + rv = func(baton, pollfd); + if (rv) { + return rv; + } + rv = apr_pollcb_add(pollcb, pollfd); + } + } + + return rv; +} + +static apr_pollcb_provider_t impl_cb = { + impl_pollcb_create, + impl_pollcb_add, + impl_pollcb_remove, + impl_pollcb_poll, + "port" +}; + +apr_pollcb_provider_t *apr_pollcb_provider_port = &impl_cb; + +#endif /* HAVE_PORT_CREATE */ diff --git a/poll/unix/select.c b/poll/unix/select.c new file mode 100644 index 0000000..61a064f --- /dev/null +++ b/poll/unix/select.c @@ -0,0 +1,449 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 +/* POSIX defines 1024 for the FD_SETSIZE */ +#define FD_SETSIZE 1024 +#endif + +#include "apr.h" +#include "apr_poll.h" +#include "apr_time.h" +#include "apr_portable.h" +#include "apr_arch_file_io.h" +#include "apr_arch_networkio.h" +#include "apr_arch_poll_private.h" + +#ifdef POLL_USES_SELECT + +APR_DECLARE(apr_status_t) apr_poll(apr_pollfd_t *aprset, int num, + apr_int32_t *nsds, + apr_interval_time_t timeout) +{ + fd_set readset, writeset, exceptset; + int rv, i; + int maxfd = -1; + struct timeval tv, *tvptr; +#ifdef NETWARE + apr_datatype_e set_type = APR_NO_DESC; +#endif + +#ifdef WIN32 + /* On Win32, select() must be presented with at least one socket to + * poll on, or select() will return WSAEINVAL. So, we'll just + * short-circuit and bail now. + */ + if (num == 0) { + (*nsds) = 0; + if (timeout > 0) { + apr_sleep(timeout); + return APR_TIMEUP; + } + return APR_SUCCESS; + } +#endif + + if (timeout < 0) { + tvptr = NULL; + } + else { + tv.tv_sec = (long) apr_time_sec(timeout); + tv.tv_usec = (long) apr_time_usec(timeout); + tvptr = &tv; + } + + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + + for (i = 0; i < num; i++) { + apr_os_sock_t fd; + + aprset[i].rtnevents = 0; + + if (aprset[i].desc_type == APR_POLL_SOCKET) { +#ifdef NETWARE + if (HAS_PIPES(set_type)) { + return APR_EBADF; + } + else { + set_type = APR_POLL_SOCKET; + } +#endif + fd = aprset[i].desc.s->socketdes; + } + else if (aprset[i].desc_type == APR_POLL_FILE) { +#if !APR_FILES_AS_SOCKETS + return APR_EBADF; +#else +#ifdef NETWARE + if (aprset[i].desc.f->is_pipe && !HAS_SOCKETS(set_type)) { + set_type = APR_POLL_FILE; + } + else + return APR_EBADF; +#endif /* NETWARE */ + + fd = aprset[i].desc.f->filedes; + +#endif /* APR_FILES_AS_SOCKETS */ + } + else { + break; + } +#if !defined(WIN32) && !defined(NETWARE) /* socket sets handled with array of handles */ + if (fd >= FD_SETSIZE) { + /* XXX invent new error code so application has a clue */ + return APR_EBADF; + } +#endif + if (aprset[i].reqevents & APR_POLLIN) { + FD_SET(fd, &readset); + } + if (aprset[i].reqevents & APR_POLLOUT) { + FD_SET(fd, &writeset); + } + if (aprset[i].reqevents & + (APR_POLLPRI | APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) { + FD_SET(fd, &exceptset); + } + if ((int) fd > maxfd) { + maxfd = (int) fd; + } + } + +#ifdef NETWARE + if (HAS_PIPES(set_type)) { + rv = pipe_select(maxfd + 1, &readset, &writeset, &exceptset, tvptr); + } + else { +#endif + + rv = select(maxfd + 1, &readset, &writeset, &exceptset, tvptr); + +#ifdef NETWARE + } +#endif + + (*nsds) = rv; + if ((*nsds) == 0) { + return APR_TIMEUP; + } + if ((*nsds) < 0) { + return apr_get_netos_error(); + } + + (*nsds) = 0; + for (i = 0; i < num; i++) { + apr_os_sock_t fd; + + if (aprset[i].desc_type == APR_POLL_SOCKET) { + fd = aprset[i].desc.s->socketdes; + } + else if (aprset[i].desc_type == APR_POLL_FILE) { +#if !APR_FILES_AS_SOCKETS + return APR_EBADF; +#else + fd = aprset[i].desc.f->filedes; +#endif + } + else { + break; + } + if (FD_ISSET(fd, &readset)) { + aprset[i].rtnevents |= APR_POLLIN; + } + if (FD_ISSET(fd, &writeset)) { + aprset[i].rtnevents |= APR_POLLOUT; + } + if (FD_ISSET(fd, &exceptset)) { + aprset[i].rtnevents |= APR_POLLERR; + } + if (aprset[i].rtnevents) { + (*nsds)++; + } + } + + return APR_SUCCESS; +} + +#endif /* POLL_USES_SELECT */ + +struct apr_pollset_private_t +{ + fd_set readset, writeset, exceptset; + int maxfd; + apr_pollfd_t *query_set; + apr_pollfd_t *result_set; + apr_uint32_t flags; +#ifdef NETWARE + int set_type; +#endif +}; + +static apr_status_t impl_pollset_create(apr_pollset_t *pollset, + apr_uint32_t size, + apr_pool_t *p, + apr_uint32_t flags) +{ + if (flags & APR_POLLSET_THREADSAFE) { + pollset->p = NULL; + return APR_ENOTIMPL; + } +#ifdef FD_SETSIZE + if (size > FD_SETSIZE) { + pollset->p = NULL; + return APR_EINVAL; + } +#endif + pollset->p = apr_palloc(p, sizeof(apr_pollset_private_t)); + FD_ZERO(&(pollset->p->readset)); + FD_ZERO(&(pollset->p->writeset)); + FD_ZERO(&(pollset->p->exceptset)); + pollset->p->maxfd = 0; +#ifdef NETWARE + pollset->p->set_type = APR_NO_DESC; +#endif + pollset->p->query_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + pollset->p->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); + + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_add(apr_pollset_t *pollset, + const apr_pollfd_t *descriptor) +{ + apr_os_sock_t fd; + + if (pollset->nelts == pollset->nalloc) { + return APR_ENOMEM; + } + + pollset->p->query_set[pollset->nelts] = *descriptor; + + if (descriptor->desc_type == APR_POLL_SOCKET) { +#ifdef NETWARE + /* NetWare can't handle mixed descriptor types in select() */ + if (HAS_PIPES(pollset->p->set_type)) { + return APR_EBADF; + } + else { + pollset->p->set_type = APR_POLL_SOCKET; + } +#endif + fd = descriptor->desc.s->socketdes; + } + else { +#if !APR_FILES_AS_SOCKETS + if ((pollset->flags & APR_POLLSET_WAKEABLE) && + descriptor->desc.f == pollset->wakeup_pipe[0]) + fd = (apr_os_sock_t)descriptor->desc.f->filedes; + else + return APR_EBADF; +#else +#ifdef NETWARE + /* NetWare can't handle mixed descriptor types in select() */ + if (descriptor->desc.f->is_pipe && !HAS_SOCKETS(pollset->p->set_type)) { + pollset->p->set_type = APR_POLL_FILE; + fd = descriptor->desc.f->filedes; + } + else { + return APR_EBADF; + } +#else + fd = descriptor->desc.f->filedes; +#endif +#endif + } +#if !defined(WIN32) && !defined(NETWARE) /* socket sets handled with array of handles */ + if (fd >= FD_SETSIZE) { + /* XXX invent new error code so application has a clue */ + return APR_EBADF; + } +#endif + if (descriptor->reqevents & APR_POLLIN) { + FD_SET(fd, &(pollset->p->readset)); + } + if (descriptor->reqevents & APR_POLLOUT) { + FD_SET(fd, &(pollset->p->writeset)); + } + if (descriptor->reqevents & + (APR_POLLPRI | APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) { + FD_SET(fd, &(pollset->p->exceptset)); + } + if ((int) fd > pollset->p->maxfd) { + pollset->p->maxfd = (int) fd; + } + pollset->nelts++; + return APR_SUCCESS; +} + +static apr_status_t impl_pollset_remove(apr_pollset_t * pollset, + const apr_pollfd_t * descriptor) +{ + apr_uint32_t i; + apr_os_sock_t fd; + + if (descriptor->desc_type == APR_POLL_SOCKET) { + fd = descriptor->desc.s->socketdes; + } + else { +#if !APR_FILES_AS_SOCKETS + return APR_EBADF; +#else + fd = descriptor->desc.f->filedes; +#endif + } + + for (i = 0; i < pollset->nelts; i++) { + if (descriptor->desc.s == pollset->p->query_set[i].desc.s) { + /* Found an instance of the fd: remove this and any other copies */ + apr_uint32_t dst = i; + apr_uint32_t old_nelts = pollset->nelts; + pollset->nelts--; + for (i++; i < old_nelts; i++) { + if (descriptor->desc.s == pollset->p->query_set[i].desc.s) { + pollset->nelts--; + } + else { + pollset->p->query_set[dst] = pollset->p->query_set[i]; + dst++; + } + } + FD_CLR(fd, &(pollset->p->readset)); + FD_CLR(fd, &(pollset->p->writeset)); + FD_CLR(fd, &(pollset->p->exceptset)); + if (((int) fd == pollset->p->maxfd) && (pollset->p->maxfd > 0)) { + pollset->p->maxfd--; + } + return APR_SUCCESS; + } + } + + return APR_NOTFOUND; +} + +static apr_status_t impl_pollset_poll(apr_pollset_t *pollset, + apr_interval_time_t timeout, + apr_int32_t *num, + const apr_pollfd_t **descriptors) +{ + int rs; + apr_uint32_t i, j; + struct timeval tv, *tvptr; + fd_set readset, writeset, exceptset; + apr_status_t rv = APR_SUCCESS; + +#ifdef WIN32 + /* On Win32, select() must be presented with at least one socket to + * poll on, or select() will return WSAEINVAL. So, we'll just + * short-circuit and bail now. + */ + if (pollset->nelts == 0) { + (*num) = 0; + if (timeout > 0) { + apr_sleep(timeout); + return APR_TIMEUP; + } + return APR_SUCCESS; + } +#endif + + if (timeout < 0) { + tvptr = NULL; + } + else { + tv.tv_sec = (long) apr_time_sec(timeout); + tv.tv_usec = (long) apr_time_usec(timeout); + tvptr = &tv; + } + + memcpy(&readset, &(pollset->p->readset), sizeof(fd_set)); + memcpy(&writeset, &(pollset->p->writeset), sizeof(fd_set)); + memcpy(&exceptset, &(pollset->p->exceptset), sizeof(fd_set)); + +#ifdef NETWARE + if (HAS_PIPES(pollset->p->set_type)) { + rs = pipe_select(pollset->p->maxfd + 1, &readset, &writeset, &exceptset, + tvptr); + } + else +#endif + rs = select(pollset->p->maxfd + 1, &readset, &writeset, &exceptset, + tvptr); + + (*num) = rs; + if (rs < 0) { + return apr_get_netos_error(); + } + if (rs == 0) { + return APR_TIMEUP; + } + j = 0; + for (i = 0; i < pollset->nelts; i++) { + apr_os_sock_t fd; + if (pollset->p->query_set[i].desc_type == APR_POLL_SOCKET) { + fd = pollset->p->query_set[i].desc.s->socketdes; + } + else { + if ((pollset->flags & APR_POLLSET_WAKEABLE) && + pollset->p->query_set[i].desc.f == pollset->wakeup_pipe[0]) { + apr_pollset_drain_wakeup_pipe(pollset); + rv = APR_EINTR; + continue; + } + else { +#if !APR_FILES_AS_SOCKETS + return APR_EBADF; +#else + fd = pollset->p->query_set[i].desc.f->filedes; +#endif + } + } + if (FD_ISSET(fd, &readset) || FD_ISSET(fd, &writeset) || + FD_ISSET(fd, &exceptset)) { + pollset->p->result_set[j] = pollset->p->query_set[i]; + pollset->p->result_set[j].rtnevents = 0; + if (FD_ISSET(fd, &readset)) { + pollset->p->result_set[j].rtnevents |= APR_POLLIN; + } + if (FD_ISSET(fd, &writeset)) { + pollset->p->result_set[j].rtnevents |= APR_POLLOUT; + } + if (FD_ISSET(fd, &exceptset)) { + pollset->p->result_set[j].rtnevents |= APR_POLLERR; + } + j++; + } + } + if (((*num) = j) != 0) + rv = APR_SUCCESS; + + if (descriptors) + *descriptors = pollset->p->result_set; + return rv; +} + +static apr_pollset_provider_t impl = { + impl_pollset_create, + impl_pollset_add, + impl_pollset_remove, + impl_pollset_poll, + NULL, + "select" +}; + +apr_pollset_provider_t *apr_pollset_provider_select = &impl; |