MediaWiki  REL1_19
ForkController.php
Go to the documentation of this file.
00001 <?php
00002 
00012 class ForkController {
00013         var $children = array();
00014         var $termReceived = false;
00015         var $flags = 0, $procsToStart = 0;
00016 
00017         static $restartableSignals = array(
00018                 SIGFPE,
00019                 SIGILL,
00020                 SIGSEGV,
00021                 SIGBUS,
00022                 SIGABRT,
00023                 SIGSYS,
00024                 SIGPIPE,
00025                 SIGXCPU,
00026                 SIGXFSZ,
00027         );
00028 
00033         const RESTART_ON_ERROR = 1;
00034 
00035         public function __construct( $numProcs, $flags = 0 ) {
00036                 if ( php_sapi_name() != 'cli' ) {
00037                         throw new MWException( "ForkController cannot be used from the web." );
00038                 }
00039                 $this->procsToStart = $numProcs;
00040                 $this->flags = $flags;
00041         }
00042 
00053         public function start() {
00054                 // Trap SIGTERM
00055                 pcntl_signal( SIGTERM, array( $this, 'handleTermSignal' ), false );
00056 
00057                 do {
00058                         // Start child processes
00059                         if ( $this->procsToStart ) {
00060                                 if ( $this->forkWorkers( $this->procsToStart ) == 'child' ) {
00061                                         return 'child';
00062                                 }
00063                                 $this->procsToStart = 0;
00064                         }
00065 
00066                         // Check child status
00067                         $status = false;
00068                         $deadPid = pcntl_wait( $status );
00069 
00070                         if ( $deadPid > 0 ) {
00071                                 // Respond to child process termination
00072                                 unset( $this->children[$deadPid] );
00073                                 if ( $this->flags & self::RESTART_ON_ERROR ) {
00074                                         if ( pcntl_wifsignaled( $status ) ) {
00075                                                 // Restart if the signal was abnormal termination
00076                                                 // Don't restart if it was deliberately killed
00077                                                 $signal = pcntl_wtermsig( $status );
00078                                                 if ( in_array( $signal, self::$restartableSignals ) ) {
00079                                                         echo "Worker exited with signal $signal, restarting\n";
00080                                                         $this->procsToStart++;
00081                                                 }
00082                                         } elseif ( pcntl_wifexited( $status ) ) {
00083                                                 // Restart on non-zero exit status
00084                                                 $exitStatus = pcntl_wexitstatus( $status );
00085                                                 if ( $exitStatus != 0 ) {
00086                                                         echo "Worker exited with status $exitStatus, restarting\n";
00087                                                         $this->procsToStart++;
00088                                                 } else {
00089                                                         echo "Worker exited normally\n";
00090                                                 }
00091                                         }
00092                                 }
00093                                 // Throttle restarts
00094                                 if ( $this->procsToStart ) {
00095                                         usleep( 500000 );
00096                                 }
00097                         }
00098 
00099                         // Run signal handlers
00100                         if ( function_exists( 'pcntl_signal_dispatch' ) ) {
00101                                 pcntl_signal_dispatch();
00102                         } else {
00103                                 declare (ticks=1) { $status = $status; }
00104                         }
00105                         // Respond to TERM signal
00106                         if ( $this->termReceived ) {
00107                                 foreach ( $this->children as $childPid => $unused ) {
00108                                         posix_kill( $childPid, SIGTERM );
00109                                 }
00110                                 $this->termReceived = false;
00111                         }
00112                 } while ( count( $this->children ) );
00113                 pcntl_signal( SIGTERM, SIG_DFL );
00114                 return 'done';
00115         }
00116 
00117         protected function prepareEnvironment() {
00118                 global $wgMemc;
00119                 // Don't share DB or memcached connections
00120                 wfGetLBFactory()->destroyInstance();
00121                 ObjectCache::clear();
00122                 $wgMemc = null;
00123         }
00124 
00130         protected function forkWorkers( $numProcs ) {
00131                 $this->prepareEnvironment();
00132 
00133                 // Create the child processes
00134                 for ( $i = 0; $i < $numProcs; $i++ ) {
00135                         // Do the fork
00136                         $pid = pcntl_fork();
00137                         if ( $pid === -1 || $pid === false ) {
00138                                 echo "Error creating child processes\n";
00139                                 exit( 1 );
00140                         }
00141 
00142                         if ( !$pid ) {
00143                                 $this->initChild();
00144                                 return 'child';
00145                         } else {
00146                                 // This is the parent process
00147                                 $this->children[$pid] = true;
00148                         }
00149                 }
00150 
00151                 return 'parent';
00152         }
00153 
00154         protected function initChild() {
00155                 global $wgMemc, $wgMainCacheType;
00156                 $wgMemc = wfGetCache( $wgMainCacheType );
00157                 $this->children = null;
00158                 pcntl_signal( SIGTERM, SIG_DFL );
00159         }
00160 
00161         protected function handleTermSignal( $signal ) {
00162                 $this->termReceived = true;
00163         }
00164 }