689b9c8de2945e241230471bf1920f2a29aaa74b
[lhc/web/wiklou.git] / includes / ForkController.php
1 <?php
2
3 /**
4 * Class for managing forking command line scripts.
5 * Currently just does forking and process control, but it could easily be extended
6 * to provide IPC and job dispatch.
7 */
8 class ForkController {
9 var $children = array();
10 var $termReceived = false;
11
12 public function __construct() {
13 if ( php_sapi_name() != 'cli' ) {
14 throw new MWException( "MultiProcess cannot be used from the web." );
15 }
16 }
17
18 protected function prepareEnvironment() {
19 global $wgCaches, $wgMemc;
20 // Don't share DB or memcached connections
21 wfGetLBFactory()->destroyInstance();
22 $wgCaches = array();
23 unset( $wgMemc );
24 }
25
26 /**
27 * Fork a number of worker processes.
28 *
29 * This should only be called from the command line. It should be called
30 * as early as possible during execution. It will return 'child' in the
31 * child processes and 'parent' in the parent process. The parent process
32 * should then call monitor().
33 *
34 * This function requires the posix and pcntl extensions.
35 */
36 public function forkWorkers( $numProcs ) {
37 global $wgMemc, $wgCaches, $wgMainCacheType;
38
39 $this->prepareEnvironment();
40
41 // Create the child processes
42 for ( $i = 0; $i < $numProcs; $i++ ) {
43 // Do the fork
44 $pid = pcntl_fork();
45 if ( $pid === -1 || $pid === false ) {
46 echo "Error creating child processes\n";
47 exit( 1 );
48 }
49
50 if ( !$pid ) {
51 $this->initChild();
52 $this->children = null;
53 return 'child';
54 } else {
55 // This is the parent process
56 $this->children[$pid] = true;
57 }
58 }
59
60 return 'parent';
61 }
62
63 /**
64 * The parent process main loop
65 */
66 public function runParent() {
67 // Trap SIGTERM
68 pcntl_signal( SIGTERM, array( $this, 'handleTermSignal' ), false );
69
70 do {
71 $status = false;
72 $deadPid = pcntl_wait( $status );
73
74 if ( $deadPid > 0 ) {
75 unset( $this->children[$deadPid] );
76 }
77
78 // Run signal handlers
79 if ( function_exists( 'pcntl_signal_dispatch' ) ) {
80 pcntl_signal_dispatch();
81 } else {
82 declare (ticks=1) { $status = $status; }
83 }
84 // Respond to TERM signal
85 if ( $this->termReceived ) {
86 foreach ( $this->children as $childPid => $unused ) {
87 posix_kill( $childPid, SIGTERM );
88 }
89 $this->termReceived = false;
90 }
91 } while ( count( $this->children ) );
92 pcntl_signal( SIGTERM, SIG_DFL );
93 }
94
95 protected function initChild() {
96 global $wgMemc, $wgMainCacheType;
97 $wgMemc = wfGetCache( $wgMainCacheType );
98 }
99
100 protected function handleTermSignal( $signal ) {
101 $this->termReceived = true;
102 }
103 }