MediaWiki REL1_34
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
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();
157 JobQueueGroup::destroySingletons();
158 ObjectCache::clear();
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() {
197 $this->children = null;
198 pcntl_signal( SIGTERM, SIG_DFL );
199 }
200
201 protected function handleTermSignal( $signal ) {
202 $this->termReceived = true;
203 }
204}
$wgMainCacheType
Main cache type.
wfGetCache( $cacheType)
Get a specific cache object.
wfIsCLI()
Check if we are running from the commandline.
$wgMemc
Definition Setup.php:790
static destroySingleton()
Destroy the singleton instance.
Class for managing forking command line scripts.
__construct( $numProcs, $flags=0)
getChildNumber()
Get the number of the child currently running.
start()
Start the child processes.
forkWorkers( $numProcs)
Fork a number of worker processes.
const RESTART_ON_ERROR
Pass this flag to __construct() to cause the class to automatically restart workers that exit with no...
static $restartableSignals
handleTermSignal( $signal)
MediaWiki exception.
MediaWikiServices is the service locator for the application scope of MediaWiki.
static destroySingletons()
Destroy all singleton() instances.