MediaWiki REL1_37
ForkController.php
Go to the documentation of this file.
1<?php
23
35 protected $children = [];
36
38 protected $childNumber = 0;
39
41 protected $termReceived = false;
42
44 protected $flags = 0;
45
47 protected $procsToStart = 0;
48
49 protected static $RESTARTABLE_SIGNALS = [];
50
55 private const RESTART_ON_ERROR = 1;
56
61 public function __construct( $numProcs, $flags = 0 ) {
62 if ( !wfIsCLI() ) {
63 throw new MWException( "ForkController cannot be used from the web." );
64 } elseif ( !extension_loaded( 'pcntl' ) ) {
65 throw new MWException( 'ForkController requires pcntl extension to be installed.' );
66 } elseif ( !extension_loaded( 'posix' ) ) {
67 throw new MWException( 'ForkController requires posix extension to be installed.' );
68 }
69 $this->procsToStart = $numProcs;
70 $this->flags = $flags;
71
72 // Define this only after confirming PCNTL support
73 self::$RESTARTABLE_SIGNALS = [
74 SIGFPE, SIGILL, SIGSEGV, SIGBUS,
75 SIGABRT, SIGSYS, SIGPIPE, SIGXCPU,SIGXFSZ,
76 ];
77 }
78
90 public function start() {
91 // Trap SIGTERM
92 pcntl_signal( SIGTERM, [ $this, 'handleTermSignal' ], false );
93
94 do {
95 // Start child processes
96 if ( $this->procsToStart ) {
97 if ( $this->forkWorkers( $this->procsToStart ) == 'child' ) {
98 return 'child';
99 }
100 $this->procsToStart = 0;
101 }
102
103 // Check child status
104 $status = false;
105 $deadPid = pcntl_wait( $status );
106
107 if ( $deadPid > 0 ) {
108 // Respond to child process termination
109 unset( $this->children[$deadPid] );
110 if ( $this->flags & self::RESTART_ON_ERROR ) {
111 if ( pcntl_wifsignaled( $status ) ) {
112 // Restart if the signal was abnormal termination
113 // Don't restart if it was deliberately killed
114 $signal = pcntl_wtermsig( $status );
115 if ( in_array( $signal, self::$RESTARTABLE_SIGNALS ) ) {
116 echo "Worker exited with signal $signal, restarting\n";
117 $this->procsToStart++;
118 }
119 } elseif ( pcntl_wifexited( $status ) ) {
120 // Restart on non-zero exit status
121 $exitStatus = pcntl_wexitstatus( $status );
122 if ( $exitStatus != 0 ) {
123 echo "Worker exited with status $exitStatus, restarting\n";
124 $this->procsToStart++;
125 } else {
126 echo "Worker exited normally\n";
127 }
128 }
129 }
130 // Throttle restarts
131 if ( $this->procsToStart ) {
132 usleep( 500000 );
133 }
134 }
135
136 // Run signal handlers
137 if ( function_exists( 'pcntl_signal_dispatch' ) ) {
138 pcntl_signal_dispatch();
139 } else {
140 declare( ticks = 1 ) {
141 // @phan-suppress-next-line PhanPluginDuplicateExpressionAssignment
142 $status = $status;
143 }
144 }
145 // Respond to TERM signal
146 if ( $this->termReceived ) {
147 foreach ( $this->children as $childPid => $unused ) {
148 posix_kill( $childPid, SIGTERM );
149 }
150 $this->termReceived = false;
151 }
152 } while ( count( $this->children ) );
153 pcntl_signal( SIGTERM, SIG_DFL );
154 return 'done';
155 }
156
163 public function getChildNumber() {
164 return $this->childNumber;
165 }
166
167 protected function prepareEnvironment() {
168 // Don't share DB, storage, or memcached connections
169 MediaWikiServices::resetChildProcessServices();
170 JobQueueGroup::destroySingletons();
171 ObjectCache::clear();
173 }
174
181 protected function forkWorkers( $numProcs ) {
182 $this->prepareEnvironment();
183
184 // Create the child processes
185 for ( $i = 0; $i < $numProcs; $i++ ) {
186 // Do the fork
187 $pid = pcntl_fork();
188 if ( $pid === -1 || $pid === false ) {
189 echo "Error creating child processes\n";
190 exit( 1 );
191 }
192
193 if ( !$pid ) {
194 $this->initChild();
195 $this->childNumber = $i;
196 return 'child';
197 } else {
198 // This is the parent process
199 $this->children[$pid] = true;
200 }
201 }
202
203 return 'parent';
204 }
205
206 protected function initChild() {
207 $this->children = null;
208 pcntl_signal( SIGTERM, SIG_DFL );
209 }
210
211 protected function handleTermSignal( $signal ) {
212 $this->termReceived = true;
213 }
214}
wfIsCLI()
Check if we are running from the commandline.
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.
array null $children
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 $RESTARTABLE_SIGNALS
handleTermSignal( $signal)
MediaWiki exception.
MediaWikiServices is the service locator for the application scope of MediaWiki.
static destroySingletons()
Destroy all singleton() instances.