blob: 1f2d6346a3b3e064bef3ad51498afd14f94f43cc [file]
/* Copyright 2005 The Apache Software Foundation or its licensors, as
* applicable.
*
* Licensed 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_poll.h"
#include "apr_ring.h"
#include "apr_thread_cond.h"
#include "apr_thread_mutex.h"
#include "io_multiplexer.h"
extern server_rec *ap_server_conf;
APR_RING_HEAD(timeout_ring_header_t, conn_state_t);
struct io_multiplexer {
int stopped;
apr_thread_mutex_t *lock;
apr_thread_mutex_t *pollset_lock;
apr_pollset_t *pollset;
apr_int32_t num_pending_events;
const apr_pollfd_t *next_pending_event;
struct timeout_ring_header_t pending_timeouts;
struct timeout_ring_header_t expired_timeouts;
volatile int poll_sequence_num;
};
static apr_status_t io_multiplexer_remove_internal(io_multiplexer *iom,
multiplexable *m);
apr_status_t io_multiplexer_create(io_multiplexer **iom, apr_pool_t *p,
apr_uint32_t max_descriptors)
{
apr_status_t rv;
*iom = (io_multiplexer *)apr_palloc(p, sizeof(**iom));
rv = apr_thread_mutex_create(&((*iom)->lock), APR_THREAD_MUTEX_DEFAULT, p);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_thread_mutex_create(&((*iom)->pollset_lock),
APR_THREAD_MUTEX_DEFAULT, p);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_pollset_create(&((*iom)->pollset), max_descriptors, p,
APR_POLLSET_THREADSAFE);
if (rv != APR_SUCCESS) {
return rv;
}
(*iom)->stopped = 0;
(*iom)->num_pending_events = 0;
(*iom)->next_pending_event = NULL;
APR_RING_INIT(&((*iom)->pending_timeouts), conn_state_t, timeout_list);
APR_RING_INIT(&((*iom)->expired_timeouts), conn_state_t, timeout_list);
(*iom)->poll_sequence_num = 0;
return APR_SUCCESS;
}
#define DEFAULT_POLL_TIMEOUT 1000000
apr_status_t io_multiplexer_get_event(io_multiplexer *iom,
apr_pollfd_t *event)
{
apr_status_t rv;
rv = apr_thread_mutex_lock(iom->pollset_lock);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_thread_mutex_lock(iom->lock);
if (rv != APR_SUCCESS) {
apr_thread_mutex_unlock(iom->pollset_lock);
return rv;
}
if (iom->stopped) {
apr_thread_mutex_unlock(iom->lock);
apr_thread_mutex_unlock(iom->pollset_lock);
return APR_EINVAL;
}
for (;;) {
/* Invariant: at the start of each iteration of this loop, the
* active thread holds iom->lock.
*/
if (!APR_RING_EMPTY(&(iom->expired_timeouts), conn_state_t, timeout_list)) {
/* There are some timeout notifications remaining
* from the last poll. Return the next one.
*/
conn_state_t *cs = APR_RING_FIRST(&(iom->expired_timeouts));
APR_RING_REMOVE(cs, timeout_list);
*event = cs->pfd;
event->rtnevents |= IOM_POLL_TIMEOUT;
apr_thread_mutex_unlock(iom->lock);
return apr_thread_mutex_unlock(iom->pollset_lock);
}
else if (iom->num_pending_events > 0) {
/* There are some events remaining from the last
* poll. Return the next one.
*/
*event = *(iom->next_pending_event++);
apr_pollset_remove(iom->pollset, event);
iom->num_pending_events--;
apr_thread_mutex_unlock(iom->lock);
return apr_thread_mutex_unlock(iom->pollset_lock);
}
else {
/* No unprocessed events remain from the previous poll,
* so initiate a new poll.
*/
apr_int32_t num_pending_events = 0;
const apr_pollfd_t *next_pending_event;
apr_interval_time_t poll_timeout;
int i;
if (APR_RING_EMPTY(&(iom->pending_timeouts), conn_state_t,
timeout_list)) {
poll_timeout = DEFAULT_POLL_TIMEOUT;
}
else {
/* If there are pending timeouts, check whether
* any of them have expired. If none have expired,
* use the expiration time on the first one to
* determine how long the poll should block.
*/
apr_time_t now = apr_time_now();
conn_state_t *cs = APR_RING_FIRST(&(iom->pending_timeouts));
if (cs->expiration_time <= now) {
do {
APR_RING_REMOVE(cs, timeout_list);
apr_pollset_remove(iom->pollset, &(cs->pfd));
APR_RING_INSERT_TAIL(&(iom->expired_timeouts), cs,
conn_state_t, timeout_list);
if (APR_RING_EMPTY(&(iom->pending_timeouts),
conn_state_t, timeout_list)) {
break;
}
cs = APR_RING_FIRST(&(iom->pending_timeouts));
} while (cs->expiration_time <= now);
continue;
}
else {
poll_timeout = cs->expiration_time - now;
}
}
apr_thread_mutex_unlock(iom->lock);
rv = apr_pollset_poll(iom->pollset, poll_timeout,
&num_pending_events, &next_pending_event);
if ((rv != APR_SUCCESS) && !APR_STATUS_IS_TIMEUP(rv) && !APR_STATUS_IS_EINTR(rv)) {
apr_thread_mutex_unlock(iom->pollset_lock);
return rv;
}
apr_thread_mutex_lock(iom->lock);
if (num_pending_events > 0) {
iom->num_pending_events = num_pending_events;
iom->next_pending_event = next_pending_event;
for (i = 0; i < num_pending_events; i++) {
multiplexable *m = (multiplexable *)next_pending_event[i].client_data;
if (m != NULL) {
io_multiplexer_remove_internal(iom, m);
}
}
}
}
}
}
apr_status_t io_multiplexer_stop(io_multiplexer *iom, int graceful) {
iom->stopped = 1;
return APR_SUCCESS;
}
apr_status_t io_multiplexer_add(io_multiplexer *iom, multiplexable *m,
long timeout_in_usec)
{
apr_status_t rv;
apr_thread_mutex_lock(iom->lock);
if (iom->stopped) {
rv = APR_EINVAL;
}
else if (m->type == IOM_CONNECTION) {
APR_RING_REMOVE(m->c->cs, timeout_list);
m->c->cs->pfd.client_data = m;
rv = apr_pollset_add(iom->pollset, &(m->c->cs->pfd));
if (timeout_in_usec >= 0) {
/* XXX: Keep the pending_timeouts list sorted */
m->c->cs->expiration_time = apr_time_now() + timeout_in_usec;
APR_RING_INSERT_TAIL(&(iom->pending_timeouts), m->c->cs,
conn_state_t, timeout_list);
}
}
else if (m->type == IOM_LISTENER) {
apr_pollfd_t desc;
desc.desc_type = APR_POLL_SOCKET;
desc.desc.s = m->l->sd;
desc.reqevents = APR_POLLIN;
desc.client_data = m;
rv = apr_pollset_add(iom->pollset, &desc);
}
else {
rv = APR_EINVALSOCK;
}
apr_thread_mutex_unlock(iom->lock);
return rv;
}
apr_status_t io_multiplexer_remove(io_multiplexer *iom, multiplexable *m)
{
apr_status_t rv;
apr_thread_mutex_lock(iom->lock);
rv = io_multiplexer_remove_internal(iom, m);
apr_thread_mutex_unlock(iom->lock);
return rv;
}
static apr_status_t io_multiplexer_remove_internal(io_multiplexer *iom,
multiplexable *m)
{
apr_status_t rv;
if (iom->stopped) {
rv = APR_EINVAL;
}
else if (m->type == IOM_CONNECTION) {
APR_RING_REMOVE(m->c->cs, timeout_list);
rv = apr_pollset_remove(iom->pollset, &(m->c->cs->pfd));
}
else if (m->type == IOM_LISTENER) {
apr_pollfd_t desc;
desc.desc_type = APR_POLL_SOCKET;
desc.desc.s = m->l->sd;
desc.reqevents = APR_POLLIN;
desc.client_data = NULL;
rv = apr_pollset_remove(iom->pollset, &desc);
}
else {
rv = APR_EINVALSOCK;
}
return rv;
}