MediaWiki
REL1_20
|
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 }