MediaWiki REL1_35
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();
155 JobQueueGroup::destroySingletons();
156 ObjectCache::clear();
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}
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.
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.