summaryrefslogtreecommitdiff
path: root/poll
diff options
context:
space:
mode:
authorLorry <lorry@roadtrain.codethink.co.uk>2012-08-28 17:19:35 +0100
committerLorry <lorry@roadtrain.codethink.co.uk>2012-08-28 17:19:35 +0100
commitd3c52eb4b4402f0afbebc3f36b9ef8c0994a6c1a (patch)
treeb5ed37757fd9e0f821d103c26692e35ddf2976cb /poll
downloadlibapr-tarball-d3c52eb4b4402f0afbebc3f36b9ef8c0994a6c1a.tar.gz
Tarball conversion
Diffstat (limited to 'poll')
-rw-r--r--poll/os2/poll.c105
-rw-r--r--poll/os2/pollset.c230
-rw-r--r--poll/unix/epoll.c462
-rw-r--r--poll/unix/kqueue.c480
-rw-r--r--poll/unix/poll.c440
-rw-r--r--poll/unix/pollcb.c171
-rw-r--r--poll/unix/pollset.c344
-rw-r--r--poll/unix/port.c577
-rw-r--r--poll/unix/select.c449
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;