Back to index

php5  5.3.10
Defines | Functions | Variables
fpm_process_ctl.c File Reference
#include "fpm_config.h"
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include "fpm.h"
#include "fpm_clock.h"
#include "fpm_children.h"
#include "fpm_signals.h"
#include "fpm_events.h"
#include "fpm_process_ctl.h"
#include "fpm_cleanup.h"
#include "fpm_request.h"
#include "fpm_worker_pool.h"
#include "fpm_scoreboard.h"
#include "fpm_sockets.h"
#include "zlog.h"

Go to the source code of this file.

Defines

#define optional_arg(c)   (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")

Functions

static void fpm_pctl_cleanup (int which, void *arg)
static void fpm_pctl_action (struct fpm_event_s *ev, short which, void *arg)
static int fpm_pctl_timeout_set (int sec)
static void fpm_pctl_exit ()
static void fpm_pctl_exec ()
static void fpm_pctl_action_last ()
int fpm_pctl_kill (pid_t pid, int how)
void fpm_pctl_kill_all (int signo)
static void fpm_pctl_action_next ()
void fpm_pctl (int new_state, int action)
int fpm_pctl_can_spawn_children ()
int fpm_pctl_child_exited ()
int fpm_pctl_init_main ()
static void fpm_pctl_check_request_timeout (struct timeval *now)
static void fpm_pctl_perform_idle_server_maintenance (struct timeval *now)
void fpm_pctl_heartbeat (struct fpm_event_s *ev, short which, void *arg)
void fpm_pctl_perform_idle_server_maintenance_heartbeat (struct fpm_event_s *ev, short which, void *arg)
void fpm_pctl_on_socket_accept (struct fpm_event_s *ev, short which, void *arg)

Variables

static int fpm_state = FPM_PCTL_STATE_NORMAL
static int fpm_signal_sent = 0
static const char * fpm_state_names []
static int saved_argc
static char ** saved_argv
static struct fpm_event_s

Define Documentation

#define optional_arg (   c)    (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")

Definition at line 78 of file fpm_process_ctl.c.


Function Documentation

void fpm_pctl ( int  new_state,
int  action 
)

Definition at line 203 of file fpm_process_ctl.c.

{
       switch (action) {
              case FPM_PCTL_ACTION_SET :
                     if (fpm_state == new_state) { /* already in progress - just ignore duplicate signal */
                            return;
                     }

                     switch (fpm_state) { /* check which states can be overridden */
                            case FPM_PCTL_STATE_NORMAL :
                                   /* 'normal' can be overridden by any other state */
                                   break;
                            case FPM_PCTL_STATE_RELOADING :
                                   /* 'reloading' can be overridden by 'finishing' */
                                   if (new_state == FPM_PCTL_STATE_FINISHING) break;
                            case FPM_PCTL_STATE_FINISHING :
                                   /* 'reloading' and 'finishing' can be overridden by 'terminating' */
                                   if (new_state == FPM_PCTL_STATE_TERMINATING) break;
                            case FPM_PCTL_STATE_TERMINATING :
                                   /* nothing can override 'terminating' state */
                                   zlog(ZLOG_DEBUG, "not switching to '%s' state, because already in '%s' state",
                                          fpm_state_names[new_state], fpm_state_names[fpm_state]);
                                   return;
                     }

                     fpm_signal_sent = 0;
                     fpm_state = new_state;

                     zlog(ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]);
                     /* fall down */

              case FPM_PCTL_ACTION_TIMEOUT :
                     fpm_pctl_action_next();
                     break;
              case FPM_PCTL_ACTION_LAST_CHILD_EXITED :
                     fpm_pctl_action_last();
                     break;

       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void fpm_pctl_action ( struct fpm_event_s ev,
short  which,
void *  arg 
) [static]

Definition at line 54 of file fpm_process_ctl.c.

Here is the call graph for this function:

Here is the caller graph for this function:

static void fpm_pctl_action_last ( ) [static]

Definition at line 107 of file fpm_process_ctl.c.

Here is the call graph for this function:

Here is the caller graph for this function:

static void fpm_pctl_action_next ( ) [static]

Definition at line 173 of file fpm_process_ctl.c.

{
       int sig, timeout;

       if (!fpm_globals.running_children) {
              fpm_pctl_action_last();
       }

       if (fpm_signal_sent == 0) {
              if (fpm_state == FPM_PCTL_STATE_TERMINATING) {
                     sig = SIGTERM;
              } else {
                     sig = SIGQUIT;
              }
              timeout = fpm_global_config.process_control_timeout;
       } else {
              if (fpm_signal_sent == SIGQUIT) {
                     sig = SIGTERM;
              } else {
                     sig = SIGKILL;
              }
              timeout = 1;
       }

       fpm_pctl_kill_all(sig);
       fpm_signal_sent = sig;
       fpm_pctl_timeout_set(timeout);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 245 of file fpm_process_ctl.c.

Here is the caller graph for this function:

static void fpm_pctl_check_request_timeout ( struct timeval *  now) [static]

Definition at line 292 of file fpm_process_ctl.c.

{
       struct fpm_worker_pool_s *wp;

       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
              int terminate_timeout = wp->config->request_terminate_timeout;
              int slowlog_timeout = wp->config->request_slowlog_timeout;
              struct fpm_child_s *child;

              if (terminate_timeout || slowlog_timeout) {
                     for (child = wp->children; child; child = child->next) {
                            fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
                     }
              }
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 251 of file fpm_process_ctl.c.

{
       if (fpm_state == FPM_PCTL_STATE_NORMAL) {
              return 0;
       }

       if (!fpm_globals.running_children) {
              fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED);
       }
       return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void fpm_pctl_cleanup ( int  which,
void *  arg 
) [static]

Definition at line 40 of file fpm_process_ctl.c.

{
       int i;
       if (which != FPM_CLEANUP_PARENT_EXEC) {
              for (i = 0; i < saved_argc; i++) {
                     free(saved_argv[i]);
              }
              free(saved_argv);
       }
}

Here is the caller graph for this function:

static void fpm_pctl_exec ( ) [static]

Definition at line 80 of file fpm_process_ctl.c.

{

       zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
                     "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
                     "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
              "})",
              saved_argv[0], saved_argv[0],
              optional_arg(1),
              optional_arg(2),
              optional_arg(3),
              optional_arg(4),
              optional_arg(5),
              optional_arg(6),
              optional_arg(7),
              optional_arg(8),
              optional_arg(9),
              optional_arg(10)
       );

       fpm_cleanups_run(FPM_CLEANUP_PARENT_EXEC);
       execvp(saved_argv[0], saved_argv);
       zlog(ZLOG_SYSERROR, "failed to reload: execvp() failed");
       exit(1);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void fpm_pctl_exit ( ) [static]

Definition at line 68 of file fpm_process_ctl.c.

Here is the call graph for this function:

Here is the caller graph for this function:

void fpm_pctl_heartbeat ( struct fpm_event_s ev,
short  which,
void *  arg 
)

Definition at line 441 of file fpm_process_ctl.c.

{
       static struct fpm_event_s heartbeat;
       struct timeval now;

       if (fpm_globals.parent_pid != getpid()) {
              return; /* sanity check */
       }

       if (which == FPM_EV_TIMEOUT) {
              fpm_clock_get(&now);
              fpm_pctl_check_request_timeout(&now);
              return;
       }

       /* ensure heartbeat is not lower than FPM_PCTL_MIN_HEARTBEAT */
       fpm_globals.heartbeat = MAX(fpm_globals.heartbeat, FPM_PCTL_MIN_HEARTBEAT);

       /* first call without setting to initialize the timer */
       zlog(ZLOG_DEBUG, "heartbeat have been set up with a timeout of %dms", fpm_globals.heartbeat);
       fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_heartbeat, NULL);
       fpm_event_add(&heartbeat, fpm_globals.heartbeat);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 264 of file fpm_process_ctl.c.

{
       int i;

       saved_argc = fpm_globals.argc;
       saved_argv = malloc(sizeof(char *) * (saved_argc + 1));

       if (!saved_argv) {
              return -1;
       }

       for (i = 0; i < saved_argc; i++) {
              saved_argv[i] = strdup(fpm_globals.argv[i]);

              if (!saved_argv[i]) {
                     return -1;
              }
       }

       saved_argv[i] = 0;

       if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_pctl_cleanup, 0)) {
              return -1;
       }
       return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int fpm_pctl_kill ( pid_t  pid,
int  how 
)

Definition at line 122 of file fpm_process_ctl.c.

{
       int s = 0;

       switch (how) {
              case FPM_PCTL_TERM :
                     s = SIGTERM;
                     break;
              case FPM_PCTL_STOP :
                     s = SIGSTOP;
                     break;
              case FPM_PCTL_CONT :
                     s = SIGCONT;
                     break;
              case FPM_PCTL_QUIT :
                     s = SIGQUIT;
                     break;
              default :
                     break;
       }
       return kill(pid, s);
}

Here is the caller graph for this function:

void fpm_pctl_kill_all ( int  signo)

Definition at line 146 of file fpm_process_ctl.c.

{
       struct fpm_worker_pool_s *wp;
       int alive_children = 0;

       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
              struct fpm_child_s *child;

              for (child = wp->children; child; child = child->next) {
                     int res = kill(child->pid, signo);

                     zlog(ZLOG_DEBUG, "[pool %s] sending signal %d %s to child %d",
                            child->wp->config->name, signo,
                            fpm_signal_names[signo] ? fpm_signal_names[signo] : "", (int) child->pid);

                     if (res == 0) {
                            ++alive_children;
                     }
              }
       }

       if (alive_children) {
              zlog(ZLOG_DEBUG, "%d child(ren) still alive", alive_children);
       }
}

Here is the caller graph for this function:

void fpm_pctl_on_socket_accept ( struct fpm_event_s ev,
short  which,
void *  arg 
)

Definition at line 496 of file fpm_process_ctl.c.

{
       struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg;
       struct fpm_child_s *child;


       if (fpm_globals.parent_pid != getpid()) {
              /* prevent a event race condition when child process
               * have not set up its own event loop */
              return;
       }

       wp->socket_event_set = 0;

/*     zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/

       if (wp->running_children >= wp->config->pm_max_children) {
              if (!wp->warn_max_children) {
                     fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
                     zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
                     wp->warn_max_children = 1;
              }

              return;
       }

       for (child = wp->children; child; child = child->next) {
              /* if there is at least on idle child, it will handle the connection, stop here */
              if (fpm_request_is_idle(child)) {
                     return;
              }
       }

       wp->warn_max_children = 0;
       fpm_children_make(wp, 1, 1, 1);

       if (fpm_globals.is_child) {
              return;
       }

       zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void fpm_pctl_perform_idle_server_maintenance ( struct timeval *  now) [static]

Definition at line 310 of file fpm_process_ctl.c.

{
       struct fpm_worker_pool_s *wp;

       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
              struct fpm_child_s *child;
              struct fpm_child_s *last_idle_child = NULL;
              int idle = 0;
              int active = 0;
              int children_to_fork;
              unsigned cur_lq = 0;

              if (wp->config == NULL) continue;

              for (child = wp->children; child; child = child->next) {
                     if (fpm_request_is_idle(child)) {
                            if (last_idle_child == NULL) {
                                   last_idle_child = child;
                            } else {
                                   if (timercmp(&child->started, &last_idle_child->started, <)) {
                                          last_idle_child = child;
                                   }
                            }
                            idle++;
                     } else {
                            active++;
                     }
              }

              /* update status structure for all PMs */
              if (wp->listen_address_domain == FPM_AF_INET) {
                     if (0 > fpm_socket_get_listening_queue(wp->listening_socket, &cur_lq, NULL)) {
                            cur_lq = 0;
#if 0
                     } else {
                            if (cur_lq > 0) {
                                   if (!wp->warn_lq) {
                                          zlog(ZLOG_WARNING, "[pool %s] listening queue is not empty, #%d requests are waiting to be served, consider raising pm.max_children setting (%d)", wp->config->name, cur_lq, wp->config->pm_max_children);
                                          wp->warn_lq = 1;
                                   }
                            } else {
                                   wp->warn_lq = 0;
                            }
#endif
                     }
              }
              fpm_scoreboard_update(idle, active, cur_lq, -1, -1, -1, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);

              /* this is specific to PM_STYLE_ONDEMAND */
              if (wp->config->pm == PM_STYLE_ONDEMAND) {
                     struct timeval last, now;

                     zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle);

                     if (!last_idle_child) continue;

                     fpm_request_last_activity(last_idle_child, &last);
                     fpm_clock_get(&now);
                     if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
                            last_idle_child->idle_kill = 1;
                            fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
                     }

                     continue;
              }

              /* the rest is only used by PM_STYLE_DYNAMIC */
              if (wp->config->pm != PM_STYLE_DYNAMIC) continue;

              zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children, %d running children. Spawning rate %d", wp->config->name, active, idle, wp->running_children, wp->idle_spawn_rate);

              if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
                     last_idle_child->idle_kill = 1;
                     fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
                     wp->idle_spawn_rate = 1;
                     continue;
              }

              if (idle < wp->config->pm_min_spare_servers) {
                     if (wp->running_children >= wp->config->pm_max_children) {
                            if (!wp->warn_max_children) {
                                   fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
                                   zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
                                   wp->warn_max_children = 1;
                            }
                            wp->idle_spawn_rate = 1;
                            continue;
                     }

                     if (wp->idle_spawn_rate >= 8) {
                            zlog(ZLOG_WARNING, "[pool %s] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning %d children, there are %d idle, and %d total children", wp->config->name, wp->idle_spawn_rate, idle, wp->running_children);
                     }

                     /* compute the number of idle process to spawn */
                     children_to_fork = MIN(wp->idle_spawn_rate, wp->config->pm_min_spare_servers - idle);

                     /* get sure it won't exceed max_children */
                     children_to_fork = MIN(children_to_fork, wp->config->pm_max_children - wp->running_children);
                     if (children_to_fork <= 0) {
                            if (!wp->warn_max_children) {
                                   fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
                                   zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
                                   wp->warn_max_children = 1;
                            }
                            wp->idle_spawn_rate = 1;
                            continue;
                     }
                     wp->warn_max_children = 0;

                     fpm_children_make(wp, 1, children_to_fork, 1);

                     /* if it's a child, stop here without creating the next event
                      * this event is reserved to the master process
                      */
                     if (fpm_globals.is_child) {
                            return;
                     }

                     zlog(ZLOG_DEBUG, "[pool %s] %d child(ren) have been created dynamically", wp->config->name, children_to_fork);  

                     /* Double the spawn rate for the next iteration */
                     if (wp->idle_spawn_rate < FPM_MAX_SPAWN_RATE) {
                            wp->idle_spawn_rate *= 2;
                     }
                     continue;
              }
              wp->idle_spawn_rate = 1;
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void fpm_pctl_perform_idle_server_maintenance_heartbeat ( struct fpm_event_s ev,
short  which,
void *  arg 
)

Definition at line 466 of file fpm_process_ctl.c.

{
       static struct fpm_event_s heartbeat;
       struct timeval now;

       if (fpm_globals.parent_pid != getpid()) {
              return; /* sanity check */
       }

       if (which == FPM_EV_TIMEOUT) {
              fpm_clock_get(&now);
              if (fpm_pctl_can_spawn_children()) {
                     fpm_pctl_perform_idle_server_maintenance(&now);

                     /* if it's a child, stop here without creating the next event
                      * this event is reserved to the master process
                      */
                     if (fpm_globals.is_child) {
                            return;
                     }
              }
              return;
       }

       /* first call without setting which to initialize the timer */
       fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_perform_idle_server_maintenance_heartbeat, NULL);
       fpm_event_add(&heartbeat, FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int fpm_pctl_timeout_set ( int  sec) [static]

Definition at line 60 of file fpm_process_ctl.c.

{
       fpm_event_set_timer(&pctl_event, 0, &fpm_pctl_action, NULL);
       fpm_event_add(&pctl_event, sec * 1000);
       return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

struct fpm_event_s [static]

Definition at line 52 of file fpm_process_ctl.c.

int fpm_signal_sent = 0 [static]

Definition at line 27 of file fpm_process_ctl.c.

Definition at line 26 of file fpm_process_ctl.c.

const char* fpm_state_names[] [static]
Initial value:
 {
       [FPM_PCTL_STATE_NORMAL] = "normal",
       [FPM_PCTL_STATE_RELOADING] = "reloading",
       [FPM_PCTL_STATE_TERMINATING] = "terminating",
       [FPM_PCTL_STATE_FINISHING] = "finishing"
}

Definition at line 30 of file fpm_process_ctl.c.

int saved_argc [static]

Definition at line 37 of file fpm_process_ctl.c.

char** saved_argv [static]

Definition at line 38 of file fpm_process_ctl.c.