Commit 5fcf870c authored by Paul Querna's avatar Paul Querna
Browse files

The Event MPM.

Designed to minimize Apache's KeepAlive overhead.

This MPM depends on the current APR-trunk for new features added to 
the apr_pollset interface. Currently the underlying operating
system must support KQueue or EPoll.

Status:
  Should work as a drop in replacement for all non-ssl servers.
  SSL Requests that use HTTP 1.1 Pipelining do not currently work.

Testing:
  I have tested it with Linux 2.6, FreeBSD 5.2.1, and OS X 10.3.
  
Originally based on the patch by Greg Ames.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@105919 13f79535-47bb-0310-9956-ffa450edef68
parent 8aceeea0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -139,6 +139,7 @@ AP_DECLARE(apr_status_t) ap_os_create_privileged_process(
#define AP_MPMQ_MAX_REQUESTS_DAEMON  11  /* Max # of requests per daemon */
#define AP_MPMQ_MAX_DAEMONS          12  /* Max # of daemons by config   */
#define AP_MPMQ_MPM_STATE            13  /* starting, running, stopping  */
#define AP_MPMQ_IS_ASYNC             14  /* MPM can process async connections  */

/**
 * Query a property of the current MPM.  
+23 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include "apr_time.h"
#include "apr_network_io.h"
#include "apr_buckets.h"
#include "apr_poll.h"

#include "os.h"

@@ -675,6 +676,8 @@ typedef struct server_rec server_rec;
typedef struct conn_rec conn_rec;
/** A structure that represents the current request */
typedef struct request_rec request_rec;
/** A structure that represents the status of the current connection */
typedef struct conn_state_t conn_state_t;

/* ### would be nice to not include this from httpd.h ... */
/* This comes after we have defined the request_rec type */
@@ -1011,6 +1014,26 @@ struct conn_rec {
    void *sbh;
    /** The bucket allocator to use for all bucket/brigade creations */
    struct apr_bucket_alloc_t *bucket_alloc;
    /** The current state of this connection */
    conn_state_t *cs;
    /** Is there data pending in the input filters? */ 
    int data_in_input_filters;
};

typedef enum  {
    CONN_STATE_CHECK_REQUEST_LINE_READABLE,
    CONN_STATE_READ_REQUEST_LINE,
    CONN_STATE_LINGER,
} conn_state_e;

struct conn_state_t {
    APR_RING_ENTRY(conn_state_t) timeout_list; 
    apr_time_t expiration_time; 
    conn_state_e state;
    conn_rec *c;
    apr_pool_t *p;
    apr_bucket_alloc_t *bucket_alloc;
    apr_pollfd_t pfd;
};

/* Per-vhost config... */
+60 −0
Original line number Diff line number Diff line
@@ -231,6 +231,53 @@ static const char *http_method(const request_rec *r)
static apr_port_t http_port(const request_rec *r)
    { return DEFAULT_HTTP_PORT; }

static int ap_process_http_async_connection(conn_rec *c)
{
    request_rec *r;
    conn_state_t *cs = c->cs;

    switch (cs->state) {
    case CONN_STATE_READ_REQUEST_LINE:
        ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
        while ((r = ap_read_request(c)) != NULL) {
            c->keepalive = AP_CONN_UNKNOWN;

            /* process the request if it was read without error */
            ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);

            if (r->status == HTTP_OK)
                ap_process_request(r);

            if (ap_extended_status)
                ap_increment_counts(c->sbh, r);

            if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted) {
                cs->state = CONN_STATE_LINGER;
                break;
            }
            else {
                cs->state = CONN_STATE_CHECK_REQUEST_LINE_READABLE;
            }

            apr_pool_destroy(r->pool);

            if (ap_graceful_stop_signalled())
                break;

            if (c->data_in_input_filters) {
                continue;
            }
            break;
        }
	break;
    default:
        AP_DEBUG_ASSERT(0);
        cs->state = CONN_STATE_LINGER;
    }

    return OK;
}

static int ap_process_http_connection(conn_rec *c)
{
    request_rec *r;
@@ -290,8 +337,21 @@ static int http_create_request(request_rec *r)

static void register_hooks(apr_pool_t *p)
{
    /**
     * If we ae using an MPM That Supports Async Connections,
     * use a different processing function
     */
    int async_mpm = 0;
    if(ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm) == APR_SUCCESS 
       && async_mpm == 1) {
        ap_hook_process_connection(ap_process_http_async_connection,NULL,
            NULL,APR_HOOK_REALLY_LAST);
    }
    else {
    ap_hook_process_connection(ap_process_http_connection,NULL,NULL,
			       APR_HOOK_REALLY_LAST);
    }

    ap_hook_map_to_storage(ap_send_http_trace,NULL,NULL,APR_HOOK_MIDDLE);
    ap_hook_http_method(http_method,NULL,NULL,APR_HOOK_REALLY_LAST);
    ap_hook_default_port(http_port,NULL,NULL,APR_HOOK_REALLY_LAST);
+10 −3
Original line number Diff line number Diff line
@@ -205,9 +205,17 @@ static void check_pipeline_flush(request_rec *r)
     */
    /* ### shouldn't this read from the connection input filters? */
    /* ### is zero correct? that means "read one line" */
    if (r->connection->keepalive == AP_CONN_CLOSE || 
        ap_get_brigade(r->input_filters, bb, AP_MODE_EATCRLF, 
    if (r->connection->keepalive != AP_CONN_CLOSE) {
        if (ap_get_brigade(r->input_filters, bb, AP_MODE_EATCRLF, 
                       APR_NONBLOCK_READ, 0) != APR_SUCCESS) {
            c->data_in_input_filters = 0;  /* we got APR_EOF or an error */
        }
        else {
            c->data_in_input_filters = 1;
            return;    /* don't flush */
        }
    }

        apr_bucket *e = apr_bucket_flush_create(c->bucket_alloc);

        /* We just send directly to the connection based filters.  At
@@ -219,7 +227,6 @@ static void check_pipeline_flush(request_rec *r)
        APR_BRIGADE_INSERT_HEAD(bb, e);
        ap_pass_brigade(r->connection->output_filters, bb);
}
}

void ap_process_request(request_rec *r)
{
+3 −3
Original line number Diff line number Diff line
AC_MSG_CHECKING(which MPM to use)
AC_ARG_WITH(mpm,
APACHE_HELP_STRING(--with-mpm=MPM,Choose the process model for Apache to use.
                          MPM={beos|worker|prefork|mpmt_os2|perchild|leader|threadpool}),[
                          MPM={beos|event|worker|prefork|mpmt_os2|perchild|leader|threadpool}),[
  APACHE_MPM=$withval
],[
  if test "x$APACHE_MPM" = "x"; then
@@ -12,7 +12,7 @@ AC_MSG_RESULT($APACHE_MPM)

apache_cv_mpm=$APACHE_MPM
	
if test "$apache_cv_mpm" = "worker" -o "$apache_cv_mpm" = "perchild" -o "$apache_cv_mpm" = "leader" -o "$apache_cv_mpm" = "threadpool" ; then
if test "$apache_cv_mpm" = "worker" -o "$apache_cv_mpm" = "event" -o "$apache_cv_mpm" = "perchild" -o "$apache_cv_mpm" = "leader" -o "$apache_cv_mpm" = "threadpool" ; then
  APR_CHECK_APR_DEFINE(APR_HAS_THREADS)

  if test "x$ac_cv_define_APR_HAS_THREADS" = "xno"; then
@@ -26,7 +26,7 @@ fi
APACHE_FAST_OUTPUT(server/mpm/Makefile)

MPM_NAME=$apache_cv_mpm
if test "$MPM_NAME" = "leader" -o "$MPM_NAME" = "threadpool" -o "$MPM_NAME" = "perchild"; then
if test "$MPM_NAME" = "event" -o "$MPM_NAME" = "leader" -o "$MPM_NAME" = "threadpool" -o "$MPM_NAME" = "perchild"; then
  AC_MSG_WARN(You have selected an EXPERIMENTAL MPM.  Be warned!)
  MPM_SUBDIR_NAME=experimental/$MPM_NAME
else
Loading