MediaWiki  1.34.0
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  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  global $wgMemc;
154  // Don't share DB, storage, or memcached connections
155  MediaWikiServices::resetChildProcessServices();
160  $wgMemc = null;
161  }
162 
169  protected function forkWorkers( $numProcs ) {
170  $this->prepareEnvironment();
171 
172  // Create the child processes
173  for ( $i = 0; $i < $numProcs; $i++ ) {
174  // Do the fork
175  $pid = pcntl_fork();
176  if ( $pid === -1 || $pid === false ) {
177  echo "Error creating child processes\n";
178  exit( 1 );
179  }
180 
181  if ( !$pid ) {
182  $this->initChild();
183  $this->childNumber = $i;
184  return 'child';
185  } else {
186  // This is the parent process
187  $this->children[$pid] = true;
188  }
189  }
190 
191  return 'parent';
192  }
193 
194  protected function initChild() {
195  global $wgMemc, $wgMainCacheType;
197  $this->children = null;
198  pcntl_signal( SIGTERM, SIG_DFL );
199  }
200 
201  protected function handleTermSignal( $signal ) {
202  $this->termReceived = true;
203  }
204 }
ObjectCache\clear
static clear()
Clear all the cached instances.
Definition: ObjectCache.php:351
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:117
ForkController\handleTermSignal
handleTermSignal( $signal)
Definition: ForkController.php:201
JobQueueGroup\destroySingletons
static destroySingletons()
Destroy the singleton instances.
Definition: JobQueueGroup.php:98
RedisConnectionPool\destroySingletons
static destroySingletons()
Destroy all singleton() instances.
Definition: RedisConnectionPool.php:163
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
$wgMemc
$wgMemc
Definition: Setup.php:791
FileBackendGroup\destroySingleton
static destroySingleton()
Destroy the singleton instance.
Definition: FileBackendGroup.php:58
ForkController\$termReceived
$termReceived
Definition: ForkController.php:35
ForkController\forkWorkers
forkWorkers( $numProcs)
Fork a number of worker processes.
Definition: ForkController.php:169
MWException
MediaWiki exception.
Definition: MWException.php:26
$wgMainCacheType
$wgMainCacheType
Main cache type.
Definition: DefaultSettings.php:2345
wfGetCache
wfGetCache( $cacheType)
Get a specific cache object.
Definition: GlobalFunctions.php:2848
ForkController\$childNumber
$childNumber
Definition: ForkController.php:34
wfIsCLI
wfIsCLI()
Check if we are running from the commandline.
Definition: GlobalFunctions.php:1932
$status
return $status
Definition: SyntaxHighlight.php:347
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:194