Projects
Wiki     Timeline     Browser     Search     New Ticket     Bug Reports

apache: 20100421a-apache-gcdmpm.diff

File 20100421a-apache-gcdmpm.diff, 32.7 KB (added by robert@…, 21 months ago)

GCD MPM patch for Apache

  • server/mpm/MPM.NAMING

     
    1212  worker ........ Multi Process model with threads.  One acceptor thread, 
    1313                  multiple worker threads. 
    1414  netware ....... Multi-threaded MPM for Netware 
     15  gcd ........... Experimental Grand Central Dispatch (GCD) MPM. 
  • server/mpm/config.m4

     
    3838    fi 
    3939]) 
    4040 
     41dnl Is GCD supported on this platform? 
     42AC_CACHE_CHECK([whether GCD is available], [ac_cv_have_gcd], [ 
     43    AC_CHECK_LIB(dispatch, dispatch_main) 
     44    if test "$ac_cv_func_dispatch_main" != "no"; then 
     45        ac_cv_have_gcd=yes 
     46    else 
     47        ac_cv_have_gcd=no 
     48    fi 
     49]) 
     50 
     51dnl Check for C blocks support. 
     52AC_CACHE_CHECK([for C blocks support], ac_cv_blocks, [ 
     53    save_CFLAGS=$CFLAGS 
     54    CFLAGS="$CFLAGS -fblocks" 
     55    AC_TRY_COMPILE([], [ ^{ ;}; ], ac_cv_cblocks=yes, ac_cv_cblocks=no)] 
     56    CFLAGS=$save_CFLAGS 
     57) 
     58if test "$ac_cv_cblocks" = "yes"; then 
     59    CFLAGS="$CFLAGS -fblocks" 
     60    AC_MSG_RESULT(yes) 
     61else 
     62    AC_MSG_RESULT(no) 
     63fi 
     64 
    4165dnl See if this is a forking platform w.r.t. MPMs 
    4266case $host in 
    4367    *mingw32* | *os2-emx*) 
  • server/mpm/config2.m4

     
    11AC_MSG_CHECKING(which MPM to use by default) 
    22AC_ARG_WITH(mpm, 
    33APACHE_HELP_STRING(--with-mpm=MPM,Choose the process model for Apache to use by default. 
    4                           MPM={simple|event|worker|prefork|winnt} 
     4                          MPM={simple|event|worker|prefork|winnt|gcd} 
    55                          This will be statically linked as the only available MPM unless 
    66                          --enable-mpms-shared is also specified. 
    77),[ 
  • server/mpm/gcd/Makefile.in

     
     1include $(top_srcdir)/build/special.mk 
  • server/mpm/gcd/gcd.h

    Property changes on: server/mpm/gcd/Makefile.in
    ___________________________________________________________________
    Added: svn:mime-type
       + text/plain
    Added: svn:keywords
       + FreeBSD=%H
    Added: svn:eol-style
       + native
    
     
     1/* 
     2 * Copyright (c) 2009-2010 Apple Inc. All rights reserved. 
     3 * 
     4 * @APPLE_APACHE_LICENSE_HEADER_START@ 
     5 *  
     6 * Licensed under the Apache License, Version 2.0 (the "License"); 
     7 * you may not use this file except in compliance with the License. 
     8 * You may obtain a copy of the License at 
     9 *  
     10 *     http://www.apache.org/licenses/LICENSE-2.0 
     11 *  
     12 * Unless required by applicable law or agreed to in writing, software 
     13 * distributed under the License is distributed on an "AS IS" BASIS, 
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
     15 * See the License for the specific language governing permissions and 
     16 * limitations under the License. 
     17 *  
     18 * @APPLE_APACHE_LICENSE_HEADER_END@ 
     19 */ 
     20 
     21/* 
     22 * Variables from gcd_parent.c. 
     23 */ 
     24extern int                      mpm_state; 
     25extern volatile ap_generation_t my_generation; 
     26extern volatile int             is_graceful; 
     27extern volatile int             restart_pending; 
     28extern volatile int             shutdown_pending; 
     29extern apr_pool_t               *pconf; 
     30extern u_int                    listener_count; 
     31 
     32/* 
     33 * Variables and functions from gcd.c. 
     34 */ 
     35apr_status_t    gcdmpm_child_run(void); 
     36extern u_int    gcdmpm_connection_limit; 
  • server/mpm/gcd/mpm_default.h

    Property changes on: server/mpm/gcd/gcd.h
    ___________________________________________________________________
    Added: svn:mime-type
       + text/plain
    Added: svn:keywords
       + FreeBSD=%H
    Added: svn:eol-style
       + native
    
     
     1#ifndef DEFAULT_PIDLOG 
     2#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid" 
     3#endif 
  • server/mpm/gcd/config.m4

     
     1AC_MSG_CHECKING(if gcd MPM supports this platform) 
     2if test $ac_cv_have_gcd != yes; then 
     3    AC_MSG_RESULT(no - This is not a GCD-aware platform) 
     4elif test $ac_cv_cblocks != yes; then 
     5    AC_MSG_RESULT(no - No compiler support for blocks) 
     6else 
     7    AC_MSG_RESULT(yes) 
     8    APACHE_MPM_SUPPORTED(gcd, yes, yes) 
     9fi 
  • server/mpm/gcd/config3.m4

     
     1APACHE_MPM_MODULE(gcd, $enable_mpm_gcd, gcd_parent.lo gcd.lo) 
  • server/mpm/gcd/gcd_parent.c

     
     1/* 
     2 * Copyright (c) 2009-2010 Apple Inc. All rights reserved. 
     3 * 
     4 * @APPLE_APACHE_LICENSE_HEADER_START@ 
     5 *  
     6 * Licensed under the Apache License, Version 2.0 (the "License"); 
     7 * you may not use this file except in compliance with the License. 
     8 * You may obtain a copy of the License at 
     9 *  
     10 *     http://www.apache.org/licenses/LICENSE-2.0 
     11 *  
     12 * Unless required by applicable law or agreed to in writing, software 
     13 * distributed under the License is distributed on an "AS IS" BASIS, 
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
     15 * See the License for the specific language governing permissions and 
     16 * limitations under the License. 
     17 *  
     18 * @APPLE_APACHE_LICENSE_HEADER_END@ 
     19 */ 
     20 
     21/* 
     22 * This file implements the main Apache work loop and process, and is 
     23 * responsible for starting (and restarting) a child process that implements 
     24 * connection listening and processing using GCD.  We are careful not to kick 
     25 * GCD (as we would be with threading) before calling fork(), so have to use 
     26 * normal UNIX signal handlers for signal processing.  The body of the GCD 
     27 * MPM is found in gcd.c:gcdmpm_child_run(). 
     28 */ 
     29 
     30#include "apr.h" 
     31#include "apr_portable.h" 
     32#include "apr_signal.h" 
     33 
     34#include "httpd.h" 
     35#include "http_main.h" 
     36#include "http_log.h" 
     37#include "http_config.h" 
     38#include "http_connection.h" 
     39#include "http_vhost.h" 
     40#include "mpm_common.h" 
     41#include "ap_listen.h" 
     42#include "scoreboard.h" 
     43#include "unixd.h" 
     44 
     45#include "gcd.h" 
     46#include "mpm_default.h" 
     47 
     48/* 
     49 * Global variables relating to MPM state and state transitions. 
     50 */ 
     51int                             mpm_state = AP_MPMQ_STARTING; 
     52volatile ap_generation_t        my_generation = 0; 
     53volatile int                    is_graceful; 
     54volatile int                    restart_pending; 
     55volatile int                    shutdown_pending; 
     56 
     57static pid_t                    child_pid; 
     58 
     59apr_pool_t                      *pconf; /* Pool for config stuff. */ 
     60u_int                           listener_count; 
     61 
     62/* 
     63 * Handle signals in the parent; gcdmpm_run will forward to the child. 
     64 */ 
     65static void 
     66gcdmpm_signal_handler(int sig) 
     67{ 
     68 
     69        switch (sig) { 
     70        case AP_SIG_GRACEFUL: 
     71                is_graceful = 1; 
     72        case SIGHUP: 
     73                restart_pending = 1; 
     74                break; 
     75        case AP_SIG_GRACEFUL_STOP: 
     76                is_graceful = 1; 
     77        case SIGTERM: 
     78                shutdown_pending = 1; 
     79                break; 
     80        } 
     81} 
     82 
     83static const char * 
     84gcdmpm_get_name(void) 
     85{ 
     86 
     87        return ("gcd"); 
     88} 
     89 
     90static int 
     91gcdmpm_query(int query_code, int *result, apr_status_t *rv) 
     92{ 
     93 
     94        *rv = APR_SUCCESS; 
     95        switch (query_code) { 
     96        case AP_MPMQ_IS_THREADED: 
     97                *result = AP_MPMQ_DYNAMIC; 
     98                break; 
     99        case AP_MPMQ_IS_FORKED: 
     100                *result = AP_MPMQ_STATIC; 
     101                break; 
     102        case AP_MPMQ_IS_ASYNC: 
     103                *result = 1; 
     104                break; 
     105        case AP_MPMQ_HARD_LIMIT_DAEMONS: 
     106        case AP_MPMQ_MAX_DAEMONS: 
     107        case AP_MPMQ_MAX_DAEMON_USED: 
     108                *result = 1; 
     109                break; 
     110        case AP_MPMQ_HARD_LIMIT_THREADS: 
     111                *result = gcdmpm_connection_limit; 
     112                break; 
     113        case AP_MPMQ_MAX_THREADS: 
     114                *result = gcdmpm_connection_limit; 
     115                break; 
     116        case AP_MPMQ_MIN_SPARE_DAEMONS: 
     117        case AP_MPMQ_MAX_SPARE_DAEMONS: 
     118        case AP_MPMQ_MIN_SPARE_THREADS: 
     119        case AP_MPMQ_MAX_SPARE_THREADS: 
     120                *result = 0; 
     121                break; 
     122        case AP_MPMQ_MAX_REQUESTS_DAEMON: 
     123                *result = -1; 
     124                break; 
     125        case AP_MPMQ_MPM_STATE: 
     126                *result = mpm_state; 
     127                break; 
     128        case AP_MPMQ_GENERATION: 
     129                *result = my_generation; 
     130                break; 
     131        default: 
     132                *rv = APR_ENOTIMPL; 
     133                break; 
     134        } 
     135        return (OK); 
     136} 
     137 
     138static int 
     139gcdmpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) 
     140{ 
     141        int rv; 
     142 
     143        restart_pending = shutdown_pending = 0; 
     144 
     145        ap_log_pid(pconf, ap_pid_fname); 
     146 
     147        if (!is_graceful) { 
     148                if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { 
     149                        mpm_state = AP_MPMQ_STOPPING; 
     150                        return (DONE); 
     151                } 
     152                ap_scoreboard_image->global->running_generation = 
     153                    my_generation; 
     154        } 
     155 
     156        /* 
     157         * XXXGCD: Should use APR APIs for fork/wait/... 
     158         */ 
     159        child_pid = fork(); 
     160        if (child_pid < 0) { 
     161                ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, 
     162                    "fork: Unable to fork new process"); 
     163                apr_sleep(apr_time_from_sec(10)); 
     164                return -1; 
     165        } 
     166        if (child_pid == 0) { 
     167                exit(gcdmpm_child_run()); 
     168        } 
     169 
     170        apr_signal(AP_SIG_GRACEFUL, gcdmpm_signal_handler); 
     171        apr_signal(SIGHUP, gcdmpm_signal_handler); 
     172        apr_signal(AP_SIG_GRACEFUL_STOP, gcdmpm_signal_handler); 
     173        apr_signal(SIGTERM, gcdmpm_signal_handler); 
     174 
     175        while (!restart_pending && !shutdown_pending) { 
     176                static const sigset_t ss; 
     177                (void)sigsuspend(&ss); 
     178        } 
     179 
     180        if (shutdown_pending && !is_graceful) { 
     181                const char *pidfile; 
     182 
     183                kill(child_pid, SIGTERM); 
     184                pidfile = ap_server_root_relative(pconf, ap_pid_fname); 
     185                if (pidfile != NULL && unlink(pidfile) == 0) { 
     186                        ap_log_error(APLOG_MARK, APLOG_INFO, 0, 
     187                            ap_server_conf, "removed PID file %s (pid=%ld)", 
     188                            pidfile, (long)getpid()); 
     189                } 
     190                ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, 
     191                    "caught SIGTERM, shutting down"); 
     192                return (DONE); 
     193        } else if (shutdown_pending) { 
     194                const char *pidfile; 
     195 
     196                kill(child_pid, AP_SIG_GRACEFUL_STOP); 
     197                ap_close_listeners(); 
     198                pidfile = ap_server_root_relative(pconf, ap_pid_fname); 
     199                if (pidfile != NULL && unlink(pidfile) == 0) { 
     200                        ap_log_error(APLOG_MARK, APLOG_INFO, 0, 
     201                            ap_server_conf, "removed PID file %s (pid=%ld)", 
     202                            pidfile, (long)getpid()); 
     203                } 
     204                ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, 
     205                    "caught " AP_SIG_GRACEFUL_STOP_STRING 
     206                    ", shutting down gracefully"); 
     207                do { 
     208                        rv = waitpid(child_pid, NULL, 0); 
     209                        if (rv < 0 && errno != EINTR) { 
     210                                /* XXXGCD: handle? */ 
     211                        } 
     212                } while (rv != child_pid); 
     213                return (DONE); 
     214        } 
     215 
     216        ++my_generation; 
     217        ap_scoreboard_image->global->running_generation = my_generation; 
     218 
     219        if (is_graceful) { 
     220                ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, 
     221                    AP_SIG_GRACEFUL_STRING " received.  " 
     222                    "Doing graceful restart"); 
     223                kill(child_pid, AP_SIG_GRACEFUL); 
     224        } else { 
     225                ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, 
     226                    "SIGHUP received.  Attempting restart"); 
     227                kill(child_pid, SIGHUP); 
     228        } 
     229        return (OK); 
     230} 
     231 
     232static int 
     233gcdmpm_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, 
     234    server_rec *s) 
     235{ 
     236 
     237        pconf = p; 
     238        listener_count = ap_setup_listeners(ap_server_conf); 
     239        if (listener_count < 0) { 
     240                ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s, 
     241                    "no listening sockets available, shutting down"); 
     242                return (DONE); 
     243        } 
     244        return (OK); 
     245} 
     246 
     247static int 
     248gcdmpm_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) 
     249{ 
     250        int debug, foreground, no_detach; 
     251        apr_status_t rv; 
     252 
     253        /* XXXRW: Possibly more should be done here. */ 
     254 
     255        mpm_state = AP_MPMQ_STARTING; 
     256        debug = ap_exists_config_define("DEBUG"); 
     257        if (debug) { 
     258                foreground = 1; 
     259                no_detach = 0; 
     260        } else { 
     261                no_detach = ap_exists_config_define("NO_DETACH"); 
     262                foreground = ap_exists_config_define("FOREGROUND"); 
     263        } 
     264        if (!foreground) { 
     265                rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND 
     266                    : APR_PROC_DETACH_DAEMONIZE); 
     267                if (rv != APR_SUCCESS) { 
     268                        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, 
     269                            "apr_proc_detach failed"); 
     270                        return (HTTP_INTERNAL_SERVER_ERROR); 
     271                } 
     272        } 
     273        ap_listen_pre_config(); 
     274        ap_pid_fname = DEFAULT_PIDLOG; 
     275        return (OK); 
     276} 
     277 
     278static void 
     279gcdmpm_hooks(apr_pool_t *p) 
     280{ 
     281        static const char *const aszSucc[] = { "core.c", NULL }; 
     282 
     283        ap_hook_open_logs(gcdmpm_open_logs, NULL, aszSucc, 
     284            APR_HOOK_REALLY_FIRST); 
     285        ap_hook_pre_config(gcdmpm_pre_config, NULL, NULL, 
     286            APR_HOOK_REALLY_FIRST); 
     287        /* ap_hook_check_config() */ 
     288        ap_hook_mpm(gcdmpm_run, NULL, NULL, APR_HOOK_MIDDLE); 
     289        ap_hook_mpm_query(gcdmpm_query, NULL, NULL, APR_HOOK_MIDDLE); 
     290        ap_hook_mpm_get_name(gcdmpm_get_name, NULL, NULL, APR_HOOK_MIDDLE); 
     291} 
     292 
     293static const command_rec gcdmpm_cmds[] = { 
     294        LISTEN_COMMANDS, 
     295        AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND, 
     296        { NULL } 
     297}; 
     298 
     299module AP_MODULE_DECLARE_DATA mpm_gcd_module = { 
     300        MPM20_MODULE_STUFF, 
     301        NULL,                   /* hook to run before apache parses args */ 
     302        NULL,                   /* create per-directory config structure */ 
     303        NULL,                   /* merge per-directory config structures */ 
     304        NULL,                   /* create per-server config structure */ 
     305        NULL,                   /* merge per-server config structures */ 
     306        gcdmpm_cmds,            /* command apr_table_t */ 
     307        gcdmpm_hooks            /* register_hooks */ 
     308}; 
  • server/mpm/gcd/gcd.c

    Property changes on: server/mpm/gcd/gcd_parent.c
    ___________________________________________________________________
    Added: svn:mime-type
       + text/plain
    Added: svn:keywords
       + FreeBSD=%H
    Added: svn:eol-style
       + native
    
     
     1/*- 
     2 * Copyright (c) 2009-2010 Apple Inc. All rights reserved. 
     3 * 
     4 * @APPLE_APACHE_LICENSE_HEADER_START@ 
     5 *  
     6 * Licensed under the Apache License, Version 2.0 (the "License"); 
     7 * you may not use this file except in compliance with the License. 
     8 * You may obtain a copy of the License at 
     9 *  
     10 *     http://www.apache.org/licenses/LICENSE-2.0 
     11 *  
     12 * Unless required by applicable law or agreed to in writing, software 
     13 * distributed under the License is distributed on an "AS IS" BASIS, 
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
     15 * See the License for the specific language governing permissions and 
     16 * limitations under the License. 
     17 *  
     18 * @APPLE_APACHE_LICENSE_HEADER_END@ 
     19 */ 
     20 
     21/*- 
     22 * Experimental GCD Apache MPM. 
     23 * 
     24 * This Apache module makes use of Apple's Grand Central Dispatch (GCD) 
     25 * concurrent programming framework to manage Apache concurrency.  While 
     26 * similar in design to the event MPM, this MPM associates GCD dispatch 
     27 * queues, rather than threads, with various tasks. 
     28 * 
     29 * Four types of queues are used in this module: 
     30 * 
     31 * main queue - configuration and shutdown, blocked in the steady state, 
     32 * signals queue - serial queue to await and process signals, 
     33 * listener queue - concurrent queue accepting from listen sockets, 
     34 * connection queues - one serial queue for each active connection, quantity 
     35 *                     limited by gcdmpm_connection_sema. 
     36 * 
     37 * Because we wish to restart across privilege drops, we do need multiple 
     38 * processes for the GCD MPM.  gcd_parent.c encapsulates a parent process, 
     39 * whose single goal is to spawn (and respawn) a GCD-enabled child process 
     40 * implemented in this file.  This also makes it easier to implement 
     41 * ungraceful shutdown and restart by simply calling exit() from the child. 
     42 * 
     43 * Author: Robert N. M. Watson <robert@fledge.watson.org>. 
     44 */ 
     45 
     46/*- 
     47 * gcdmpm TODO 
     48 * 
     49 * - Because we don't bother with threads, and hence thread IDs, we don't 
     50 *   properly maintain the scoreboard.  This should be solved in some 
     51 *   moderately scalable way. 
     52 * - Are there custom configuration parameters that could/should be 
     53 *   supported? 
     54 * - Error checking for apr and GCD routines leaves something to be desired, 
     55 *   but is not inconsistent with other MPMs. 
     56 * - APR routines for fork/wait/sigsuspend should be used, rather than UNIX 
     57 *   interfaces in gcd_parent.c. 
     58 * - The semaphore wait for a free connection slot following accept(2) is 
     59 *   non-interruptible, which could prevent the listener pool from draining 
     60 *   during graceful restart or shutdown.  As soon as necessary connections 
     61 *   close, it will be able to continue.  This may be a bug. 
     62 * - There is the (unexploited) opportunity to check for graceful 
     63 *   restart/shutdown while the HTTP connection is idle. 
     64 */ 
     65 
     66#include <dispatch/dispatch.h> 
     67 
     68#include "apr.h" 
     69#include "apr_portable.h" 
     70#include "apr_signal.h" 
     71 
     72#if !APR_HAS_THREADS 
     73#error "The GCD MPM requires APR threading support, which is not present." 
     74#endif 
     75 
     76#include "httpd.h" 
     77#include "http_main.h" 
     78#include "http_log.h" 
     79#include "http_config.h" 
     80#include "http_connection.h" 
     81#include "http_core.h" 
     82#include "http_vhost.h" 
     83#include "mpm_common.h" 
     84#include "ap_listen.h" 
     85#include "scoreboard.h" 
     86#include "unixd.h" 
     87 
     88#include "gcd.h" 
     89 
     90/* 
     91 * Semaphore that the main queue will block on waiting for restart/shutdown 
     92 * events.  Fired from the gcdmpm_signals_queue. 
     93 */ 
     94static dispatch_semaphore_t     gcdmpm_signalfired_sema; 
     95 
     96/* 
     97 * A queue to process signals, a semaphore to notify when the queue has 
     98 * drained, and dispatch sources for each signal. 
     99 */ 
     100static dispatch_queue_t         gcdmpm_signals_queue; 
     101static dispatch_source_t        ds_sighup, ds_sigterm; 
     102static dispatch_source_t        ds_sig_graceful, ds_sig_graceful_stop; 
     103 
     104/* 
     105 * A concurrent queue in which to perform accept(2) operations, a semaphore 
     106 * to notify when the queue has drained, and an array of dispatch sources 
     107 * (size listener_count), one for each listen socket. 
     108 */ 
     109static dispatch_queue_t         gcdmpm_listen_queue; 
     110static dispatch_source_t        *ds_listeners; 
     111 
     112/* 
     113 * Connection queues are created dynamically as sockets are accepted; the 
     114 * number of queues is bounded by gcdmpm_connection_sema, and each active 
     115 * connection is counted towards gcdmpm_connection_group which can then be 
     116 * waited on during a graceful shutdown.  gcdmpm_connection_limit is a bound 
     117 * on the number of connection queues we will create concurrently. 
     118 * 
     119 * Each per-connection queue executes in serial, with gcdmpm_process_socket() 
     120 * bumping between connection states in response to dispatch sources set up 
     121 * in gcdmpm_newconn(). 
     122 */ 
     123static dispatch_semaphore_t     gcdmpm_connection_sema; 
     124static dispatch_group_t         gcdmpm_connection_group; 
     125u_int                           gcdmpm_connection_limit = 1024; 
     126 
     127/* 
     128 * Each in-flight connection is described by one instance of 
     129 * gcdmpm_connection, which holds references to its associated queue, 
     130 * dispatch sources, socket, and Apache connection state. 
     131 */ 
     132struct gcdmpm_connection { 
     133        /* GCD-related state. */ 
     134        dispatch_queue_t         gc_queue; 
     135        dispatch_source_t        gc_read_source; 
     136        dispatch_source_t        gc_write_source; 
     137        dispatch_source_t        gc_timer_source; 
     138        int                      gc_read_source_enabled; 
     139        int                      gc_write_source_enabled; 
     140        int                      gc_timer_source_enabled; 
     141 
     142        /* Apache state. */ 
     143        apr_socket_t            *gc_sock; 
     144        conn_state_t            *gc_cs; 
     145        apr_pool_t              *gc_pool; 
     146}; 
     147 
     148/* 
     149 * Close a connection: tear down GCD state, tear down Apache state, and 
     150 * signal gcdmpm_connection_sema so that processing can start on a new 
     151 * connection if we're at the limit. 
     152 */ 
     153static void 
     154gcdmpm_closeconn(struct gcdmpm_connection *gcp) 
     155{ 
     156 
     157        dispatch_source_cancel(gcp->gc_read_source); 
     158        dispatch_source_cancel(gcp->gc_write_source); 
     159        dispatch_source_cancel(gcp->gc_timer_source); 
     160 
     161        dispatch_resume(gcp->gc_read_source); 
     162        dispatch_resume(gcp->gc_write_source); 
     163        dispatch_resume(gcp->gc_timer_source); 
     164 
     165        dispatch_release(gcp->gc_read_source); 
     166        dispatch_release(gcp->gc_write_source); 
     167        dispatch_release(gcp->gc_timer_source); 
     168 
     169        ap_lingering_close(gcp->gc_cs->c); 
     170        dispatch_release(gcp->gc_queue); 
     171        apr_pool_destroy(gcp->gc_pool); 
     172 
     173        dispatch_semaphore_signal(gcdmpm_connection_sema); 
     174} 
     175 
     176/* 
     177 * Suspend I/O and timer dispatch sources for a connection. 
     178 */ 
     179static void 
     180gcdmpm_suspend_sources(struct gcdmpm_connection *gcp) 
     181{ 
     182 
     183        if (gcp->gc_read_source_enabled) { 
     184                dispatch_suspend(gcp->gc_read_source); 
     185                gcp->gc_read_source_enabled = 0; 
     186        } 
     187        if (gcp->gc_write_source_enabled) { 
     188                dispatch_suspend(gcp->gc_write_source); 
     189                gcp->gc_write_source_enabled = 0; 
     190        } 
     191        if (gcp->gc_timer_source_enabled) { 
     192                dispatch_suspend(gcp->gc_timer_source); 
     193                gcp->gc_timer_source_enabled = 0; 
     194        } 
     195} 
     196 
     197/* 
     198 * Resume I/O dispatch sources for a connection. 
     199 */ 
     200static void 
     201gcdmpm_read_set(struct gcdmpm_connection *gcp) 
     202{ 
     203 
     204        dispatch_resume(gcp->gc_read_source); 
     205        gcp->gc_read_source_enabled = 1; 
     206} 
     207 
     208static void 
     209gcdmpm_write_set(struct gcdmpm_connection *gcp) 
     210{ 
     211 
     212        dispatch_resume(gcp->gc_write_source); 
     213        gcp->gc_write_source_enabled = 1; 
     214} 
     215 
     216/* 
     217 * Set a timeout for a connection. 
     218 */ 
     219static void 
     220gcdmpm_timer_set(struct gcdmpm_connection *gcp, apr_interval_time_t it) 
     221{ 
     222        uint64_t dt; 
     223 
     224        /* NB: Only second granularity at this point. */ 
     225        dt = (uint64_t)apr_time_sec(it) * NSEC_PER_SEC; 
     226        dispatch_source_set_timer(gcp->gc_timer_source, 
     227            dispatch_time(DISPATCH_TIME_NOW, dt), DISPATCH_TIME_FOREVER, 0); 
     228        dispatch_resume(gcp->gc_timer_source); 
     229        gcp->gc_timer_source_enabled = 1; 
     230} 
     231 
     232/* 
     233 * Setup and operate a single connection.  This code is modeled on the event 
     234 * MPM's process_socket(). 
     235 */ 
     236static void 
     237gcdmpm_process_socket(struct gcdmpm_connection *gcp) 
     238{ 
     239        ap_sb_handle_t *sbh; 
     240        conn_state_t *cs; 
     241        conn_rec *c; 
     242        apr_status_t rv; 
     243 
     244        gcdmpm_suspend_sources(gcp); 
     245 
     246        ap_create_sb_handle(&sbh, gcp->gc_pool, 0, 0); 
     247 
     248        /* 
     249         * Allocate state for a new connection. 
     250         */ 
     251        if (gcp->gc_cs == NULL) { 
     252                cs = gcp->gc_cs = apr_pcalloc(gcp->gc_pool, 
     253                    sizeof(*gcp->gc_cs)); 
     254                cs->bucket_alloc = apr_bucket_alloc_create(gcp->gc_pool); 
     255                c = cs->c = ap_run_create_connection(gcp->gc_pool, 
     256                    ap_server_conf, gcp->gc_sock, 0, sbh, 
     257                    cs->bucket_alloc); 
     258                c->cs = cs; 
     259 
     260                ap_update_vhost_given_ip(c); 
     261                rv = ap_run_pre_connection(c, gcp->gc_sock); 
     262                if (rv != OK && rv != DONE) { 
     263                        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 
     264                            ap_server_conf, 
     265                            "gcdmpm_process_socket: connection aborted"); 
     266                        c->aborted = 1; 
     267                } 
     268                cs->state = CONN_STATE_CHECK_REQUEST_LINE_READABLE; 
     269        } else { 
     270                cs = gcp->gc_cs; 
     271                c = cs->c; 
     272                c->sbh = sbh; 
     273        } 
     274 
     275        /* 
     276         * If clogging input filters are present (mod_ssl), hand over control. 
     277         */ 
     278        if (c->clogging_input_filters && !c->aborted) { 
     279                ap_run_process_connection(c); 
     280                if (cs->state != CONN_STATE_SUSPENDED) { 
     281                        cs->state = CONN_STATE_LINGER; 
     282                } 
     283        } 
     284 
     285read_request: 
     286        /* 
     287         * Ready to read on the socket. 
     288         */ 
     289        if (cs->state == CONN_STATE_READ_REQUEST_LINE) { 
     290                if (!c->aborted) { 
     291                        ap_run_process_connection(c); 
     292                } else { 
     293                        cs->state = CONN_STATE_LINGER; 
     294                } 
     295        } 
     296 
     297        /* 
     298         * Need to write on the socket. 
     299         */ 
     300        if (cs->state == CONN_STATE_WRITE_COMPLETION) { 
     301                ap_filter_t *output_filter = c->output_filters; 
     302 
     303                while (output_filter->next != NULL) { 
     304                        output_filter = output_filter->next; 
     305                } 
     306                rv = output_filter->frec->filter_func.out_func(output_filter, 
     307                    NULL); 
     308                if (rv != APR_SUCCESS) { 
     309                        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, 
     310                            ap_server_conf, 
     311                            "network write failure im core output filter"); 
     312                        cs->state = CONN_STATE_LINGER; 
     313                } else if (c->data_in_output_filters) { 
     314                        gcdmpm_timer_set(gcp, ap_server_conf->timeout); 
     315                        gcdmpm_write_set(gcp); 
     316                        return; 
     317                } else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted) { 
     318                        /* Could check shutdown/restart_pending here as well? */ 
     319                        cs->state = CONN_STATE_LINGER; 
     320                } else if (c->data_in_input_filters) { 
     321                        cs->state = CONN_STATE_READ_REQUEST_LINE; 
     322                        goto read_request; 
     323                } else { 
     324                        cs->state = CONN_STATE_CHECK_REQUEST_LINE_READABLE; 
     325                } 
     326        } 
     327 
     328        if (cs->state == CONN_STATE_LINGER) { 
     329                gcdmpm_closeconn(gcp); 
     330        } else if (cs->state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) { 
     331                gcdmpm_timer_set(gcp, ap_server_conf->keep_alive_timeout); 
     332                gcdmpm_read_set(gcp); 
     333        } 
     334} 
     335 
     336/* 
     337 * Allocate basic state for a new connection, configure GCD, and give it a 
     338 * a kick so that further new connection processing can be performed 
     339 * asynchronously in its per-connection queue.  All further processing on the 
     340 * connection is done by gcdmpm_process_socket(). 
     341 */ 
     342static void 
     343gcdmpm_newconn(apr_socket_t *sock, apr_pool_t *ptrans) 
     344{ 
     345        struct gcdmpm_connection *gcp; 
     346        int fd; 
     347 
     348        gcp = apr_pcalloc(ptrans, sizeof(*gcp)); 
     349        gcp->gc_sock = sock; 
     350        gcp->gc_pool = ptrans; 
     351 
     352        /* 
     353         * Create a queue from which we'll run any connection-related events. 
     354         */ 
     355        gcp->gc_queue = dispatch_queue_create("gcdmpm_work", NULL); 
     356        dispatch_suspend(gcp->gc_queue); 
     357 
     358        /* 
     359         * Create three sources: read/write on the socket, and a timer. 
     360         */ 
     361        apr_os_sock_get(&fd, sock); 
     362        gcp->gc_read_source = dispatch_source_create( 
     363            DISPATCH_SOURCE_TYPE_READ, fd, 0, gcp->gc_queue); 
     364        dispatch_source_set_event_handler(gcp->gc_read_source, ^{ 
     365                switch (gcp->gc_cs->state) { 
     366                case CONN_STATE_CHECK_REQUEST_LINE_READABLE: 
     367                        gcp->gc_cs->state = CONN_STATE_READ_REQUEST_LINE; 
     368                        break; 
     369                default: 
     370                        ap_log_error(APLOG_MARK, APLOG_ERR, 0, 
     371                            ap_server_conf, "gcdmpm_newconn: unexpected " 
     372                            "state %d", gcp->gc_cs->state); 
     373                        AP_DEBUG_ASSERT(0); 
     374                } 
     375                gcdmpm_process_socket(gcp); 
     376        }); 
     377 
     378        gcp->gc_write_source = dispatch_source_create( 
     379            DISPATCH_SOURCE_TYPE_WRITE, fd, 0, gcp->gc_queue); 
     380        dispatch_source_set_event_handler(gcp->gc_write_source, ^{ 
     381                gcdmpm_process_socket(gcp); 
     382        }); 
     383 
     384        gcp->gc_timer_source = dispatch_source_create( 
     385            DISPATCH_SOURCE_TYPE_TIMER, 0, 0, gcp->gc_queue); 
     386        dispatch_source_set_event_handler(gcp->gc_timer_source, ^{ 
     387                gcp->gc_cs->state = CONN_STATE_LINGER; 
     388                gcdmpm_process_socket(gcp); 
     389        }); 
     390 
     391        /* 
     392         * Continue setup asynchronously in gcdmpm_process_socket(). 
     393         */ 
     394        dispatch_group_async(gcdmpm_connection_group, gcp->gc_queue, ^{ 
     395                gcdmpm_process_socket(gcp); 
     396        }); 
     397        dispatch_resume(gcp->gc_queue); 
     398} 
     399 
     400/* 
     401 * Implement connection accept for an accept-ready socket; block if all slots 
     402 * are in use. 
     403 */ 
     404static void 
     405gcdmpm_server_accept(ap_listen_rec *lr) 
     406{ 
     407        apr_allocator_t *allocator; 
     408        apr_pool_t *ptrans; 
     409        apr_socket_t *sock; 
     410        apr_status_t rv; 
     411 
     412        /* 
     413         * XXXGCD: This is a non-interruptible wait, and may block a listener 
     414         * thread waiting for a worker thread to complete during shutdown. 
     415         */ 
     416        dispatch_semaphore_wait(gcdmpm_connection_sema, DISPATCH_TIME_FOREVER); 
     417 
     418        apr_allocator_create(&allocator); 
     419        apr_allocator_max_free_set(allocator, ap_max_mem_free); 
     420        apr_pool_create_ex(&ptrans, pconf, NULL, allocator); 
     421        apr_allocator_owner_set(allocator, ptrans); 
     422        apr_pool_tag(ptrans, "transaction"); 
     423 
     424        rv = lr->accept_func((void **)&sock, lr, ptrans); 
     425        AP_DEBUG_ASSERT(rv == APR_SUCCESS || sock == NULL); 
     426        if (sock == NULL) { 
     427                /* XXXRW: does ptrans need to be freed here? */ 
     428                return; 
     429        } 
     430 
     431        gcdmpm_newconn(sock, ptrans); 
     432} 
     433 
     434/* 
     435 * Create two queues: a signal queue, and concurrent listen queue.  A 
     436 * separate signal queue prevents the concurrency bound on the listen queue 
     437 * from blocking signal delivery. 
     438 */ 
     439static void 
     440gcdmpm_setup_queues(void) 
     441{ 
     442 
     443        /* Serialized signal queue. */ 
     444        gcdmpm_signals_queue = dispatch_queue_create("gcdmpm_signals", NULL); 
     445 
     446        /* Concurrent listener queue. */ 
     447        gcdmpm_listen_queue = dispatch_queue_create("gcdmpm_listen", NULL); 
     448        dispatch_queue_set_width(gcdmpm_listen_queue, listener_count); 
     449} 
     450 
     451/* 
     452 * Set up signal handlers for SIGHUP, SIGTERM, and their GRACEFUL variants. 
     453 */ 
     454static void 
     455gcdmpm_setup_signals(void) 
     456{ 
     457 
     458        apr_signal(SIGPIPE, SIG_IGN); 
     459 
     460        apr_signal(SIGHUP, SIG_IGN); 
     461        ds_sighup = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, 
     462            SIGHUP, 0, gcdmpm_signals_queue); 
     463        dispatch_source_set_event_handler(ds_sighup, ^{ 
     464                mpm_state = AP_MPMQ_STOPPING; 
     465                is_graceful = 0; 
     466                restart_pending = 1; 
     467                dispatch_semaphore_signal(gcdmpm_signalfired_sema); 
     468        }); 
     469        dispatch_resume(ds_sighup); 
     470 
     471        apr_signal(AP_SIG_GRACEFUL, SIG_IGN); 
     472        ds_sig_graceful = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, 
     473            AP_SIG_GRACEFUL, 0, gcdmpm_signals_queue); 
     474        dispatch_source_set_event_handler(ds_sig_graceful, ^{ 
     475                mpm_state = AP_MPMQ_STOPPING; 
     476                is_graceful = 1; 
     477                restart_pending = 1; 
     478                dispatch_semaphore_signal(gcdmpm_signalfired_sema); 
     479        }); 
     480        dispatch_resume(ds_sig_graceful); 
     481 
     482        apr_signal(SIGTERM, SIG_IGN); 
     483        ds_sigterm = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, 
     484            SIGTERM, 0, gcdmpm_signals_queue); 
     485        dispatch_source_set_event_handler(ds_sigterm, ^{ 
     486                mpm_state = AP_MPMQ_STOPPING; 
     487                is_graceful = 0; 
     488                shutdown_pending = 1; 
     489                dispatch_semaphore_signal(gcdmpm_signalfired_sema); 
     490        }); 
     491        dispatch_resume(ds_sigterm); 
     492 
     493        apr_signal(AP_SIG_GRACEFUL_STOP, SIG_IGN); 
     494        ds_sig_graceful_stop = dispatch_source_create( 
     495            DISPATCH_SOURCE_TYPE_SIGNAL, AP_SIG_GRACEFUL_STOP, 0, 
     496            gcdmpm_signals_queue); 
     497        dispatch_source_set_event_handler(ds_sig_graceful_stop, ^{ 
     498                mpm_state = AP_MPMQ_STOPPING; 
     499                is_graceful = 1; 
     500                shutdown_pending = 1; 
     501                dispatch_semaphore_signal(gcdmpm_signalfired_sema); 
     502        }); 
     503        dispatch_resume(ds_sig_graceful_stop); 
     504} 
     505 
     506/* 
     507 * Create connection group and semaphore. 
     508 */ 
     509static void 
     510gcdmpm_setup_connections(void) 
     511{ 
     512 
     513        gcdmpm_connection_sema = 
     514            dispatch_semaphore_create(gcdmpm_connection_limit); 
     515        gcdmpm_connection_group = dispatch_group_create(); 
     516} 
     517 
     518/* 
     519 * Cancel signal registration. 
     520 */ 
     521static void 
     522gcdmpm_cleanup_signals(void) 
     523{ 
     524 
     525        dispatch_source_cancel(ds_sighup); 
     526        dispatch_release(ds_sighup); 
     527 
     528        dispatch_source_cancel(ds_sigterm); 
     529        dispatch_release(ds_sigterm); 
     530 
     531        dispatch_source_cancel(ds_sig_graceful); 
     532        dispatch_release(ds_sig_graceful); 
     533 
     534        dispatch_source_cancel(ds_sig_graceful_stop); 
     535        dispatch_release(ds_sig_graceful_stop); 
     536} 
     537 
     538/* 
     539 * Set up a dispatch source for each listening socket. 
     540 */ 
     541static void 
     542gcdmpm_setup_listeners(void) 
     543{ 
     544        ap_listen_rec *lr; 
     545        u_int i; 
     546        int fd; 
     547 
     548        ds_listeners = malloc(listener_count * sizeof(*ds_listeners)); 
     549        for (lr = ap_listeners, i = 0; lr != NULL; lr = lr->next, i++) { 
     550                lr->accept_func = ap_unixd_accept; 
     551                apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1); 
     552                apr_os_sock_get(&fd, lr->sd); 
     553                ds_listeners[i] = dispatch_source_create( 
     554                    DISPATCH_SOURCE_TYPE_READ, fd, 0, gcdmpm_listen_queue); 
     555                dispatch_source_set_event_handler(ds_listeners[i], ^{ 
     556                        gcdmpm_server_accept(lr); 
     557                }); 
     558                dispatch_resume(ds_listeners[i]); 
     559        } 
     560} 
     561 
     562/* 
     563 * Cancel listeners. 
     564 */ 
     565static void 
     566gcdmpm_cleanup_listeners(void) 
     567{ 
     568        u_int i; 
     569 
     570        for (i = 0; i < listener_count; i++) { 
     571                dispatch_source_cancel(ds_listeners[i]); 
     572                dispatch_release(ds_listeners[i]); 
     573        } 
     574        free(ds_listeners); 
     575} 
     576 
     577/* 
     578 * Wait for any in-progress connections to terminate. 
     579 */ 
     580static void 
     581gcdmpm_cleanup_connections(void) 
     582{ 
     583 
     584        dispatch_group_wait(gcdmpm_connection_group, DISPATCH_TIME_FOREVER); 
     585        dispatch_release(gcdmpm_connection_group); 
     586        dispatch_release(gcdmpm_connection_sema); 
     587} 
     588 
     589/* 
     590 * Wait for listeners and the signal queue to terminate. 
     591 */ 
     592static void 
     593gcdmpm_cleanup_queues(void) 
     594{ 
     595 
     596        dispatch_sync(gcdmpm_listen_queue, ^{}); 
     597        dispatch_release(gcdmpm_listen_queue); 
     598 
     599        dispatch_sync(gcdmpm_signals_queue, ^{}); 
     600        dispatch_release(gcdmpm_signals_queue); 
     601} 
     602 
     603/* 
     604 * This is the main run loop of the GCD MPM. 
     605 */ 
     606apr_status_t 
     607gcdmpm_child_run(void) 
     608{ 
     609        apr_status_t rv; 
     610 
     611        rv = ap_run_drop_privileges(pconf, ap_server_conf); 
     612        if (rv) 
     613                return (APEXIT_CHILDFATAL); 
     614 
     615        restart_pending = shutdown_pending = 0; 
     616        mpm_state = AP_MPMQ_RUNNING; 
     617 
     618        while (!shutdown_pending) { 
     619                gcdmpm_signalfired_sema = dispatch_semaphore_create(0); 
     620                gcdmpm_setup_queues(); 
     621                gcdmpm_setup_signals(); 
     622                gcdmpm_setup_connections(); 
     623 
     624                ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, 
     625                    "%s configured -- resuming normal operations", 
     626                    ap_get_server_description()); 
     627                ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, 
     628                    "Server built: %s", ap_get_server_built()); 
     629 
     630                gcdmpm_setup_listeners(); 
     631 
     632                /* Block until restart/shutdown signal received. */ 
     633                dispatch_semaphore_wait(gcdmpm_signalfired_sema, 
     634                    DISPATCH_TIME_FOREVER); 
     635 
     636                /* On ungraceful restart/shutdown, just exit. */ 
     637                if (!is_graceful) 
     638                        break; 
     639 
     640                gcdmpm_cleanup_listeners(); 
     641                gcdmpm_cleanup_connections(); 
     642                gcdmpm_cleanup_signals(); 
     643                gcdmpm_cleanup_queues(); 
     644                dispatch_release(gcdmpm_signalfired_sema); 
     645        } 
     646        return (0); 
     647} 
  • modules/arch/unix/config5.m4

    Property changes on: server/mpm/gcd/gcd.c
    ___________________________________________________________________
    Added: svn:mime-type
       + text/plain
    Added: svn:keywords
       + FreeBSD=%H
    Added: svn:eol-style
       + native
    
     
    44if ap_mpm_is_enabled "simple" \ 
    55   || ap_mpm_is_enabled "worker" \ 
    66   || ap_mpm_is_enabled "event" \ 
     7   || ap_mpm_is_enabled "gcd" \ 
    78   || ap_mpm_is_enabled "prefork"; then 
    89    unixd_mods_enable=yes 
    910else