You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by pq...@apache.org on 2008/09/20 13:58:09 UTC

svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Author: pquerna
Date: Sat Sep 20 04:58:08 2008
New Revision: 697357

URL: http://svn.apache.org/viewvc?rev=697357&view=rev
Log:
Introduce Suspendable Requests to the Event MPM.

Using this basic framework, you can return SUSPENDED from an HTTP Handler,
and then register a callback that is invoked by the MPM at a later time.

This initial version only supports _timers_ as callbacks, but in the future I
would like to add things like wait for socket activity, on a socket specified by
the handler.

Once in a callback, It is then the responsibility of the callback fucntion 
to finish the HTTP Request handling, but this alows you to do cool things like 
a fully async proxy, COMET support, or even rate limiting.

To prove I'm not insane, I've inlcuded an example module, mod_dialup.

You can configure it like this:
<Location "/docs">
  ModemStandard "V.32"
</Location>

And for static files inside that path, you will be rate limited to V.32 speeds, 
aka 9.6 kilobits/second.

Does anyone besides Rüdiger read commit emails :-) ?

I know there are likely huge problems with this, but I would like to see how far
we can push the Event MPM, figure out what to do better, if there is anything, 
and then really dive into the 3.0 development before ApacheCon.

* server/mpm/experimental/event/fdqueue.h:
    (timer_event_t): New structure to hold timer events and callback functions.
    
* server/mpm/experimental/event/fdqueue.c
    (ap_queue_empty): Modify to also look at Timer Ring.

    (ap_queue_init): Initialize Timer Ring.

    (ap_queue_push_timer): New function, pushes a timer event into the queue.

    (ap_queue_pop_something): Renamed function, returns a timer event or
        a socket/pool for a worker thread to run.


* server/mpm/experimental/event/event.c
    (process_socket): If the connection is in SUSPENDED state, don't force it
        into linger mode yet, the callback will have to take care of that.

    (push_timer2worker): New shortcut function, pushes timer event into queue
        for a worker to run.

    (timer_free_ring): New global data structure to recycle memory used by 
        timer events.

    (timer_ring): New global data structure to hold active timer events.

    (g_timer_ring_mtx): Thread mutex to protect timer event data structures.

    (ap_mpm_register_timed_callback): New Function, registers a callback to be
        invoked by the MPM at a later time.

    (listener_thread): Calculate our wakeup time based on the upcoming Event 
        Queue, and after pollset_poll runs, push any Timers that have passed
        onto worker threads to run.
    
    (worker_thread): Call new queue pop method, and if the Timer Event is 
        non-null, invoke the callback.  Once the callback is done, push the
        structure onto the timer_free_ring, to be recycled.

    (child_main): Initialize new mutex and ring structures.


* server/config.c
    (ap_invoke_handler): Allow SUSPENDED aa valid return code from handlers.


* modules/http/http_core.c
    (ap_process_http_async_connection): Don't close the connection when in 
        SUSPENDED state.


* modules/http/http_request.c
    (ap_process_request_after_handler): New function, body pulled from the old,
        ap_process_async_request.  Split to let handlers invoke this so they 
        don't need to know all of the details of finishing a request.

    (ap_process_async_request): If the handler returns SUSPENDED, don't do
        anything but return.


* include/ap_mmn.h: Bump MMN.


* include/ap_mpm.h
    (ap_mpm_register_timed_callback): New function.


* include/httpd.h:
    (SUSPENDED): New return code for handlers.
    (request_rec::invoke_mtx): New mutex to protect callback invokcations
        from being run before the original handler finishes running.
    (conn_state_e): Add a suspended state.


* include/http_request.h
    (ap_process_request_after_handler): New function to make it easier for 
        handlers to finish the HTTP Request.


* modules/test/config.m4: Add mod_dialup to build.


* modules/test/mod_dialup.c: New rate limiting module, requires the Event MPM 
    to work.



Added:
    httpd/httpd/trunk/modules/test/mod_dialup.c   (with props)
Modified:
    httpd/httpd/trunk/include/ap_mmn.h
    httpd/httpd/trunk/include/ap_mpm.h
    httpd/httpd/trunk/include/http_request.h
    httpd/httpd/trunk/include/httpd.h
    httpd/httpd/trunk/modules/http/http_core.c
    httpd/httpd/trunk/modules/http/http_request.c
    httpd/httpd/trunk/modules/test/config.m4
    httpd/httpd/trunk/server/config.c
    httpd/httpd/trunk/server/mpm/experimental/event/event.c
    httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.c
    httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.h

Modified: httpd/httpd/trunk/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mmn.h (original)
+++ httpd/httpd/trunk/include/ap_mmn.h Sat Sep 20 04:58:08 2008
@@ -168,13 +168,14 @@
  * 20080722.2 (2.3.0-dev)  Add scolonsep to proxy_balancer
  * 20080829.0 (2.3.0-dev)  Add cookie attributes when removing cookies
  * 20080830.0 (2.3.0-dev)  Cookies can be set on headers_out and err_headers_out
+ * 20080920.0 (2.3.0-dev)  Add ap_mpm_register_timed_callback. 
  *
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
 
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
-#define MODULE_MAGIC_NUMBER_MAJOR 20080830
+#define MODULE_MAGIC_NUMBER_MAJOR 20080920
 #endif
 #define MODULE_MAGIC_NUMBER_MINOR 0                     /* 0...n */
 

Modified: httpd/httpd/trunk/include/ap_mpm.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mpm.h?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mpm.h (original)
+++ httpd/httpd/trunk/include/ap_mpm.h Sat Sep 20 04:58:08 2008
@@ -152,6 +152,14 @@
  */
 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result);
 
+
+typedef void (ap_mpm_callback_fn_t)(void *baton);
+
+/* XXXXXXX: only added support in the Event MPM.... */
+AP_DECLARE(void) ap_mpm_register_timed_callback(apr_time_t t,
+                                                ap_mpm_callback_fn_t *cbfn,
+                                                void *baton);
+    
 /* Defining GPROF when compiling uses the moncontrol() function to
  * disable gprof profiling in the parent, and enable it only for
  * request processing in children (or in one_process mode).  It's

Modified: httpd/httpd/trunk/include/http_request.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/http_request.h?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/include/http_request.h (original)
+++ httpd/httpd/trunk/include/http_request.h Sat Sep 20 04:58:08 2008
@@ -315,7 +315,10 @@
  */
 void ap_process_request(request_rec *);
 
-/**
+/* For post-processing after a handler has finished with a request. (Commonly used after it was suspended) */
+void ap_process_request_after_handler(request_rec *r);
+
+    /**
  * Process a top-level request from a client, allowing some or all of
  * the response to remain buffered in the core output filter for later,
  * asynchronous write completion

Modified: httpd/httpd/trunk/include/httpd.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/httpd.h?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/include/httpd.h (original)
+++ httpd/httpd/trunk/include/httpd.h Sat Sep 20 04:58:08 2008
@@ -457,6 +457,8 @@
 #define DONE -2			/**< Module has served the response completely 
 				 *  - it's safe to die() with no more output
 				 */
+#define SUSPENDED -3 /**< Module will handle the remainder of the request. 
+                      * The core will never invoke the request again, */
 #define OK 0			/**< Module has handled this stage. */
 
 
@@ -989,6 +991,8 @@
     /** The optional kept body of the request. */
     apr_bucket_brigade *kept_body;
 
+    apr_thread_mutex_t *invoke_mtx;
+
 /* Things placed at the end of the record to avoid breaking binary
  * compatibility.  It would be nice to remember to reorder the entire
  * record to improve 64bit alignment the next time we need to break
@@ -1105,6 +1109,7 @@
     CONN_STATE_READ_REQUEST_LINE,
     CONN_STATE_HANDLER,
     CONN_STATE_WRITE_COMPLETION,
+    CONN_STATE_SUSPENDED,
     CONN_STATE_LINGER
 } conn_state_e;
 

Modified: httpd/httpd/trunk/modules/http/http_core.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http/http_core.c?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http/http_core.c (original)
+++ httpd/httpd/trunk/modules/http/http_core.c Sat Sep 20 04:58:08 2008
@@ -154,7 +154,8 @@
                 r = NULL;
             }
 
-            if (cs->state != CONN_STATE_WRITE_COMPLETION) {
+            if (cs->state != CONN_STATE_WRITE_COMPLETION && 
+                cs->state != CONN_STATE_SUSPENDED) {
                 /* Something went wrong; close the connection */
                 cs->state = CONN_STATE_LINGER;
             }

Modified: httpd/httpd/trunk/modules/http/http_request.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http/http_request.c?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http/http_request.c (original)
+++ httpd/httpd/trunk/modules/http/http_request.c Sat Sep 20 04:58:08 2008
@@ -213,13 +213,39 @@
 }
 
 
-void ap_process_async_request(request_rec *r)
+void ap_process_request_after_handler(request_rec *r)
 {
-    int access_status;
     apr_bucket_brigade *bb;
     apr_bucket *b;
     conn_rec *c = r->connection;
 
+    /* Send an EOR bucket through the output filter chain.  When
+     * this bucket is destroyed, the request will be logged and
+     * its pool will be freed
+     */
+    bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);
+    b = ap_bucket_eor_create(r->connection->bucket_alloc, r);
+    APR_BRIGADE_INSERT_HEAD(bb, b);
+    
+    ap_pass_brigade(r->connection->output_filters, bb);
+    
+    /* From here onward, it is no longer safe to reference r
+     * or r->pool, because r->pool may have been destroyed
+     * already by the EOR bucket's cleanup function.
+     */
+    
+    c->cs->state = CONN_STATE_WRITE_COMPLETION;
+    check_pipeline(c);
+    if (ap_extended_status) {
+        ap_time_process_request(c->sbh, STOP_PREQUEST);
+    }
+}
+
+void ap_process_async_request(request_rec *r)
+{
+    conn_rec *c = r->connection;
+    int access_status;
+
     /* Give quick handlers a shot at serving the request on the fast
      * path, bypassing all of the other Apache hooks.
      *
@@ -234,8 +260,12 @@
      * Use this hook with extreme care and only if you know what you are
      * doing.
      */
-    if (ap_extended_status)
+    if (ap_extended_status) {
         ap_time_process_request(r->connection->sbh, START_PREQUEST);
+    }
+
+    apr_thread_mutex_create(&r->invoke_mtx, APR_THREAD_MUTEX_DEFAULT, r->pool);
+    apr_thread_mutex_lock(r->invoke_mtx);
     access_status = ap_run_quick_handler(r, 0);  /* Not a look-up request */
     if (access_status == DECLINED) {
         access_status = ap_process_request_internal(r);
@@ -244,6 +274,16 @@
         }
     }
 
+    if (access_status == SUSPENDED) {
+        if (ap_extended_status) {
+            ap_time_process_request(c->sbh, STOP_PREQUEST);
+        }
+        c->cs->state = CONN_STATE_SUSPENDED;
+        apr_thread_mutex_unlock(r->invoke_mtx);
+        return;
+    }
+    apr_thread_mutex_unlock(r->invoke_mtx);
+
     if (access_status == DONE) {
         /* e.g., something not in storage like TRACE */
         access_status = OK;
@@ -257,24 +297,7 @@
         ap_die(access_status, r);
     }
 
-    /* Send an EOR bucket through the output filter chain.  When
-     * this bucket is destroyed, the request will be logged and
-     * its pool will be freed
-     */
-    bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);
-    b = ap_bucket_eor_create(r->connection->bucket_alloc, r);
-    APR_BRIGADE_INSERT_HEAD(bb, b);
-    ap_pass_brigade(r->connection->output_filters, bb);
-
-    /* From here onward, it is no longer safe to reference r
-     * or r->pool, because r->pool may have been destroyed
-     * already by the EOR bucket's cleanup function.
-     */
-
-    c->cs->state = CONN_STATE_WRITE_COMPLETION;
-    check_pipeline(c);
-    if (ap_extended_status)
-        ap_time_process_request(c->sbh, STOP_PREQUEST);
+    return ap_process_request_after_handler(r);
 }
 
 void ap_process_request(request_rec *r)

Modified: httpd/httpd/trunk/modules/test/config.m4
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/test/config.m4?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/test/config.m4 (original)
+++ httpd/httpd/trunk/modules/test/config.m4 Sat Sep 20 04:58:08 2008
@@ -6,4 +6,6 @@
 APACHE_MODULE(optional_fn_import, example optional function importer, , , no)
 APACHE_MODULE(optional_fn_export, example optional function exporter, , , no)
 
+APACHE_MODULE(dialup, rate limits static files to dialup modem speeds, , , no)
+
 APACHE_MODPATH_FINISH

Added: httpd/httpd/trunk/modules/test/mod_dialup.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/test/mod_dialup.c?rev=697357&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/test/mod_dialup.c (added)
+++ httpd/httpd/trunk/modules/test/mod_dialup.c Sat Sep 20 04:58:08 2008
@@ -0,0 +1,308 @@
+/* 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 "httpd.h"
+#include "util_filter.h"
+#include "http_log.h"
+#include "http_config.h"
+#include "http_request.h"
+
+/* to detect sendfile enabled, we need CORE_PRIVATE. Someone should fix this. */
+#define CORE_PRIVATE
+#include "http_core.h"
+
+
+module AP_MODULE_DECLARE_DATA dialup_module;
+
+#ifndef apr_time_from_msec
+#define apr_time_from_msec(x) (x * 1000)
+#endif
+
+
+typedef struct dialup_dcfg_t {
+    apr_size_t bytes_per_second;
+} dialup_dcfg_t;
+
+typedef struct dialup_baton_t {
+    apr_size_t bytes_per_second;
+    request_rec *r;
+    apr_file_t *fd;
+    apr_bucket_brigade *bb;
+    apr_bucket_brigade *tmpbb;
+} dialup_baton_t;
+
+static int
+dialup_send_pulse(dialup_baton_t *db)
+{
+    int status;
+    apr_off_t len = 0;
+    apr_size_t bytes_sent = 0;
+    
+    while (!APR_BRIGADE_EMPTY(db->bb) && bytes_sent < db->bytes_per_second) {
+        apr_bucket *e;
+
+        if (db->r->connection->aborted) {
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        
+        status = apr_brigade_partition(db->bb, db->bytes_per_second, &e);
+
+        if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
+            /* XXXXXX: Log me. */
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        if (e != APR_BRIGADE_SENTINEL(db->bb)) {
+            apr_bucket *f;
+            apr_bucket *b = APR_BUCKET_PREV(e);
+            f = APR_RING_FIRST(&db->bb->list);
+            APR_RING_UNSPLICE(f, b, link);
+            APR_RING_SPLICE_HEAD(&db->tmpbb->list, f, b, apr_bucket, link);
+        }
+        else {
+            APR_BRIGADE_CONCAT(db->tmpbb, db->bb);
+        }
+        
+        e = apr_bucket_flush_create(db->r->connection->bucket_alloc);
+        
+        APR_BRIGADE_INSERT_TAIL(db->tmpbb, e);
+
+        apr_brigade_length(db->tmpbb, 1, &len);
+        bytes_sent += len;
+        status = ap_pass_brigade(db->r->output_filters, db->tmpbb);
+
+        apr_brigade_cleanup(db->tmpbb);
+
+        if (status != OK) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, db->r,
+                          "dialup: pulse: ap_pass_brigade failed:");
+            return status;
+        }
+    }
+
+    if (APR_BRIGADE_EMPTY(db->bb)) {
+        return DONE;
+    }
+    else {
+        return SUSPENDED;
+    }
+}
+
+void 
+dialup_callback(void *baton)
+{
+    int status;
+    dialup_baton_t *db = (dialup_baton_t *)baton;
+
+    apr_thread_mutex_lock(db->r->invoke_mtx);
+
+    status = dialup_send_pulse(db);
+
+    if (status == SUSPENDED) {
+        ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, baton);
+    }
+    else if (status == DONE) {
+        apr_thread_mutex_unlock(db->r->invoke_mtx);
+        ap_finalize_request_protocol(db->r);
+        ap_process_request_after_handler(db->r);
+        return;
+    }
+    else {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, db->r,
+                      "dialup: pulse returned: %d", status);
+        db->r->status = HTTP_OK;
+        ap_die(status, db->r);
+    }
+
+    apr_thread_mutex_unlock(db->r->invoke_mtx);
+}
+
+static int
+dialup_handler(request_rec *r)
+{
+    int status;
+    apr_status_t rv;
+
+    /* See core.c, default handler for all of the cases we just decline. */
+    if (r->method_number != M_GET || 
+        r->finfo.filetype == 0 || 
+        r->finfo.filetype == APR_DIR) {
+        return DECLINED;
+    }
+
+    dialup_dcfg_t *dcfg = ap_get_module_config(r->per_dir_config,
+                                               &dialup_module);
+    if (dcfg->bytes_per_second == 0) {
+        return DECLINED;
+    }
+    core_dir_config *ccfg = ap_get_module_config(r->per_dir_config,
+                                                     &core_module);
+    
+    apr_file_t *fd;
+
+    rv = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY
+#if APR_HAS_SENDFILE
+                           | ((ccfg->enable_sendfile == ENABLE_SENDFILE_OFF)
+                              ? 0 : APR_SENDFILE_ENABLED)
+#endif
+                       , 0, r->pool);
+
+    if (rv) {
+        return DECLINED;
+    }
+
+    /* copied from default handler: */
+    ap_update_mtime(r, r->finfo.mtime);
+    ap_set_last_modified(r);
+    ap_set_etag(r);
+    apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
+    ap_set_content_length(r, r->finfo.size);
+
+    status = ap_meets_conditions(r);
+    if (status != OK) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "dialup: declined, meets conditions, good luck core handler");
+        return DECLINED;
+    }
+
+    apr_bucket_brigade *bb;
+
+    dialup_baton_t *db = apr_palloc(r->pool, sizeof(dialup_baton_t));
+    
+    db->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    db->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+    apr_bucket *e;
+
+    e = apr_brigade_insert_file(db->bb, fd, 0, r->finfo.size, r->pool);
+
+#if APR_HAS_MMAP
+    if (ccfg->enable_mmap == ENABLE_MMAP_OFF) {
+        apr_bucket_file_enable_mmap(e, 0);
+    }
+#endif
+    
+    
+    db->bytes_per_second = dcfg->bytes_per_second;
+    db->r = r;
+    db->fd = fd;
+
+    e = apr_bucket_eos_create(r->connection->bucket_alloc);
+
+    APR_BRIGADE_INSERT_TAIL(db->bb, e);
+
+    status = dialup_send_pulse(db);
+    if (status != SUSPENDED && status != DONE) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "dialup: failed, send pulse");
+        return status;
+    }
+
+    ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, db);
+
+    return SUSPENDED;
+}
+
+
+
+#ifndef APR_HOOK_ALMOST_LAST
+#define APR_HOOK_ALMOST_LAST (APR_HOOK_REALLY_LAST - 1)
+#endif
+
+static void
+dialup_register_hooks(apr_pool_t *p)
+{
+    ap_hook_handler(dialup_handler, NULL, NULL, APR_HOOK_ALMOST_LAST);
+}
+
+typedef struct modem_speed_t {
+    const char *name;
+    apr_size_t bytes_per_second;
+} modem_speed_t;
+
+#ifndef BITRATE_TO_BYTES
+#define BITRATE_TO_BYTES(x) ((1000 * x)/8)
+#endif
+
+static const modem_speed_t modem_bitrates[] =
+{
+    {"V.21",    BITRATE_TO_BYTES(0.1)},
+    {"V.26bis", BITRATE_TO_BYTES(2.4)},
+    {"V.32",    BITRATE_TO_BYTES(9.6)},
+    {"V.34",    BITRATE_TO_BYTES(28.8)},
+    {"V.92",    BITRATE_TO_BYTES(56.0)},
+    {"i-was-rich-and-got-a-leased-line", BITRATE_TO_BYTES(1500)},
+    {NULL, 0}
+};
+
+static const char *
+cmd_modem_standard(cmd_parms *cmd,
+             void *dconf,
+             const char *input)
+{
+    const modem_speed_t *standard;
+    int i = 0;
+    dialup_dcfg_t *dcfg = (dialup_dcfg_t*)dconf;
+    
+    dcfg->bytes_per_second = 0;
+
+    while (modem_bitrates[i].name != NULL) {
+        standard = &modem_bitrates[i];
+        if (strcasecmp(standard->name, input) == 0) {
+            dcfg->bytes_per_second = standard->bytes_per_second;
+            break;
+        }
+        i++;
+    }
+
+    if (dcfg->bytes_per_second == 0) {
+        return "mod_diaulup: Unkonwn Modem Standard specified.";
+    }
+
+    return NULL;
+}
+
+static void *
+dialup_dcfg_create(apr_pool_t *p, char *dummy)
+{
+    dialup_dcfg_t *cfg = apr_palloc(p, sizeof(dialup_dcfg_t));
+    
+    cfg->bytes_per_second = 0;
+
+    return cfg;
+}
+
+
+static const command_rec dialup_cmds[] =
+{
+    AP_INIT_TAKE1("ModemStandard", cmd_modem_standard, NULL, ACCESS_CONF,
+                  "Modem Standard to.. simulate. "
+                  "Must be one of: 'V.21', 'V.26bis', 'V.32', 'V.34', or 'V.92'"),
+    NULL
+};
+
+module AP_MODULE_DECLARE_DATA dialup_module =
+{
+    STANDARD20_MODULE_STUFF,
+    dialup_dcfg_create,
+    NULL,
+    NULL,
+    NULL,
+    dialup_cmds,
+    dialup_register_hooks
+};

Propchange: httpd/httpd/trunk/modules/test/mod_dialup.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpd/httpd/trunk/modules/test/mod_dialup.c
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: httpd/httpd/trunk/modules/test/mod_dialup.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpd/httpd/trunk/server/config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/config.c?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/server/config.c (original)
+++ httpd/httpd/trunk/server/config.c Sat Sep 20 04:58:08 2008
@@ -381,7 +381,7 @@
         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
             "handler \"%s\" not found for: %s", r->handler, r->filename);
     }
-    if ((result != OK) && (result != DONE) && (result != DECLINED)
+    if ((result != OK) && (result != DONE) && (result != DECLINED) && (result != SUSPENDED)
         && !ap_is_HTTP_VALID_RESPONSE(result)) {
         /* If a module is deliberately returning something else
          * (request_rec in non-HTTP or proprietary extension?)

Modified: httpd/httpd/trunk/server/mpm/experimental/event/event.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/mpm/experimental/event/event.c?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/server/mpm/experimental/event/event.c (original)
+++ httpd/httpd/trunk/server/mpm/experimental/event/event.c Sat Sep 20 04:58:08 2008
@@ -612,7 +612,9 @@
          * like the Worker MPM does.
          */
         ap_run_process_connection(c);
-        cs->state = CONN_STATE_LINGER;
+        if (cs->state != CONN_STATE_SUSPENDED) {
+            cs->state = CONN_STATE_LINGER;
+        }
     }
 
 read_request:
@@ -796,6 +798,11 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t push_timer2worker(timer_event_t* te)
+{
+    return ap_queue_push_timer(worker_queue, te);
+}
+
 static apr_status_t push2worker(const apr_pollfd_t * pfd,
                                 apr_pollset_t * pollset)
 {
@@ -871,8 +878,70 @@
     }
 }
 
+/* XXXXXX: Convert to skiplist or other better data structure 
+ * (yes, this is VERY VERY VERY VERY BAD)
+ */
+
+/* Structures to reuse */
+static APR_RING_HEAD(timer_free_ring_t, timer_event_t) timer_free_ring;
+/* Active timers */
+static APR_RING_HEAD(timer_ring_t, timer_event_t) timer_ring;
+
+static apr_thread_mutex_t *g_timer_ring_mtx;
+
+AP_DECLARE(void) ap_mpm_register_timed_callback(apr_time_t t,
+                                                ap_mpm_callback_fn_t *cbfn,
+                                                void *baton)
+{
+    timer_event_t *ep;
+    timer_event_t *te;
+    /* oh yeah, and make locking smarter/fine grained. */
+    apr_thread_mutex_lock(g_timer_ring_mtx);
+
+    if (!APR_RING_EMPTY(&timer_free_ring, timer_event_t, link)) {
+        te = APR_RING_FIRST(&timer_free_ring);
+        APR_RING_REMOVE(te, link);
+    }
+    else {
+        /* XXXXX: lol, pool allocation without a context from any thread.Yeah. Right. MPMs Suck. */
+        te = malloc(sizeof(timer_event_t));
+        APR_RING_ELEM_INIT(te, link);
+    }
+
+    te->cbfunc = cbfn;
+    te->baton = baton;
+    /* XXXXX: optimize */
+    te->when = t + apr_time_now();
+
+    /* Okay, insert sorted by when.. */
+    int inserted = 0;
+    for (ep = APR_RING_FIRST(&timer_ring);
+         ep != APR_RING_SENTINEL(&timer_ring,
+                                 timer_event_t, link);
+         ep = APR_RING_NEXT(ep, link))
+    {
+        if (ep->when > te->when) {
+            inserted = 1;
+            APR_RING_INSERT_BEFORE(ep, te, link);
+            break;
+        }
+    }
+    
+    if (!inserted) {
+        APR_RING_INSERT_TAIL(&timer_ring, te, timer_event_t, link);
+    }
+
+    apr_thread_mutex_unlock(g_timer_ring_mtx);
+}
+
+#ifndef apr_time_from_msec
+#define apr_time_from_msec(x) (x * 1000)
+#endif
+
 static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
 {
+    timer_event_t *ep;
+    timer_event_t *te;
     apr_status_t rc;
     proc_info *ti = dummy;
     int process_slot = ti->pid;
@@ -891,20 +960,13 @@
 
     free(ti);
 
-    /* We set this to force apr_pollset to wakeup if there hasn't been any IO
-     * on any of its sockets.  This allows sockets to have been added
-     * when no other keepalive operations where going on.
-     *
-     * current value is 1 second
-     */
-    timeout_interval = 1000000;
-
     /* the following times out events that are really close in the future
      *   to prevent extra poll calls
      *
      * current value is .1 second
      */
 #define TIMEOUT_FUDGE_FACTOR 100000
+#define EVENT_FUDGE_FACTOR 10000
 
     rc = init_pollset(tpool);
     if (rc != APR_SUCCESS) {
@@ -927,6 +989,26 @@
             check_infinite_requests();
         }
 
+
+        {
+            apr_time_t now = apr_time_now();
+            apr_thread_mutex_lock(g_timer_ring_mtx);
+
+            if (!APR_RING_EMPTY(&timer_ring, timer_event_t, link)) {
+                te = APR_RING_FIRST(&timer_ring);
+                if (te->when > now) {
+                    timeout_interval = te->when - now;
+                }
+                else {
+                    timeout_interval = 1;
+                }
+            }
+            else {
+                timeout_interval = apr_time_from_msec(100);
+            }
+            apr_thread_mutex_unlock(g_timer_ring_mtx);
+        }
+
         rc = apr_pollset_poll(event_pollset, timeout_interval, &num,
                               &out_pfd);
 
@@ -945,6 +1027,25 @@
         if (listener_may_exit)
             break;
 
+        {
+            apr_time_t now = apr_time_now();
+            apr_thread_mutex_lock(g_timer_ring_mtx);
+            for (ep = APR_RING_FIRST(&timer_ring);
+                 ep != APR_RING_SENTINEL(&timer_ring,
+                                         timer_event_t, link);
+                 ep = APR_RING_FIRST(&timer_ring))
+            {
+                if (ep->when < now + EVENT_FUDGE_FACTOR) {
+                    APR_RING_REMOVE(ep, link);
+                    push_timer2worker(ep);
+                }
+                else {
+                    break;
+                }
+            }
+            apr_thread_mutex_unlock(g_timer_ring_mtx);
+        }
+
         while (num && get_worker(&have_idle_worker)) {
             pt = (listener_poll_type *) out_pfd->client_data;
             if (pt->type == PT_CSD) {
@@ -1143,7 +1244,8 @@
     apr_pool_t *ptrans;         /* Pool for per-transaction stuff */
     apr_status_t rv;
     int is_idle = 0;
-
+    timer_event_t *te = NULL;
+    
     free(ti);
 
     ap_scoreboard_image->servers[process_slot][thread_slot].pid = ap_my_pid;
@@ -1171,7 +1273,10 @@
         if (workers_may_exit) {
             break;
         }
-        rv = ap_queue_pop(worker_queue, &csd, &cs, &ptrans);
+
+        te = NULL;
+        
+        rv = ap_queue_pop_something(worker_queue, &csd, &cs, &ptrans, &te);
 
         if (rv != APR_SUCCESS) {
             /* We get APR_EOF during a graceful shutdown once all the
@@ -1201,13 +1306,25 @@
             }
             continue;
         }
-        is_idle = 0;
-        worker_sockets[thread_slot] = csd;
-        rv = process_socket(ptrans, csd, cs, process_slot, thread_slot);
-        if (!rv) {
-            requests_this_child--;
+        if (te != NULL) {
+            
+            te->cbfunc(te->baton);
+
+            {
+                apr_thread_mutex_lock(g_timer_ring_mtx);
+                APR_RING_INSERT_TAIL(&timer_free_ring, te, timer_event_t, link);
+                apr_thread_mutex_unlock(g_timer_ring_mtx);
+            }
+        }
+        else {
+            is_idle = 0;
+            worker_sockets[thread_slot] = csd;
+            rv = process_socket(ptrans, csd, cs, process_slot, thread_slot);
+            if (!rv) {
+                requests_this_child--;
+            }
+            worker_sockets[thread_slot] = NULL;
         }
-        worker_sockets[thread_slot] = NULL;
     }
 
     ap_update_child_status_from_indexes(process_slot, thread_slot,
@@ -1462,6 +1579,10 @@
         clean_child_exit(APEXIT_CHILDFATAL);
     }
 
+    apr_thread_mutex_create(&g_timer_ring_mtx, APR_THREAD_MUTEX_DEFAULT, pchild);
+    APR_RING_INIT(&timer_free_ring, timer_event_t, link);
+    APR_RING_INIT(&timer_ring, timer_event_t, link);
+    
     ap_run_child_init(pchild, ap_server_conf);
 
     /* done with init critical section */

Modified: httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.c?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.c (original)
+++ httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.c Sat Sep 20 04:58:08 2008
@@ -268,7 +268,7 @@
  * Detects when the fd_queue_t is empty. This utility function is expected
  * to be called from within critical sections, and is not threadsafe.
  */
-#define ap_queue_empty(queue) ((queue)->nelts == 0)
+#define ap_queue_empty(queue) ((queue)->nelts == 0 && APR_RING_EMPTY(&queue->timers ,timer_event_t, link))
 
 /**
  * Callback routine that is called to destroy this
@@ -305,6 +305,8 @@
         return rv;
     }
 
+    APR_RING_INIT(&queue->timers, timer_event_t, link);
+
     queue->data = apr_palloc(a, queue_capacity * sizeof(fd_queue_elem_t));
     queue->bounds = queue_capacity;
     queue->nelts = 0;
@@ -353,14 +355,36 @@
     return APR_SUCCESS;
 }
 
+apr_status_t ap_queue_push_timer(fd_queue_t * queue, timer_event_t *te)
+{
+    apr_status_t rv;
+    
+    if ((rv = apr_thread_mutex_lock(queue->one_big_mutex)) != APR_SUCCESS) {
+        return rv;
+    }
+    
+    AP_DEBUG_ASSERT(!queue->terminated);
+
+    APR_RING_INSERT_TAIL(&queue->timers, te, timer_event_t, link);
+
+    apr_thread_cond_signal(queue->not_empty);
+    
+    if ((rv = apr_thread_mutex_unlock(queue->one_big_mutex)) != APR_SUCCESS) {
+        return rv;
+    }
+    
+    return APR_SUCCESS;
+}
+
 /**
  * Retrieves the next available socket from the queue. If there are no
  * sockets available, it will block until one becomes available.
  * Once retrieved, the socket is placed into the address specified by
  * 'sd'.
  */
-apr_status_t ap_queue_pop(fd_queue_t * queue, apr_socket_t ** sd,
-                          conn_state_t ** cs, apr_pool_t ** p)
+apr_status_t ap_queue_pop_something(fd_queue_t * queue, apr_socket_t ** sd,
+                                    conn_state_t ** cs, apr_pool_t ** p,
+                                    timer_event_t ** te_out)
 {
     fd_queue_elem_t *elem;
     apr_status_t rv;
@@ -389,15 +413,23 @@
         }
     }
 
-    elem = &queue->data[--queue->nelts];
-    *sd = elem->sd;
-    *cs = elem->cs;
-    *p = elem->p;
+    *te_out = NULL;
+
+    if (!APR_RING_EMPTY(&queue->timers, timer_event_t, link)) {
+        *te_out = APR_RING_FIRST(&queue->timers);
+        APR_RING_REMOVE(*te_out, link);
+    }
+    else {
+        elem = &queue->data[--queue->nelts];
+        *sd = elem->sd;
+        *cs = elem->cs;
+        *p = elem->p;
 #ifdef AP_DEBUG
-    elem->sd = NULL;
-    elem->p = NULL;
+        elem->sd = NULL;
+        elem->p = NULL;
 #endif /* AP_DEBUG */
-
+    }
+    
     rv = apr_thread_mutex_unlock(queue->one_big_mutex);
     return rv;
 }

Modified: httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.h?rev=697357&r1=697356&r2=697357&view=diff
==============================================================================
--- httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.h (original)
+++ httpd/httpd/trunk/server/mpm/experimental/event/fdqueue.h Sat Sep 20 04:58:08 2008
@@ -37,6 +37,8 @@
 #endif
 #include <apr_errno.h>
 
+#include "ap_mpm.h"
+
 typedef struct fd_queue_info_t fd_queue_info_t;
 
 apr_status_t ap_queue_info_create(fd_queue_info_t ** queue_info,
@@ -54,8 +56,19 @@
 };
 typedef struct fd_queue_elem_t fd_queue_elem_t;
 
+typedef struct timer_event_t timer_event_t;
+
+struct timer_event_t {
+    APR_RING_ENTRY(timer_event_t) link;
+    apr_time_t when;
+    ap_mpm_callback_fn_t *cbfunc;
+    void *baton;
+};
+
+
 struct fd_queue_t
 {
+    APR_RING_HEAD(timers_t, timer_event_t) timers;
     fd_queue_elem_t *data;
     int nelts;
     int bounds;
@@ -73,8 +86,10 @@
                            apr_pool_t * a);
 apr_status_t ap_queue_push(fd_queue_t * queue, apr_socket_t * sd,
                            conn_state_t * cs, apr_pool_t * p);
-apr_status_t ap_queue_pop(fd_queue_t * queue, apr_socket_t ** sd,
-                          conn_state_t ** cs, apr_pool_t ** p);
+apr_status_t ap_queue_push_timer(fd_queue_t *queue, timer_event_t *te);
+apr_status_t ap_queue_pop_something(fd_queue_t * queue, apr_socket_t ** sd,
+                                    conn_state_t ** cs, apr_pool_t ** p,
+                                    timer_event_t ** te);
 apr_status_t ap_queue_interrupt_all(fd_queue_t * queue);
 apr_status_t ap_queue_term(fd_queue_t * queue);
 



Re: Future direction of MPMs, was Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Jim Jagielski <ji...@jaguNET.com>.
On Sep 22, 2008, at 11:51 AM, Paul Querna wrote:
>
> No, in pure requests/second, there will not be a significant  
> difference.
>
> Today, a properly tuned apache httpd, with enough RAM, can keep up  
> with the 'fastest' web servers of the day, like lighttpd.  Most of  
> the benchmarks where we do badly, is when apache httpd is mis- 
> configured, or running on extremely low RAM resources.
>
> I think what we solve, is that with a slightly more async core and  
> MPM structure, we can signfigantly reduce the memory required to  
> service several thousand long lived connections.
>

Agreed. We're not talking, imo, about increasing performance. We're
talking increasing efficiency.

> Moving forward with a hybrid that lets you pull in async abilities  
> when needed seems reasonable to me.....
>

++1...


Re: Future direction of MPMs, was Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Paul Querna <ch...@force-elite.com>.
Akins, Brian wrote:
> On 9/21/08 2:17 AM, "Bing Swen" <bs...@pku.edu.cn> wrote:
> 
>> But an optimal
>> network i/o model needs a layer that maps a *request* to a thread, so that a
>> worker thread (or process) will not have to be tied up entirely with a
>> single connection during the whole life time of the connection. Instead, a
>> worker can be scheduled to handle different connections, which helps both
>> reducing the number of workers and the performance of request handling
>> (especially on slow connections).
> 
> I still want to see this backed up with real world experience.  I know I
> keep repeating myself, but in the real world, we have never seen the
> supposed inherent performance problems in the worker model (1 connection = 1
> thread).

At $work, we just upgraded RAM in what is essentially web server 
machines, just because we are running worker MPM and expect lots of long 
lived connections.  It has a cost, and it isn't free.

> It sounds great to theorize about the wonders of a completely event driven
> or asynchronous model. However, it seems that this only nets real measurable
> performance gains is very simplistic benchmarks.


What I view happening in the event MPM today, and where I would like to 
go in 2.4, isn't a fully 'asynchronous model'.

It is much more of a hybrid, using threads (and processes) when running 
most code, but allowing requests to be moved to an event queue, waiting 
for IO, or a timer.

> I'm all for making httpd faster, scale better, etc.  I just don't want to be
> extremely disappointed if we rewrite it all and gain nothing but a more
> complicated model.  If we get great gains, wonderful, but I'd like to see
> some actually numbers before we all decided to rework the core.

No, in pure requests/second, there will not be a significant difference.

Today, a properly tuned apache httpd, with enough RAM, can keep up with 
the 'fastest' web servers of the day, like lighttpd.  Most of the 
benchmarks where we do badly, is when apache httpd is mis-configured, or 
running on extremely low RAM resources.

I think what we solve, is that with a slightly more async core and MPM 
structure, we can signfigantly reduce the memory required to service 
several thousand long lived connections.

Moving forward with a hybrid that lets you pull in async abilities when 
needed seems reasonable to me.....

-Paul




Re: Future direction of MPMs, was Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Issac Goldstand <ma...@beamartyr.net>.


Akins, Brian wrote:
> On 9/21/08 2:17 AM, "Bing Swen" <bs...@pku.edu.cn> wrote:
> 
>> But an optimal
>> network i/o model needs a layer that maps a *request* to a thread, so that a
>> worker thread (or process) will not have to be tied up entirely with a
>> single connection during the whole life time of the connection. Instead, a
>> worker can be scheduled to handle different connections, which helps both
>> reducing the number of workers and the performance of request handling
>> (especially on slow connections).
> 
> I still want to see this backed up with real world experience.  I know I
> keep repeating myself, but in the real world, we have never seen the
> supposed inherent performance problems in the worker model (1 connection = 1
> thread).
> 
> It sounds great to theorize about the wonders of a completely event driven
> or asynchronous model. However, it seems that this only nets real measurable
> performance gains is very simplistic benchmarks.
> 
> I'm all for making httpd faster, scale better, etc.  I just don't want to be
> extremely disappointed if we rewrite it all and gain nothing but a more
> complicated model.  If we get great gains, wonderful, but I'd like to see
> some actually numbers before we all decided to rework the core.
> 

Devil's advocate or not, the point is valid IMO.  We can (and likely
will) have loads of fun reworking everything, but I'm +1 with Brian here.

  Issac

Re: Future direction of MPMs, was Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Graham Leggett <mi...@sharp.fm>.
Akins, Brian wrote:

> I'm all for making httpd faster, scale better, etc.  I just don't want to be
> extremely disappointed if we rewrite it all and gain nothing but a more
> complicated model.  If we get great gains, wonderful, but I'd like to see
> some actually numbers before we all decided to rework the core.

I think the risk of being extremely disappointed is a real risk, but I 
don't think it is a reason not to give it a try.

Perhaps a suitable compromise is to say this:

- Some people want to try to come up with a purely event driven model 
that requires changing the MPM interface as necessary. Who knows, it 
might give performance gains too!

- Some people want to keep an MPM that implements the worker model, 
because we know it works to an acceptable level.

If we can achieve both at once, that will be ideal.

> (Disclaimer: yes, I'm partially playing devil's advocate here.)

Wearing the hat of someone who likes to try out new stuff, from time to 
time you hit a dead end within the design of the server that makes it 
either hard to or impossible to achieve something new.

A shake up of the core is likely to remove some of these barriers, which 
in turn means that avenues open up that up till now have been dead ends, 
which makes the development interesting again.

I think the second-from-worst case scenario is that Paul and others end 
up exploring some cool ideas and they don't work, and then the fun was 
in the exploring of the new ideas, so nothing is really lost. The best 
case scenario is obviously that some cool ideas are explored and they do 
work. The worst case scenario is that people do nothing.

Regards,
Graham
--


Re: Future direction of MPMs, was Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by "Akins, Brian" <Br...@turner.com>.
On 9/21/08 2:17 AM, "Bing Swen" <bs...@pku.edu.cn> wrote:

> But an optimal
> network i/o model needs a layer that maps a *request* to a thread, so that a
> worker thread (or process) will not have to be tied up entirely with a
> single connection during the whole life time of the connection. Instead, a
> worker can be scheduled to handle different connections, which helps both
> reducing the number of workers and the performance of request handling
> (especially on slow connections).

I still want to see this backed up with real world experience.  I know I
keep repeating myself, but in the real world, we have never seen the
supposed inherent performance problems in the worker model (1 connection = 1
thread).

It sounds great to theorize about the wonders of a completely event driven
or asynchronous model. However, it seems that this only nets real measurable
performance gains is very simplistic benchmarks.

I'm all for making httpd faster, scale better, etc.  I just don't want to be
extremely disappointed if we rewrite it all and gain nothing but a more
complicated model.  If we get great gains, wonderful, but I'd like to see
some actually numbers before we all decided to rework the core.


(Disclaimer: yes, I'm partially playing devil's advocate here.)


-- 
Brian Akins
Chief Operations Engineer
Turner Digital Media Technologies


Re: Future direction of MPMs, was Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Bing Swen <bs...@pku.edu.cn>.
"Paul Querna" <ch...@force-elite.com> wrote on  2008-9-21 4:21

> Graham Leggett wrote:
>>> I know there are likely huge problems with this, but I would like to see 
>>> how far
>>> we can push the Event MPM, figure out what to do better, if there is 
>>> anything, and then really dive into the 3.0 development before 
>>> ApacheCon.
>>
>> How difficult would this be to support in the other MPMs?
>
> Windows, Worker MPM and the similar threaded MPMs could do it easily.
>
> But, IMO, I want to eliminate all of the MPMs for 2.4/3.0.
>
> I believe the MPMs as they are designed right now, are both a layer of 
> portability, and a module that defines behavior or the model.
>
   ...
>
> Basically, one MPM to rule them all, with a configuration directive that 
> can make it act like prefork or the event MPMs.
>

Currently the role that MPMs play is to map a connection (with many 
requests, by HTTP/1.1) to a worker (a thread or process). But an optimal 
network i/o model needs a layer that maps a *request* to a thread, so that a 
worker thread (or process) will not have to be tied up entirely with a 
single connection during the whole life time of the connection. Instead, a 
worker can be scheduled to handle different connections, which helps both 
reducing the number of workers and the performance of request handling 
(especially on slow connections).

Such a layer should unify the upper interface of Event driven i/o, Windows 
i/o completion port, and many other async i/o mechanisms. With luck and 
careful design, the current filtered i/o chain and the module API can remain 
the same.

I hope that this would be one of the best features that 2.4+ will bring to 
us, as finally it will support any optimal i/o model on various platforms, 
and answer the doubts on Apache performance once and forever.

Bing
----
School of EE & CS,
Peking University,
Beijing 100871




Future direction of MPMs, was Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Paul Querna <ch...@force-elite.com>.
Graham Leggett wrote:
>> I know there are likely huge problems with this, but I would like to 
>> see how far
>> we can push the Event MPM, figure out what to do better, if there is 
>> anything, and then really dive into the 3.0 development before ApacheCon.
> 
> How difficult would this be to support in the other MPMs?

Windows, Worker MPM and the similar threaded MPMs could do it easily.

But, IMO, I want to eliminate all of the MPMs for 2.4/3.0.

I believe the MPMs as they are designed right now, are both a layer of 
portability, and a module that defines behavior or the model.

This makes all of the Unix ones very messy, with lots of copied code. 
Just look at Prefork, Worker, Event, Leader, Perchild -- they all have 
huge swaths of copied code.  And at their cores their behavior 
differences could just be runtime decisions.

I believe that at a minimum on the Unix side, and hopefully Win32 too, 
they should all be replaced with a single MPM, that has a  configurable 
behavior, and uses APR as much as possible, with only a few small ifdefs 
for portability.

Basically, one MPM to rule them all, with a configuration directive that 
can make it act like prefork or the event MPMs.

-Paul



Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Graham Leggett <mi...@sharp.fm>.
pquerna@apache.org wrote:

> Using this basic framework, you can return SUSPENDED from an HTTP Handler,
> and then register a callback that is invoked by the MPM at a later time.
> 
> This initial version only supports _timers_ as callbacks, but in the future I
> would like to add things like wait for socket activity, on a socket specified by
> the handler.

Ooooh... if you extend that idea to include callbacks on files as well 
as on sockets, you will help solve a long standing problem in the 
mod_disk_cache code.

It is something I have been keen to get into APR for a while (though I 
want to finish the current stuff on my APR plate first).

> Does anyone besides Rüdiger read commit emails :-) ?

Afraid not.

> I know there are likely huge problems with this, but I would like to see how far
> we can push the Event MPM, figure out what to do better, if there is anything, 
> and then really dive into the 3.0 development before ApacheCon.

How difficult would this be to support in the other MPMs?

Regards,
Graham
--


Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Paul Querna <ch...@force-elite.com>.
Ruediger Pluem wrote:
....
>> +
>> +typedef void (ap_mpm_callback_fn_t)(void *baton);
>> +
>> +/* XXXXXXX: only added support in the Event MPM.... */
>> +AP_DECLARE(void) ap_mpm_register_timed_callback(apr_time_t t,
>> +                                                ap_mpm_callback_fn_t 
>> *cbfn,
>> +                                                void *baton);
>> +    
> 
> This breaks compilation of other MPMs as exports is created from all 
> prototypes found in all
> header files in include. So IMHO the implementation of 
> ap_mpm_register_timed_callback either has
> to move to mpm_common.c or we need to add dummy implementation for the 
> other MPM's.
> Or we have a dummy implementation in mpm_common.c that is in a 
> conditional block and that only
> gets compiled if the MPM is not the event MPM.

I'd be okay with svn delete prefork worker as a solution :-)

Anyways, added a dummy version to mpm_common.c in r697425 for now.

-Paul


Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Ruediger Pluem <rp...@apache.org>.

On 09/20/2008 01:58 PM, pquerna@apache.org wrote:
> Author: pquerna
> Date: Sat Sep 20 04:58:08 2008
> New Revision: 697357
> 
> URL: http://svn.apache.org/viewvc?rev=697357&view=rev
> Log:
> Introduce Suspendable Requests to the Event MPM.
> 

> Modified: httpd/httpd/trunk/include/ap_mpm.h
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mpm.h?rev=697357&r1=697356&r2=697357&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/include/ap_mpm.h (original)
> +++ httpd/httpd/trunk/include/ap_mpm.h Sat Sep 20 04:58:08 2008
> @@ -152,6 +152,14 @@
>   */
>  AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result);
>  
> +
> +typedef void (ap_mpm_callback_fn_t)(void *baton);
> +
> +/* XXXXXXX: only added support in the Event MPM.... */
> +AP_DECLARE(void) ap_mpm_register_timed_callback(apr_time_t t,
> +                                                ap_mpm_callback_fn_t *cbfn,
> +                                                void *baton);
> +    

This breaks compilation of other MPMs as exports is created from all prototypes found in all
header files in include. So IMHO the implementation of ap_mpm_register_timed_callback either has
to move to mpm_common.c or we need to add dummy implementation for the other MPM's.
Or we have a dummy implementation in mpm_common.c that is in a conditional block and that only
gets compiled if the MPM is not the event MPM.

Regards

Rüdiger



Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Jim Jagielski <ji...@jaguNET.com>.
On Sep 20, 2008, at 4:31 PM, Paul Querna wrote:

> Jim Jagielski wrote:
>> On Sat, Sep 20, 2008 at 11:58:09AM -0000, pquerna@apache.org wrote:
>>> -#define ap_queue_empty(queue) ((queue)->nelts == 0)
>>> +#define ap_queue_empty(queue) ((queue)->nelts == 0 &&  
>>> APR_RING_EMPTY(&queue->timers ,timer_event_t, link))
>> Not || ?
>
> Don't think so?
>
> You want to return true if there are both zero entries in the array,  
> and zero in the ring.
>
> And false if either has something in it.
>

Ahhh yes, we need to check both structs in the queue now.

Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Paul Querna <ch...@force-elite.com>.
Jim Jagielski wrote:
> On Sat, Sep 20, 2008 at 11:58:09AM -0000, pquerna@apache.org wrote:
>> -#define ap_queue_empty(queue) ((queue)->nelts == 0)
>> +#define ap_queue_empty(queue) ((queue)->nelts == 0 && APR_RING_EMPTY(&queue->timers ,timer_event_t, link))
> 
> Not || ?

Don't think so?

You want to return true if there are both zero entries in the array, and 
zero in the ring.

And false if either has something in it.

Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Jim Jagielski <ji...@jaguNET.com>.
On Sat, Sep 20, 2008 at 11:58:09AM -0000, pquerna@apache.org wrote:
> 
> Does anyone besides Rüdiger read commit emails :-) ?
> 

No :)

> +    else {
> +        /* XXXXX: lol, pool allocation without a context from any thread.Yeah. Right. MPMs Suck. */
> +        te = malloc(sizeof(timer_event_t));
> +        APR_RING_ELEM_INIT(te, link);
> +    }
> +

> -#define ap_queue_empty(queue) ((queue)->nelts == 0)
> +#define ap_queue_empty(queue) ((queue)->nelts == 0 && APR_RING_EMPTY(&queue->timers ,timer_event_t, link))

Not || ?


Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Ruediger Pluem <rp...@apache.org>.

On 10/21/2008 01:09 AM, Nick Kew wrote:
> pquerna@apache.org wrote:
> 
>> --- httpd/httpd/trunk/modules/http/http_request.c (original)
>> +++ httpd/httpd/trunk/modules/http/http_request.c Sat Sep 20 04:58:08
>> 2008
> 
>> @@ -257,24 +297,7 @@
>>          ap_die(access_status, r);
>>      }
>>  
>> -    /* Send an EOR bucket through the output filter chain.  When
>> -     * this bucket is destroyed, the request will be logged and
>> -     * its pool will be freed
>> -     */
>> -    bb = apr_brigade_create(r->connection->pool,
>> r->connection->bucket_alloc);
>> -    b = ap_bucket_eor_create(r->connection->bucket_alloc, r);
>> -    APR_BRIGADE_INSERT_HEAD(bb, b);
>> -    ap_pass_brigade(r->connection->output_filters, bb);
>> -
>> -    /* From here onward, it is no longer safe to reference r
>> -     * or r->pool, because r->pool may have been destroyed
>> -     * already by the EOR bucket's cleanup function.
>> -     */
>> -
>> -    c->cs->state = CONN_STATE_WRITE_COMPLETION;
>> -    check_pipeline(c);
>> -    if (ap_extended_status)
>> -        ap_time_process_request(c->sbh, STOP_PREQUEST);
>> +    return ap_process_request_after_handler(r);
>>  }
> 
> This is a compile error in a void function.
> What exactly was intended here?

As ap_process_request_after_handler is a void function by itself I guess it
should be simply

ap_process_request_after_handler(r);

instead of

return ap_process_request_after_handler(r);

but this is just a guess.

Regards

Rüdiger

> 

Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Nick Kew <ni...@webthing.com>.
pquerna@apache.org wrote:

> --- httpd/httpd/trunk/modules/http/http_request.c (original)
> +++ httpd/httpd/trunk/modules/http/http_request.c Sat Sep 20 04:58:08 2008

> @@ -257,24 +297,7 @@
>          ap_die(access_status, r);
>      }
>  
> -    /* Send an EOR bucket through the output filter chain.  When
> -     * this bucket is destroyed, the request will be logged and
> -     * its pool will be freed
> -     */
> -    bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);
> -    b = ap_bucket_eor_create(r->connection->bucket_alloc, r);
> -    APR_BRIGADE_INSERT_HEAD(bb, b);
> -    ap_pass_brigade(r->connection->output_filters, bb);
> -
> -    /* From here onward, it is no longer safe to reference r
> -     * or r->pool, because r->pool may have been destroyed
> -     * already by the EOR bucket's cleanup function.
> -     */
> -
> -    c->cs->state = CONN_STATE_WRITE_COMPLETION;
> -    check_pipeline(c);
> -    if (ap_extended_status)
> -        ap_time_process_request(c->sbh, STOP_PREQUEST);
> +    return ap_process_request_after_handler(r);
>  }

This is a compile error in a void function.
What exactly was intended here?

-- 
Nick Kew

Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Paul Querna <ch...@force-elite.com>.
Nick Kew wrote:
> 
> On 20 Sep 2008, at 12:58, pquerna@apache.org wrote:
> 
>> Introduce Suspendable Requests to the Event MPM.
> 
> Hmmm ...
> 
> Are you sure this belongs in the MPM?


The core problem is, where do we have an Event scheduling and worker 
thread system.  The best place to keep that is the MPM right now -- see 
the "future direction of MPMs" thread for my thoughts on that :-)





Re: svn commit: r697357 - in /httpd/httpd/trunk: include/ modules/http/ modules/test/ server/ server/mpm/experimental/event/

Posted by Nick Kew <ni...@webthing.com>.
On 20 Sep 2008, at 12:58, pquerna@apache.org wrote:

> Introduce Suspendable Requests to the Event MPM.

Hmmm ...

Are you sure this belongs in the MPM?

> Using this basic framework, you can return SUSPENDED from an HTTP  
> Handler,
> and then register a callback that is invoked by the MPM at a later  
> time.

... an HTTP Handler not being an MPM thing begs the question: have you
just introduced a whole category of modules that'll only work with  
the one MPM?

> To prove I'm not insane,

*cough*

> Does anyone besides Rüdiger read commit emails :-) ?

Nope.

> I know there are likely huge problems with this, but I would like  
> to see how far
> we can push the Event MPM, figure out what to do better, if there  
> is anything,
> and then really dive into the 3.0 development before ApacheCon.

Damn.  I'll hafta try & look at this.

-- 
Nick Kew