From: Aaron Schulz Date: Tue, 27 Mar 2012 18:45:48 +0000 (-0700) Subject: [FileBackend] Moved scoped timeout stuff to its own file. Also removed some commented... X-Git-Tag: 1.31.0-rc.0~24110 X-Git-Url: http://git.cyclocoop.org/%22%20.%20generer_url_ecrire%28%22articles%22%2C%22id_article=%24id_article%22%29%20.%20%22?a=commitdiff_plain;h=74e089667c3a7a8291943661168d974cae9d45a7;p=lhc%2Fweb%2Fwiklou.git [FileBackend] Moved scoped timeout stuff to its own file. Also removed some commented-out code (such a call would belong in LocalFile more so than here). Change-Id: I0f0b7fe1a0f1da3fffe57c58b9f4d5a77d97cd29 --- diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 44ed532483..301c024406 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -195,6 +195,7 @@ $wgAutoloadLocalClasses = array( 'RevisionList' => 'includes/RevisionList.php', 'RSSFeed' => 'includes/Feed.php', 'Sanitizer' => 'includes/Sanitizer.php', + 'ScopedPHPTimeout' => 'includes/ScopedPHPTimeout.php', 'SiteConfiguration' => 'includes/SiteConfiguration.php', 'SiteStats' => 'includes/SiteStats.php', 'SiteStatsInit' => 'includes/SiteStats.php', @@ -521,7 +522,6 @@ $wgAutoloadLocalClasses = array( 'MySqlLockManager'=> 'includes/filerepo/backend/lockmanager/DBLockManager.php', 'NullLockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php', 'FileOp' => 'includes/filerepo/backend/FileOp.php', - 'FileOpScopedPHPTimeout' => 'includes/filerepo/backend/FileOp.php', 'StoreFileOp' => 'includes/filerepo/backend/FileOp.php', 'CopyFileOp' => 'includes/filerepo/backend/FileOp.php', 'MoveFileOp' => 'includes/filerepo/backend/FileOp.php', diff --git a/includes/ScopedPHPTimeout.php b/includes/ScopedPHPTimeout.php new file mode 100644 index 0000000000..97c6bbfc9b --- /dev/null +++ b/includes/ScopedPHPTimeout.php @@ -0,0 +1,58 @@ + 0 ) { // CLI uses 0 + if ( self::$totalCalls >= self::MAX_TOTAL_CALLS ) { + trigger_error( "Maximum invocations of " . __CLASS__ . " exceeded." ); + } elseif ( self::$totalElapsed >= self::MAX_TOTAL_TIME ) { + trigger_error( "Time limit within invocations of " . __CLASS__ . " exceeded." ); + } elseif ( self::$stackDepth > 0 ) { // recursion guard + trigger_error( "Resursive invocation of " . __CLASS__ . " attempted." ); + } else { + $this->oldTimeout = ini_set( 'max_execution_time', $seconds ); + $this->startTime = microtime( true ); + ++self::$stackDepth; + ++self::$totalCalls; // proof against < 1us scopes + } + } + } + + /** + * Restore the original timeout. + * This does not account for the timer value on __construct(). + */ + public function __destruct() { + if ( $this->oldTimeout ) { + $elapsed = microtime( true ) - $this->startTime; + // Note: a limit of 0 is treated as "forever" + set_time_limit( max( 1, $this->oldTimeout - (int)$elapsed ) ); + // If each scoped timeout is for less than one second, we end up + // restoring the original timeout without any decrease in value. + // Thus web scripts in an infinite loop can run forever unless we + // take some measures to prevent this. Track total time and calls. + self::$totalElapsed += $elapsed; + --self::$stackDepth; + } + } +} diff --git a/includes/filerepo/backend/FileOp.php b/includes/filerepo/backend/FileOp.php index 6cee9f9a85..ebc6b4c403 100644 --- a/includes/filerepo/backend/FileOp.php +++ b/includes/filerepo/backend/FileOp.php @@ -155,12 +155,6 @@ abstract class FileOp { $status->setResult( true, $status->value ); } - // Restart PHP's execution timer and set the timeout to safe amount. - // This handles cases where the operations take a long time or where we are - // already running low on time left. The old timeout is restored afterwards. - # @TODO: re-enable this for when the number of batches is high. - #$scopedTimeLimit = new FileOpScopedPHPTimeout( self::TIME_LIMIT_SEC ); - // Attempt each operation... foreach ( $performOps as $index => $fileOp ) { if ( $fileOp->failed() ) { @@ -432,63 +426,6 @@ abstract class FileOp { } } -/** - * FileOp helper class to expand PHP execution time for a function. - * On construction, set_time_limit() is called and set to $seconds. - * When the object goes out of scope, the timer is restarted, with - * the original time limit minus the time the object existed. - */ -class FileOpScopedPHPTimeout { - protected $startTime; // float; seconds - protected $oldTimeout; // integer; seconds - - protected static $stackDepth = 0; // integer - protected static $totalCalls = 0; // integer - protected static $totalElapsed = 0; // float; seconds - - /* Prevent callers in infinite loops from running forever */ - const MAX_TOTAL_CALLS = 1000000; - const MAX_TOTAL_TIME = 300; // seconds - - /** - * @param $seconds integer - */ - public function __construct( $seconds ) { - if ( ini_get( 'max_execution_time' ) > 0 ) { // CLI uses 0 - if ( self::$totalCalls >= self::MAX_TOTAL_CALLS ) { - trigger_error( "Maximum invocations of " . __CLASS__ . " exceeded." ); - } elseif ( self::$totalElapsed >= self::MAX_TOTAL_TIME ) { - trigger_error( "Time limit within invocations of " . __CLASS__ . " exceeded." ); - } elseif ( self::$stackDepth > 0 ) { // recursion guard - trigger_error( "Resursive invocation of " . __CLASS__ . " attempted." ); - } else { - $this->oldTimeout = ini_set( 'max_execution_time', $seconds ); - $this->startTime = microtime( true ); - ++self::$stackDepth; - ++self::$totalCalls; // proof against < 1us scopes - } - } - } - - /** - * Restore the original timeout. - * This does not account for the timer value on __construct(). - */ - public function __destruct() { - if ( $this->oldTimeout ) { - $elapsed = microtime( true ) - $this->startTime; - // Note: a limit of 0 is treated as "forever" - set_time_limit( max( 1, $this->oldTimeout - (int)$elapsed ) ); - // If each scoped timeout is for less than one second, we end up - // restoring the original timeout without any decrease in value. - // Thus web scripts in an infinite loop can run forever unless we - // take some measures to prevent this. Track total time and calls. - self::$totalElapsed += $elapsed; - --self::$stackDepth; - } - } -} - /** * Store a file into the backend from a file on the file system. * Parameters similar to FileBackendStore::storeInternal(), which include: