static int worker_run()

in server/mpm/worker/worker.c [1779:2071]


static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
    ap_listen_rec **listen_buckets = NULL;
    int num_buckets = retained->mpm->num_buckets;
    int remaining_children_to_start;
    apr_status_t rv;
    char id[16];
    int i;

    ap_log_pid(pconf, ap_pid_fname);

    /* On first startup create gen_pool to satisfy the lifetime of the
     * parent's PODs, accept mutexes and listeners; on restart stop the
     * children from the previous generation and clear gen_pool for the
     * next one.
     */
    if (!retained->gen_pool) {
        apr_pool_create(&retained->gen_pool, ap_pglobal);
    }
    else {
        if (retained->mpm->was_graceful) {
            /* wake up the children...time to die.  But we'll have more soon */
            for (i = 0; i < num_buckets; i++) {
                ap_mpm_podx_killpg(retained->buckets[i].pod,
                                   ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
            }
        }
        else {
            /* Kill 'em all.  Since the child acts the same on the parents SIGTERM
             * and a SIGHUP, we may as well use the same signal, because some user
             * pthreads are stealing signals from us left and right.
             */
            for (i = 0; i < num_buckets; i++) {
                ap_mpm_podx_killpg(retained->buckets[i].pod,
                                   ap_daemons_limit, AP_MPM_PODX_RESTART);
            }
            ap_reclaim_child_processes(1, /* Start with SIGTERM */
                                       worker_note_child_killed);
        }
        apr_pool_clear(retained->gen_pool);
        retained->buckets = NULL;

        /* advance to the next generation */
        /* XXX: we really need to make sure this new generation number isn't in
         * use by any of the previous children.
         */
        ++retained->mpm->my_generation;
    }

    /* On graceful restart, preserve the scoreboard and the listeners buckets.
     * When ungraceful, clear the scoreboard and set num_buckets to zero to let
     * ap_duplicate_listeners() below determine how many are needed/configured.
     */
    if (!retained->mpm->was_graceful) {
        if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
            retained->mpm->mpm_state = AP_MPMQ_STOPPING;
            return !OK;
        }
        num_buckets = (one_process) ? 1 : 0; /* one_process => one bucket */
        retained->mpm->num_buckets = 0; /* reset idle_spawn_rate below */
    }

    /* Now on for the new generation. */
    ap_scoreboard_image->global->running_generation = retained->mpm->my_generation;
    ap_unixd_mpm_set_signals(pconf, one_process);

    if ((rv = ap_duplicate_listeners(retained->gen_pool, ap_server_conf,
                                     &listen_buckets, &num_buckets))) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
                     ap_server_conf, APLOGNO(03291)
                     "could not duplicate listeners");
        return !OK;
    }

    retained->buckets = apr_pcalloc(retained->gen_pool,
                                    num_buckets * sizeof(*retained->buckets));
    for (i = 0; i < num_buckets; i++) {
        if (!one_process /* no POD in one_process mode */
                && (rv = ap_mpm_podx_open(retained->gen_pool,
                                          &retained->buckets[i].pod))) {
            ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
                         ap_server_conf, APLOGNO(03292)
                         "could not open pipe-of-death");
            return !OK;
        }
        /* Initialize cross-process accept lock (safe accept needed only) */
        if ((rv = SAFE_ACCEPT((apr_snprintf(id, sizeof id, "%i", i),
                               ap_proc_mutex_create(&retained->buckets[i].mutex,
                                                    NULL, AP_ACCEPT_MUTEX_TYPE,
                                                    id, s, retained->gen_pool,
                                                    0))))) {
            ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
                         ap_server_conf, APLOGNO(03293)
                         "could not create accept mutex");
            return !OK;
        }
        retained->buckets[i].listeners = listen_buckets[i];
    }

    if (retained->mpm->max_buckets < num_buckets) {
        int new_max, *new_ptr;
        new_max = retained->mpm->max_buckets * 2;
        if (new_max < num_buckets) {
            new_max = num_buckets;
        }
        new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
        if (retained->mpm->num_buckets) /* idle_spawn_rate NULL at startup */
            memcpy(new_ptr, retained->idle_spawn_rate,
                   retained->mpm->num_buckets * sizeof(int));
        retained->idle_spawn_rate = new_ptr;
        retained->mpm->max_buckets = new_max;
    }
    if (retained->mpm->num_buckets < num_buckets) {
        int rate_max = 1;
        /* If new buckets are added, set their idle spawn rate to
         * the highest so far, so that they get filled as quickly
         * as the existing ones.
         */
        for (i = 0; i < retained->mpm->num_buckets; i++) {
            if (rate_max < retained->idle_spawn_rate[i]) {
                rate_max = retained->idle_spawn_rate[i];
            }
        }
        for (/* up to date i */; i < num_buckets; i++) {
            retained->idle_spawn_rate[i] = rate_max;
        }
    }
    retained->mpm->num_buckets = num_buckets;

    /* Don't thrash since num_buckets depends on the
     * system and the number of online CPU cores...
     */
    if (ap_daemons_limit < num_buckets)
        ap_daemons_limit = num_buckets;
    if (ap_daemons_to_start < num_buckets)
        ap_daemons_to_start = num_buckets;
    /* We want to create as much children at a time as the number of buckets,
     * so to optimally accept connections (evenly distributed across buckets).
     * Thus min_spare_threads should at least maintain num_buckets children,
     * and max_spare_threads allow num_buckets more children w/o triggering
     * immediately (e.g. num_buckets idle threads margin, one per bucket).
     */
    if (min_spare_threads < threads_per_child * (num_buckets - 1) + num_buckets)
        min_spare_threads = threads_per_child * (num_buckets - 1) + num_buckets;
    if (max_spare_threads < min_spare_threads + (threads_per_child + 1) * num_buckets)
        max_spare_threads = min_spare_threads + (threads_per_child + 1) * num_buckets;

    max_spawn_rate_per_bucket = (MAX_SPAWN_RATE + num_buckets - 1) / num_buckets;
    if (max_spawn_rate_per_bucket < 1) {
        max_spawn_rate_per_bucket = 1;
    }

    /* If we're doing a graceful_restart then we're going to see a lot
     * of children exiting immediately when we get into the main loop
     * below (because we just sent them AP_SIG_GRACEFUL).  This happens pretty
     * rapidly... and for each one that exits we may start a new one, until
     * there are at least min_spare_threads idle threads, counting across
     * all children.  But we may be permitted to start more children than
     * that, so we'll just keep track of how many we're
     * supposed to start up without the 1 second penalty between each fork.
     */
    remaining_children_to_start = ap_daemons_to_start;
    if (remaining_children_to_start > ap_daemons_limit) {
        remaining_children_to_start = ap_daemons_limit;
    }
    if (!retained->mpm->was_graceful) {
        startup_children(remaining_children_to_start);
        remaining_children_to_start = 0;
    }
    else {
        /* give the system some time to recover before kicking into
            * exponential mode */
        retained->hold_off_on_exponential_spawning = 10;
    }

    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00292)
                "%s configured -- resuming normal operations",
                ap_get_server_description());
    ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00293)
                "Server built: %s", ap_get_server_built());
    ap_log_command_line(plog, s);
    ap_log_mpm_common(s);
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00294)
                "Accept mutex: %s (default: %s)",
                (retained->buckets[0].mutex)
                    ? apr_proc_mutex_name(retained->buckets[0].mutex)
                    : "none",
                apr_proc_mutex_defname());
    retained->mpm->mpm_state = AP_MPMQ_RUNNING;

    server_main_loop(remaining_children_to_start);
    retained->mpm->mpm_state = AP_MPMQ_STOPPING;

    if (retained->mpm->shutdown_pending && retained->mpm->is_ungraceful) {
        /* Time to shut down:
         * Kill child processes, tell them to call child_exit, etc...
         */
        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(retained->buckets[i].pod,
                               ap_daemons_limit, AP_MPM_PODX_RESTART);
        }
        ap_reclaim_child_processes(1, /* Start with SIGTERM */
                                   worker_note_child_killed);

        if (!child_fatal) {
            /* cleanup pid file on normal shutdown */
            ap_remove_pid(pconf, ap_pid_fname);
            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0,
                         ap_server_conf, APLOGNO(00295) "caught SIGTERM, shutting down");
        }
        return DONE;
    }

    if (retained->mpm->shutdown_pending) {
        /* Time to gracefully shut down:
         * Kill child processes, tell them to call child_exit, etc...
         */
        int active_children;
        int index;
        apr_time_t cutoff = 0;

        /* Close our listeners, and then ask our children to do same */
        ap_close_listeners();

        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(retained->buckets[i].pod,
                               ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
        }
        ap_relieve_child_processes(worker_note_child_killed);

        if (!child_fatal) {
            /* cleanup pid file on normal shutdown */
            ap_remove_pid(pconf, ap_pid_fname);
            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00296)
                         "caught " AP_SIG_GRACEFUL_STOP_STRING
                         ", shutting down gracefully");
        }

        if (ap_graceful_shutdown_timeout) {
            cutoff = apr_time_now() +
                     apr_time_from_sec(ap_graceful_shutdown_timeout);
        }

        /* Don't really exit until each child has finished */
        retained->mpm->shutdown_pending = 0;
        do {
            /* Pause for a second */
            apr_sleep(apr_time_from_sec(1));

            /* Relieve any children which have now exited */
            ap_relieve_child_processes(worker_note_child_killed);

            active_children = 0;
            for (index = 0; index < ap_daemons_limit; ++index) {
                if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) {
                    active_children = 1;
                    /* Having just one child is enough to stay around */
                    break;
                }
            }
        } while (!retained->mpm->shutdown_pending && active_children &&
                 (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff));

        /* We might be here because we received SIGTERM, either
         * way, try and make sure that all of our processes are
         * really dead.
         */
        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(retained->buckets[i].pod,
                               ap_daemons_limit, AP_MPM_PODX_RESTART);
        }
        ap_reclaim_child_processes(1, worker_note_child_killed);

        return DONE;
    }

    /* we've been told to restart */
    if (one_process) {
        /* not worth thinking about */
        return DONE;
    }

    if (!retained->mpm->is_ungraceful) {
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00297)
                     "%s received.  Doing graceful restart",
                     AP_SIG_GRACEFUL_STRING);
    }
    else {
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00298)
                     "SIGHUP received.  Attempting to restart");
    }
    return OK;
}