Basic multiprocess operation via pcntl_fork()
authorTim Starling <tstarling@users.mediawiki.org>
Thu, 19 Feb 2009 07:15:19 +0000 (07:15 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Thu, 19 Feb 2009 07:15:19 +0000 (07:15 +0000)
maintenance/runJobs.php

index e2a5895..ee6d0dc 100644 (file)
  * @ingroup Maintenance
  */
 
-$optionsWithArgs = array( 'maxjobs', 'type' );
+$optionsWithArgs = array( 'maxjobs', 'type', 'procs' );
 $wgUseNormalUser = true;
 require_once( 'commandLine.inc' );
 
+if ( isset( $options['procs'] ) ) {
+       $procs = intval( $options['procs'] );
+       if ( $procs < 1 || $procs > 1000 ) {
+               echo "Invalid argument to --procs\n";
+               exit( 1 );
+       }
+
+       // Don't share DB or memcached connections
+       $lb = wfGetLB();
+       $lb->closeAll();
+       $wgCaches = array();
+       unset( $wgMemc );
+
+       // Spawn the children
+       $children = array();
+       for ( $childId = 0; $childId < $procs; $childId++ ) {
+               $pid = pcntl_fork();
+               if ( $pid === -1 || $pid === false ) {
+                       echo "Error creating child processes\n";
+                       exit( 1 );
+               }
+               if ( !$pid ) {
+                       break;
+               }
+
+               $children[] = $pid;
+       }
+       if ( $pid ) {
+               // Parent process
+               // Trap SIGTERM
+               pcntl_signal( SIGTERM, 'handleTermSignal', false );
+               // Wait for a child to exit
+               $status = false;
+               $termReceived = false;
+               do {
+                       $deadPid = pcntl_wait( $status );
+                       // Run signal handlers
+                       if ( function_exists( 'pcntl_signal_dispatch' ) ) {
+                               pcntl_signal_dispatch();
+                       } else {
+                               declare (ticks=1) { $status = $status; } 
+                       }
+               } while ( $deadPid == -1 && !$termReceived );
+               // Kill the remaining children
+               // If they're already dead, say due to SIGTERM, then they'll be zombies until 
+               // pcntl_waitpid() below, so the PID won't be reused.
+               foreach ( $children as $childPid ) {
+                       if ( $childPid != $deadPid ) {
+                               posix_kill( $childPid, SIGTERM );
+                       }
+               }
+               foreach ( $children as $childPid ) {
+                       pcntl_waitpid( $childPid, $status );
+               }
+               // All done
+               exit( 0 );
+       }
+
+       // Set up this child
+       $wgMemc = wfGetCache( $wgMainCacheType );
+}
+
 if ( isset( $options['maxjobs'] ) ) {
        $maxJobs = $options['maxjobs'];
 } else {
@@ -65,3 +127,7 @@ function runJobsLog( $msg ) {
        wfDebugLog( 'runJobs', $msg );
 }
 
+function handleTermSignal( $signal ) {
+       $GLOBALS['termReceived'] = true;
+}
+