MediaWiki  REL1_20
ForkController.php
Go to the documentation of this file.
00001 <?php
00032 class ForkController {
00033         var $children = array();
00034         var $termReceived = false;
00035         var $flags = 0, $procsToStart = 0;
00036 
00037         static $restartableSignals = array(
00038                 SIGFPE,
00039                 SIGILL,
00040                 SIGSEGV,
00041                 SIGBUS,
00042                 SIGABRT,
00043                 SIGSYS,
00044                 SIGPIPE,
00045                 SIGXCPU,
00046                 SIGXFSZ,
00047         );
00048 
00053         const RESTART_ON_ERROR = 1;
00054 
00055         public function __construct( $numProcs, $flags = 0 ) {
00056                 if ( php_sapi_name() != 'cli' ) {
00057                         throw new MWException( "ForkController cannot be used from the web." );
00058                 }
00059                 $this->procsToStart = $numProcs;
00060                 $this->flags = $flags;
00061         }
00062 
00074         public function start() {
00075                 // Trap SIGTERM
00076                 pcntl_signal( SIGTERM, array( $this, 'handleTermSignal' ), false );
00077 
00078                 do {
00079                         // Start child processes
00080                         if ( $this->procsToStart ) {
00081                                 if ( $this->forkWorkers( $this->procsToStart ) == 'child' ) {
00082                                         return 'child';
00083                                 }
00084                                 $this->procsToStart = 0;
00085                         }
00086 
00087                         // Check child status
00088                         $status = false;
00089                         $deadPid = pcntl_wait( $status );
00090 
00091                         if ( $deadPid > 0 ) {
00092                                 // Respond to child process termination
00093                                 unset( $this->children[$deadPid] );
00094                                 if ( $this->flags & self::RESTART_ON_ERROR ) {
00095                                         if ( pcntl_wifsignaled( $status ) ) {
00096                                                 // Restart if the signal was abnormal termination
00097                                                 // Don't restart if it was deliberately killed
00098                                                 $signal = pcntl_wtermsig( $status );
00099                                                 if ( in_array( $signal, self::$restartableSignals ) ) {
00100                                                         echo "Worker exited with signal $signal, restarting\n";
00101                                                         $this->procsToStart++;
00102                                                 }
00103                                         } elseif ( pcntl_wifexited( $status ) ) {
00104                                                 // Restart on non-zero exit status
00105                                                 $exitStatus = pcntl_wexitstatus( $status );
00106                                                 if ( $exitStatus != 0 ) {
00107                                                         echo "Worker exited with status $exitStatus, restarting\n";
00108                                                         $this->procsToStart++;
00109                                                 } else {
00110                                                         echo "Worker exited normally\n";
00111                                                 }
00112                                         }
00113                                 }
00114                                 // Throttle restarts
00115                                 if ( $this->procsToStart ) {
00116                                         usleep( 500000 );
00117                                 }
00118                         }
00119 
00120                         // Run signal handlers
00121                         if ( function_exists( 'pcntl_signal_dispatch' ) ) {
00122                                 pcntl_signal_dispatch();
00123                         } else {
00124                                 declare (ticks=1) { $status = $status; }
00125                         }
00126                         // Respond to TERM signal
00127                         if ( $this->termReceived ) {
00128                                 foreach ( $this->children as $childPid => $unused ) {
00129                                         posix_kill( $childPid, SIGTERM );
00130                                 }
00131                                 $this->termReceived = false;
00132                         }
00133                 } while ( count( $this->children ) );
00134                 pcntl_signal( SIGTERM, SIG_DFL );
00135                 return 'done';
00136         }
00137 
00138         protected function prepareEnvironment() {
00139                 global $wgMemc;
00140                 // Don't share DB, storage, or memcached connections
00141                 wfGetLBFactory()->destroyInstance();
00142                 FileBackendGroup::destroySingleton();
00143                 LockManagerGroup::destroySingleton();
00144                 ObjectCache::clear();
00145                 $wgMemc = null;
00146         }
00147 
00153         protected function forkWorkers( $numProcs ) {
00154                 $this->prepareEnvironment();
00155 
00156                 // Create the child processes
00157                 for ( $i = 0; $i < $numProcs; $i++ ) {
00158                         // Do the fork
00159                         $pid = pcntl_fork();
00160                         if ( $pid === -1 || $pid === false ) {
00161                                 echo "Error creating child processes\n";
00162                                 exit( 1 );
00163                         }
00164 
00165                         if ( !$pid ) {
00166                                 $this->initChild();
00167                                 return 'child';
00168                         } else {
00169                                 // This is the parent process
00170                                 $this->children[$pid] = true;
00171                         }
00172                 }
00173 
00174                 return 'parent';
00175         }
00176 
00177         protected function initChild() {
00178                 global $wgMemc, $wgMainCacheType;
00179                 $wgMemc = wfGetCache( $wgMainCacheType );
00180                 $this->children = null;
00181                 pcntl_signal( SIGTERM, SIG_DFL );
00182         }
00183 
00184         protected function handleTermSignal( $signal ) {
00185                 $this->termReceived = true;
00186         }
00187 }