MediaWiki
REL1_24
|
00001 <?php 00032 class ForkController { 00033 protected $children = array(), $childNumber = 0; 00034 protected $termReceived = false; 00035 protected $flags = 0, $procsToStart = 0; 00036 00037 protected 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 != '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 ) { 00125 $status = $status; 00126 } 00127 } 00128 // Respond to TERM signal 00129 if ( $this->termReceived ) { 00130 foreach ( $this->children as $childPid => $unused ) { 00131 posix_kill( $childPid, SIGTERM ); 00132 } 00133 $this->termReceived = false; 00134 } 00135 } while ( count( $this->children ) ); 00136 pcntl_signal( SIGTERM, SIG_DFL ); 00137 return 'done'; 00138 } 00139 00146 public function getChildNumber() { 00147 return $this->childNumber; 00148 } 00149 00150 protected function prepareEnvironment() { 00151 global $wgMemc; 00152 // Don't share DB, storage, or memcached connections 00153 wfGetLBFactory()->destroyInstance(); 00154 FileBackendGroup::destroySingleton(); 00155 LockManagerGroup::destroySingletons(); 00156 ObjectCache::clear(); 00157 $wgMemc = null; 00158 } 00159 00166 protected function forkWorkers( $numProcs ) { 00167 $this->prepareEnvironment(); 00168 00169 // Create the child processes 00170 for ( $i = 0; $i < $numProcs; $i++ ) { 00171 // Do the fork 00172 $pid = pcntl_fork(); 00173 if ( $pid === -1 || $pid === false ) { 00174 echo "Error creating child processes\n"; 00175 exit( 1 ); 00176 } 00177 00178 if ( !$pid ) { 00179 $this->initChild(); 00180 $this->childNumber = $i; 00181 return 'child'; 00182 } else { 00183 // This is the parent process 00184 $this->children[$pid] = true; 00185 } 00186 } 00187 00188 return 'parent'; 00189 } 00190 00191 protected function initChild() { 00192 global $wgMemc, $wgMainCacheType; 00193 $wgMemc = wfGetCache( $wgMainCacheType ); 00194 $this->children = null; 00195 pcntl_signal( SIGTERM, SIG_DFL ); 00196 } 00197 00198 protected function handleTermSignal( $signal ) { 00199 $this->termReceived = true; 00200 } 00201 }