[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Class for managing forking command line scripts. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 */ 22 23 /** 24 * Class for managing forking command line scripts. 25 * Currently just does forking and process control, but it could easily be extended 26 * to provide IPC and job dispatch. 27 * 28 * This class requires the posix and pcntl extensions. 29 * 30 * @ingroup Maintenance 31 */ 32 class ForkController { 33 protected $children = array(), $childNumber = 0; 34 protected $termReceived = false; 35 protected $flags = 0, $procsToStart = 0; 36 37 protected static $restartableSignals = array( 38 SIGFPE, 39 SIGILL, 40 SIGSEGV, 41 SIGBUS, 42 SIGABRT, 43 SIGSYS, 44 SIGPIPE, 45 SIGXCPU, 46 SIGXFSZ, 47 ); 48 49 /** 50 * Pass this flag to __construct() to cause the class to automatically restart 51 * workers that exit with non-zero exit status or a signal such as SIGSEGV. 52 */ 53 const RESTART_ON_ERROR = 1; 54 55 public function __construct( $numProcs, $flags = 0 ) { 56 if ( PHP_SAPI != 'cli' ) { 57 throw new MWException( "ForkController cannot be used from the web." ); 58 } 59 $this->procsToStart = $numProcs; 60 $this->flags = $flags; 61 } 62 63 /** 64 * Start the child processes. 65 * 66 * This should only be called from the command line. It should be called 67 * as early as possible during execution. 68 * 69 * This will return 'child' in the child processes. In the parent process, 70 * it will run until all the child processes exit or a TERM signal is 71 * received. It will then return 'done'. 72 * @return string 73 */ 74 public function start() { 75 // Trap SIGTERM 76 pcntl_signal( SIGTERM, array( $this, 'handleTermSignal' ), false ); 77 78 do { 79 // Start child processes 80 if ( $this->procsToStart ) { 81 if ( $this->forkWorkers( $this->procsToStart ) == 'child' ) { 82 return 'child'; 83 } 84 $this->procsToStart = 0; 85 } 86 87 // Check child status 88 $status = false; 89 $deadPid = pcntl_wait( $status ); 90 91 if ( $deadPid > 0 ) { 92 // Respond to child process termination 93 unset( $this->children[$deadPid] ); 94 if ( $this->flags & self::RESTART_ON_ERROR ) { 95 if ( pcntl_wifsignaled( $status ) ) { 96 // Restart if the signal was abnormal termination 97 // Don't restart if it was deliberately killed 98 $signal = pcntl_wtermsig( $status ); 99 if ( in_array( $signal, self::$restartableSignals ) ) { 100 echo "Worker exited with signal $signal, restarting\n"; 101 $this->procsToStart++; 102 } 103 } elseif ( pcntl_wifexited( $status ) ) { 104 // Restart on non-zero exit status 105 $exitStatus = pcntl_wexitstatus( $status ); 106 if ( $exitStatus != 0 ) { 107 echo "Worker exited with status $exitStatus, restarting\n"; 108 $this->procsToStart++; 109 } else { 110 echo "Worker exited normally\n"; 111 } 112 } 113 } 114 // Throttle restarts 115 if ( $this->procsToStart ) { 116 usleep( 500000 ); 117 } 118 } 119 120 // Run signal handlers 121 if ( function_exists( 'pcntl_signal_dispatch' ) ) { 122 pcntl_signal_dispatch(); 123 } else { 124 declare( ticks = 1 ) { 125 $status = $status; 126 } 127 } 128 // Respond to TERM signal 129 if ( $this->termReceived ) { 130 foreach ( $this->children as $childPid => $unused ) { 131 posix_kill( $childPid, SIGTERM ); 132 } 133 $this->termReceived = false; 134 } 135 } while ( count( $this->children ) ); 136 pcntl_signal( SIGTERM, SIG_DFL ); 137 return 'done'; 138 } 139 140 /** 141 * Get the number of the child currently running. Note, this 142 * is not the pid, but rather which of the total number of children 143 * we are 144 * @return int 145 */ 146 public function getChildNumber() { 147 return $this->childNumber; 148 } 149 150 protected function prepareEnvironment() { 151 global $wgMemc; 152 // Don't share DB, storage, or memcached connections 153 wfGetLBFactory()->destroyInstance(); 154 FileBackendGroup::destroySingleton(); 155 LockManagerGroup::destroySingletons(); 156 ObjectCache::clear(); 157 $wgMemc = null; 158 } 159 160 /** 161 * Fork a number of worker processes. 162 * 163 * @param int $numProcs 164 * @return string 165 */ 166 protected function forkWorkers( $numProcs ) { 167 $this->prepareEnvironment(); 168 169 // Create the child processes 170 for ( $i = 0; $i < $numProcs; $i++ ) { 171 // Do the fork 172 $pid = pcntl_fork(); 173 if ( $pid === -1 || $pid === false ) { 174 echo "Error creating child processes\n"; 175 exit( 1 ); 176 } 177 178 if ( !$pid ) { 179 $this->initChild(); 180 $this->childNumber = $i; 181 return 'child'; 182 } else { 183 // This is the parent process 184 $this->children[$pid] = true; 185 } 186 } 187 188 return 'parent'; 189 } 190 191 protected function initChild() { 192 global $wgMemc, $wgMainCacheType; 193 $wgMemc = wfGetCache( $wgMainCacheType ); 194 $this->children = null; 195 pcntl_signal( SIGTERM, SIG_DFL ); 196 } 197 198 protected function handleTermSignal( $signal ) { 199 $this->termReceived = true; 200 } 201 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |