Commit 968f3150 authored by Paul Querna's avatar Paul Querna
Browse files

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.




git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@697357 13f79535-47bb-0310-9956-ffa450edef68
parent 58345b32
Loading
Loading
Loading
Loading
+2 −1
Changes for include/ap_mmn.h: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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 */

+8 −0
Changes for include/ap_mpm.h: 8 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -152,6 +152,14 @@ AP_DECLARE(apr_status_t) ap_os_create_privileged_process(
 */
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
+3 −0
Changes for include/http_request.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -315,6 +315,9 @@ AP_DECLARE(void) ap_allow_standard_methods(request_rec *r, int reset, ...);
 */
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,
+5 −0
Changes for include/httpd.h: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -457,6 +457,8 @@ AP_DECLARE(const char *) ap_get_server_built(void);
#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 @@ struct request_rec {
    /** 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 @@ typedef enum {
    CONN_STATE_READ_REQUEST_LINE,
    CONN_STATE_HANDLER,
    CONN_STATE_WRITE_COMPLETION,
    CONN_STATE_SUSPENDED,
    CONN_STATE_LINGER
} conn_state_e;

+2 −1
Changes for modules/http/http_core.c: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -154,7 +154,8 @@ static int ap_process_http_async_connection(conn_rec *c)
                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;
            }
Loading