MediaWiki  master
ForkController.php
Go to the documentation of this file.
1 <?php
23 
34  protected $children = [], $childNumber = 0;
35  protected $termReceived = false;
36  protected $flags = 0, $procsToStart = 0;
37 
38  protected static $restartableSignals = [
39  SIGFPE,
40  SIGILL,
41  SIGSEGV,
42  SIGBUS,
43  SIGABRT,
44  SIGSYS,
45  SIGPIPE,
46  SIGXCPU,
47  SIGXFSZ,
48  ];
49 
54  private const RESTART_ON_ERROR = 1;
55 
56  public function __construct( $numProcs, $flags = 0 ) {
57  if ( !wfIsCLI() ) {
58  throw new MWException( "ForkController cannot be used from the web." );
59  }
60  $this->procsToStart = $numProcs;
61  $this->flags = $flags;
62  }
63 
75  public function start() {
76  // Trap SIGTERM
77  pcntl_signal( SIGTERM, [ $this, 'handleTermSignal' ], false );
78 
79  do {
80  // Start child processes
81  if ( $this->procsToStart ) {
82  if ( $this->forkWorkers( $this->procsToStart ) == 'child' ) {
83  return 'child';
84  }
85  $this->procsToStart = 0;
86  }
87 
88  // Check child status
89  $status = false;
90  $deadPid = pcntl_wait( $status );
91 
92  if ( $deadPid > 0 ) {
93  // Respond to child process termination
94  unset( $this->children[$deadPid] );
95  if ( $this->flags & self::RESTART_ON_ERROR ) {
96  if ( pcntl_wifsignaled( $status ) ) {
97  // Restart if the signal was abnormal termination
98  // Don't restart if it was deliberately killed
99  $signal = pcntl_wtermsig( $status );
100  if ( in_array( $signal, self::$restartableSignals ) ) {
101  echo "Worker exited with signal $signal, restarting\n";
102  $this->procsToStart++;
103  }
104  } elseif ( pcntl_wifexited( $status ) ) {
105  // Restart on non-zero exit status
106  $exitStatus = pcntl_wexitstatus( $status );
107  if ( $exitStatus != 0 ) {
108  echo "Worker exited with status $exitStatus, restarting\n";
109  $this->procsToStart++;
110  } else {
111  echo "Worker exited normally\n";
112  }
113  }
114  }
115  // Throttle restarts
116  if ( $this->procsToStart ) {
117  usleep( 500000 );
118  }
119  }
120 
121  // Run signal handlers
122  if ( function_exists( 'pcntl_signal_dispatch' ) ) {
123  pcntl_signal_dispatch();
124  } else {
125  declare( ticks = 1 ) {
126  // @phan-suppress-next-line PhanPluginDuplicateExpressionAssignment
127  $status = $status;
128  }
129  }
130  // Respond to TERM signal
131  if ( $this->termReceived ) {
132  foreach ( $this->children as $childPid => $unused ) {
133  posix_kill( $childPid, SIGTERM );
134  }
135  $this->termReceived = false;
136  }
137  } while ( count( $this->children ) );
138  pcntl_signal( SIGTERM, SIG_DFL );
139  return 'done';
140  }
141 
148  public function getChildNumber() {
149  return $this->childNumber;
150  }
151 
152  protected function prepareEnvironment() {
153  // Don't share DB, storage, or memcached connections
154  MediaWikiServices::resetChildProcessServices();
158  }
159 
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  $this->children = null;
193  pcntl_signal( SIGTERM, SIG_DFL );
194  }
195 
196  protected function handleTermSignal( $signal ) {
197  $this->termReceived = true;
198  }
199 }
ObjectCache\clear
static clear()
Clear all the cached instances.
Definition: ObjectCache.php:281
ForkController\RESTART_ON_ERROR
const RESTART_ON_ERROR
Pass this flag to __construct() to cause the class to automatically restart workers that exit with no...
Definition: ForkController.php:54
ForkController
Class for managing forking command line scripts.
Definition: ForkController.php:33
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:152
ForkController\handleTermSignal
handleTermSignal( $signal)
Definition: ForkController.php:196
JobQueueGroup\destroySingletons
static destroySingletons()
Destroy the singleton instances.
Definition: JobQueueGroup.php:98
RedisConnectionPool\destroySingletons
static destroySingletons()
Destroy all singleton() instances.
Definition: RedisConnectionPool.php:166
ForkController\start
start()
Start the child processes.
Definition: ForkController.php:75
ForkController\getChildNumber
getChildNumber()
Get the number of the child currently running.
Definition: ForkController.php:148
ForkController\$termReceived
$termReceived
Definition: ForkController.php:35
ForkController\forkWorkers
forkWorkers( $numProcs)
Fork a number of worker processes.
Definition: ForkController.php:166
MWException
MediaWiki exception.
Definition: MWException.php:26
ForkController\$childNumber
$childNumber
Definition: ForkController.php:34
wfIsCLI
wfIsCLI()
Check if we are running from the commandline.
Definition: GlobalFunctions.php:1859
ForkController\prepareEnvironment
prepareEnvironment()
Definition: ForkController.php:152
ForkController\$procsToStart
$procsToStart
Definition: ForkController.php:36
ForkController\$flags
$flags
Definition: ForkController.php:36
ForkController\$restartableSignals
static $restartableSignals
Definition: ForkController.php:38
ForkController\$children
$children
Definition: ForkController.php:34
ForkController\__construct
__construct( $numProcs, $flags=0)
Definition: ForkController.php:56
ForkController\initChild
initChild()
Definition: ForkController.php:191