'FileDependency' => 'includes/CacheDependency.php',
'FileRevertForm' => 'includes/FileRevertForm.php',
'FileStore' => 'includes/FileStore.php',
+ 'ForkController' => 'includes/ForkController.php',
'FormatExif' => 'includes/Exif.php',
'FormOptions' => 'includes/FormOptions.php',
'FSException' => 'includes/FileStore.php',
--- /dev/null
+<?php
+
+/**
+ * Class for managing forking command line scripts.
+ * Currently just does forking and process control, but it could easily be extended
+ * to provide IPC and job dispatch.
+ */
+class ForkController {
+ var $children = array();
+ var $termReceived = false;
+
+ public function __construct() {
+ if ( php_sapi_name() != 'cli' ) {
+ throw new MWException( "MultiProcess cannot be used from the web." );
+ }
+ }
+
+ protected function prepareEnvironment() {
+ global $wgCaches, $wgMemc;
+ // Don't share DB or memcached connections
+ wfGetLBFactory()->destroyInstance();
+ $wgCaches = array();
+ unset( $wgMemc );
+ }
+
+ /**
+ * Fork a number of worker processes.
+ *
+ * This should only be called from the command line. It should be called
+ * as early as possible during execution. It will return 'child' in the
+ * child processes and 'parent' in the parent process. The parent process
+ * should then call monitor().
+ *
+ * This function requires the posix and pcntl extensions.
+ */
+ public function forkWorkers( $numProcs ) {
+ global $wgMemc, $wgCaches, $wgMainCacheType;
+
+ $this->prepareEnvironment();
+
+ // Create the child processes
+ for ( $i = 0; $i < $numProcs; $i++ ) {
+ // Do the fork
+ $pid = pcntl_fork();
+ if ( $pid === -1 || $pid === false ) {
+ echo "Error creating child processes\n";
+ exit( 1 );
+ }
+
+ if ( !$pid ) {
+ $this->initChild();
+ $this->children = null;
+ return 'child';
+ } else {
+ // This is the parent process
+ $this->children[$pid] = true;
+ }
+ }
+
+ return 'parent';
+ }
+
+ /**
+ * The parent process main loop
+ */
+ public function runParent() {
+ // Trap SIGTERM
+ pcntl_signal( SIGTERM, array( $this, 'handleTermSignal' ), false );
+
+ do {
+ $status = false;
+ $deadPid = pcntl_wait( $status );
+
+ if ( $deadPid > 0 ) {
+ unset( $this->children[$deadPid] );
+ }
+
+ // Run signal handlers
+ if ( function_exists( 'pcntl_signal_dispatch' ) ) {
+ pcntl_signal_dispatch();
+ } else {
+ declare (ticks=1) { $status = $status; }
+ }
+ // Respond to TERM signal
+ if ( $this->termReceived ) {
+ foreach ( $this->children as $childPid => $unused ) {
+ posix_kill( $childPid, SIGTERM );
+ }
+ $this->termReceived = false;
+ }
+ } while ( count( $this->children ) );
+ pcntl_signal( SIGTERM, SIG_DFL );
+ }
+
+ protected function initChild() {
+ global $wgMemc, $wgMainCacheType;
+ $wgMemc = wfGetCache( $wgMainCacheType );
+ }
+
+ protected function handleTermSignal( $signal ) {
+ $this->termReceived = true;
+ }
+}
$client = new Net_Gearman_Client( $args );
$dbr = wfGetDB( DB_SLAVE );
-$res = $dbr->select( 'page', array( 'page_namespace', 'page_title' ), false,
- __METHOD__, array( 'LIMIT' => 2 ) );
+$res = $dbr->select( 'page', array( 'page_namespace', 'page_title' ), false, __METHOD__ );
foreach ( $res as $row ) {
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$params = array(
<?php
-$optionsWithArgs = array( 'fake-job' );
+$optionsWithArgs = array( 'fake-job', 'procs' );
require( dirname(__FILE__).'/../commandLine.inc' );
require( dirname(__FILE__).'/gearman.inc' );
+if ( isset( $options['procs'] ) ) {
+ $procs = $options['procs'];
+ if ( $procs < 1 || $procs > 1000 ) {
+ echo "Invalid number of processes, please specify a number between 1 and 1000\n";
+ exit( 1 );
+ }
+ $fc = new ForkController;
+ if ( $fc->forkWorkers( $procs ) == 'parent' ) {
+ $fc->runParent();
+ exit( 0 );
+ }
+}
+
if ( !$args ) {
$args = array( 'localhost' );
}
echo "Invalid argument to --procs\n";
exit( 1 );
}
-
- // Don't share DB or memcached connections
- $lb = wfGetLB();
- $lb->closeAll();
- $wgCaches = array();
- unset( $wgMemc );
-
- // Create the child processes
- $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] = true;
- }
- 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; }
- }
- if ( $deadPid > 0 ) {
- unset( $children[$deadPid] );
- }
- // Respond to TERM signal
- if ( $termReceived ) {
- foreach ( $children as $childPid => $unused ) {
- posix_kill( $childPid, SIGTERM );
- }
- $termReceived = false;
- }
- } while ( count( $children ) );
- // All done
+ $fc = new ForkController;
+ if ( $fc->forkWorkers( $procs ) == 'parent' ) {
+ $fc->runParent();
exit( 0 );
}
-
- // Set up this child
- $wgMemc = wfGetCache( $wgMainCacheType );
}
if ( isset( $options['maxjobs'] ) ) {
wfDebugLog( 'runJobs', $msg );
}
-function handleTermSignal( $signal ) {
- $GLOBALS['termReceived'] = true;
-}