Use wikimedia/wait-condition-loop
authorKunal Mehta <legoktm@member.fsf.org>
Sat, 1 Oct 2016 07:07:19 +0000 (00:07 -0700)
committerKunal Mehta <legoktm@member.fsf.org>
Sat, 1 Oct 2016 07:25:43 +0000 (00:25 -0700)
Since the WaitConditionLoop class was first introduced in 1.28 (current
master), no back-compat alias is added.

Bug: T146256
Depends-On: Ia84774d83da79fea1e167fe065c69549981f753b
Change-Id: Ibd4f15c87105b8caccbd1f661b74b6efa012b77f

RELEASE-NOTES-1.28
autoload.php
composer.json
includes/libs/WaitConditionLoop.php [deleted file]
includes/libs/lockmanager/LockManager.php
includes/libs/lockmanager/MemcLockManager.php
includes/libs/objectcache/BagOStuff.php
includes/libs/rdbms/ChronologyProtector.php
includes/libs/rdbms/database/DatabasePostgres.php
tests/phpunit/includes/libs/WaitConditionLoopTest.php [deleted file]

index 4f687a1..4051987 100644 (file)
@@ -68,6 +68,7 @@ production.
 
 ==== New external libraries ====
 * Added wikimedia/scoped-callback v1.0.0
+* Added wikimedia/wait-condition-loop v1.0.1
 
 ==== Removed and replaced external libraries ====
 
index 323a01d..e614444 100644 (file)
@@ -1516,7 +1516,6 @@ $wgAutoloadLocalClasses = [
        'VirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTService.php',
        'VirtualRESTServiceClient' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTServiceClient.php',
        'WANObjectCache' => __DIR__ . '/includes/libs/objectcache/WANObjectCache.php',
-       'WaitConditionLoop' => __DIR__ . '/includes/libs/WaitConditionLoop.php',
        'WantedCategoriesPage' => __DIR__ . '/includes/specials/SpecialWantedcategories.php',
        'WantedFilesPage' => __DIR__ . '/includes/specials/SpecialWantedfiles.php',
        'WantedPagesPage' => __DIR__ . '/includes/specials/SpecialWantedpages.php',
index c827c0f..4d71c66 100644 (file)
@@ -41,6 +41,7 @@
                "wikimedia/running-stat": "1.1.0",
                "wikimedia/scoped-callback": "1.0.0",
                "wikimedia/utfnormal": "1.0.3",
+               "wikimedia/wait-condition-loop": "1.0.1",
                "wikimedia/wrappedstring": "2.2.0",
                "zordius/lightncandy": "0.23"
        },
diff --git a/includes/libs/WaitConditionLoop.php b/includes/libs/WaitConditionLoop.php
deleted file mode 100644 (file)
index 969e86e..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-<?php
-/**
- * Wait loop that reaches a condition or times out.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Aaron Schulz
- */
-
-/**
- * Wait loop that reaches a condition or times out
- * @since 1.28
- */
-class WaitConditionLoop {
-       /** @var callable */
-       private $condition;
-       /** @var callable[] */
-       private $busyCallbacks = [];
-       /** @var float Seconds */
-       private $timeout;
-       /** @var float Seconds */
-       private $lastWaitTime;
-       /** @var integer|null */
-       private $rusageMode;
-
-       const CONDITION_REACHED = 1;
-       const CONDITION_CONTINUE = 0; // evaluates as falsey
-       const CONDITION_FAILED = -1;
-       const CONDITION_TIMED_OUT = -2;
-       const CONDITION_ABORTED = -3;
-
-       /**
-        * @param callable $condition Callback that returns a WaitConditionLoop::CONDITION_ constant
-        * @param float $timeout Timeout in seconds
-        * @param array &$busyCallbacks List of callbacks to do useful work (by reference)
-        */
-       public function __construct( callable $condition, $timeout = 5.0, &$busyCallbacks = [] ) {
-               $this->condition = $condition;
-               $this->timeout = $timeout;
-               $this->busyCallbacks =& $busyCallbacks;
-
-               if ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
-                       $this->rusageMode = 2; // RUSAGE_THREAD
-               } elseif ( function_exists( 'getrusage' ) ) {
-                       $this->rusageMode = 0; // RUSAGE_SELF
-               }
-       }
-
-       /**
-        * Invoke the loop and continue until either:
-        *   - a) The condition callback returns neither CONDITION_CONTINUE nor false
-        *   - b) The timeout is reached
-        * This a condition callback can return true (stop) or false (continue) for convenience.
-        * In such cases, the halting result of "true" will be converted to CONDITION_REACHED.
-        *
-        * If $timeout is 0, then only the condition callback will be called (no busy callbacks),
-        * and this will immediately return CONDITION_FAILED if the condition was not met.
-        *
-        * Exceptions in callbacks will be caught and the callback will be swapped with
-        * one that simply rethrows that exception back to the caller when invoked.
-        *
-        * @return integer WaitConditionLoop::CONDITION_* constant
-        * @throws Exception Any error from the condition callback
-        */
-       public function invoke() {
-               $elapsed = 0.0; // seconds
-               $sleepUs = 0; // microseconds to sleep each time
-               $lastCheck = false;
-               $finalResult = self::CONDITION_TIMED_OUT;
-               do {
-                       $checkStartTime = $this->getWallTime();
-                       // Check if the condition is met yet
-                       $realStart = $this->getWallTime();
-                       $cpuStart = $this->getCpuTime();
-                       $checkResult = call_user_func( $this->condition );
-                       $cpu = $this->getCpuTime() - $cpuStart;
-                       $real = $this->getWallTime() - $realStart;
-                       // Exit if the condition is reached, and error occurs, or this is non-blocking
-                       if ( $this->timeout <= 0 ) {
-                               $finalResult = $checkResult ? self::CONDITION_REACHED : self::CONDITION_FAILED;
-                               break;
-                       } elseif ( (int)$checkResult !== self::CONDITION_CONTINUE ) {
-                               if ( is_int( $checkResult ) ) {
-                                       $finalResult = $checkResult;
-                               } else {
-                                       $finalResult = self::CONDITION_REACHED;
-                               }
-                               break;
-                       } elseif ( $lastCheck ) {
-                               break; // timeout reached
-                       }
-                       // Detect if condition callback seems to block or if justs burns CPU
-                       $conditionUsesInterrupts = ( $real > 0.100 && $cpu <= $real * .03 );
-                       if ( !$this->popAndRunBusyCallback() && !$conditionUsesInterrupts ) {
-                               // 10 queries = 10(10+100)/2 ms = 550ms, 14 queries = 1050ms
-                               $sleepUs = min( $sleepUs + 10 * 1e3, 1e6 ); // stop incrementing at ~1s
-                               $this->usleep( $sleepUs );
-                       }
-                       $checkEndTime = $this->getWallTime();
-                       // The max() protects against the clock getting set back
-                       $elapsed += max( $checkEndTime - $checkStartTime, 0.010 );
-                       // Do not let slow callbacks timeout without checking the condition one more time
-                       $lastCheck = ( $elapsed >= $this->timeout );
-               } while ( true );
-
-               $this->lastWaitTime = $elapsed;
-
-               return $finalResult;
-       }
-
-       /**
-        * @return float Seconds
-        */
-       public function getLastWaitTime() {
-               return $this->lastWaitTime;
-       }
-
-       /**
-        * @param integer $microseconds
-        */
-       protected function usleep( $microseconds ) {
-               usleep( $microseconds );
-       }
-
-       /**
-        * @return float
-        */
-       protected function getWallTime() {
-               return microtime( true );
-       }
-
-       /**
-        * @return float Returns 0.0 if not supported (Windows on PHP < 7)
-        */
-       protected function getCpuTime() {
-               if ( $this->rusageMode === null ) {
-                       return microtime( true ); // assume worst case (all time is CPU)
-               }
-
-               $ru = getrusage( $this->rusageMode );
-               $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
-               $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
-
-               return $time;
-       }
-
-       /**
-        * Run one of the callbacks that does work ahead of time for another caller
-        *
-        * @return bool Whether a callback was executed
-        */
-       private function popAndRunBusyCallback() {
-               if ( $this->busyCallbacks ) {
-                       reset( $this->busyCallbacks );
-                       $key = key( $this->busyCallbacks );
-                       /** @var callable $workCallback */
-                       $workCallback =& $this->busyCallbacks[$key];
-                       try {
-                               $workCallback();
-                       } catch ( Exception $e ) {
-                               $workCallback = function () use ( $e ) {
-                                       throw $e;
-                               };
-                       }
-                       unset( $this->busyCallbacks[$key] ); // consume
-
-                       return true;
-               }
-
-               return false;
-       }
-}
index e89a9c7..bee34dc 100644 (file)
@@ -4,6 +4,7 @@
  * @ingroup FileBackend
  */
 use Psr\Log\LoggerInterface;
+use Wikimedia\WaitConditionLoop;
 
 /**
  * Resource locking handling.
index 83334fe..aecdf60 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  * @ingroup LockManager
  */
+use Wikimedia\WaitConditionLoop;
 
 /**
  * Manage locks using memcached servers.
index cd79b67..d3deefb 100644 (file)
@@ -29,6 +29,7 @@
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
+use Wikimedia\WaitConditionLoop;
 
 /**
  * interface is intended to be more or less compatible with
index 1f9aff1..88af1db 100644 (file)
@@ -22,6 +22,7 @@
  */
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
+use Wikimedia\WaitConditionLoop;
 
 /**
  * Class for ensuring a consistent ordering of events as seen by the user, despite replication.
index f58628e..f82d76d 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  * @ingroup Database
  */
+use Wikimedia\WaitConditionLoop;
 
 /**
  * @ingroup Database
diff --git a/tests/phpunit/includes/libs/WaitConditionLoopTest.php b/tests/phpunit/includes/libs/WaitConditionLoopTest.php
deleted file mode 100644 (file)
index 9ce93d6..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-<?php
-
-class WaitConditionLoopFakeTime extends WaitConditionLoop {
-       protected $wallClock = 1;
-
-       function __construct( callable $condition, $timeout, array $busyCallbacks ) {
-               parent::__construct( $condition, $timeout, $busyCallbacks );
-       }
-
-       function usleep( $microseconds ) {
-               $this->wallClock += $microseconds / 1e6;
-       }
-
-       function getCpuTime() {
-               return 0.0;
-       }
-
-       function getWallTime() {
-               return $this->wallClock;
-       }
-
-       public function setWallClock( &$timestamp ) {
-               $this->wallClock =& $timestamp;
-       }
-}
-
-class WaitConditionLoopTest extends PHPUnit_Framework_TestCase {
-       public function testCallbackReached() {
-               $wallClock = microtime( true );
-
-               $count = 0;
-               $status = new StatusValue();
-               $loop = new WaitConditionLoopFakeTime(
-                       function () use ( &$count, $status ) {
-                               ++$count;
-                               $status->value = 'cookie';
-
-                               return WaitConditionLoop::CONDITION_REACHED;
-                       },
-                       10.0,
-                       $this->newBusyWork( $x, $y, $z )
-               );
-               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
-               $this->assertEquals( 1, $count );
-               $this->assertEquals( 'cookie', $status->value );
-               $this->assertEquals( [ 0, 0, 0 ], [ $x, $y, $z ], "No busy work done" );
-
-               $count = 0;
-               $loop = new WaitConditionLoopFakeTime(
-                       function () use ( &$count, &$wallClock ) {
-                               $wallClock += 1;
-                               ++$count;
-
-                               return $count >= 2 ? WaitConditionLoop::CONDITION_REACHED : false;
-                       },
-                       7.0,
-                       $this->newBusyWork( $x, $y, $z, $wallClock )
-               );
-               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke(),
-                       "Busy work did not cause timeout" );
-               $this->assertEquals( [ 1, 0, 0 ], [ $x, $y, $z ] );
-
-               $count = 0;
-               $loop = new WaitConditionLoopFakeTime(
-                       function () use ( &$count, &$wallClock ) {
-                               $wallClock += .1;
-                               ++$count;
-
-                               return $count > 80 ? true : false;
-                       },
-                       50.0,
-                       $this->newBusyWork( $x, $y, $z, $wallClock, $dontCallMe, $badCalls )
-               );
-               $this->assertEquals( 0, $badCalls, "Callback exception not yet called" );
-               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
-               $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
-               $this->assertEquals( 1, $badCalls, "Bad callback ran and was exception caught" );
-
-               try {
-                       $e = null;
-                       $dontCallMe();
-               } catch ( Exception $e ) {
-               }
-
-               $this->assertInstanceOf( 'RunTimeException', $e );
-               $this->assertEquals( 1, $badCalls, "Callback exception cached" );
-       }
-
-       public function testCallbackTimeout() {
-               $count = 0;
-               $wallClock = microtime( true );
-               $loop = new WaitConditionLoopFakeTime(
-                       function () use ( &$count, &$wallClock ) {
-                               $wallClock += 3;
-                               ++$count;
-
-                               return $count > 300 ? true : false;
-                       },
-                       50.0,
-                       $this->newBusyWork( $x, $y, $z, $wallClock )
-               );
-               $loop->setWallClock( $wallClock );
-               $this->assertEquals( $loop::CONDITION_TIMED_OUT, $loop->invoke() );
-               $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
-
-               $loop = new WaitConditionLoopFakeTime(
-                       function () use ( &$count, &$wallClock ) {
-                               $wallClock += 3;
-                               ++$count;
-
-                               return true;
-                       },
-                       0.0,
-                       $this->newBusyWork( $x, $y, $z, $wallClock )
-               );
-               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
-
-               $count = 0;
-               $loop = new WaitConditionLoopFakeTime(
-                       function () use ( &$count, &$wallClock ) {
-                               $wallClock += 3;
-                               ++$count;
-
-                               return $count > 10 ? true : false;
-                       },
-                       0,
-                       $this->newBusyWork( $x, $y, $z, $wallClock )
-               );
-               $this->assertEquals( $loop::CONDITION_FAILED, $loop->invoke() );
-       }
-
-       public function testCallbackAborted() {
-               $x = 0;
-               $wallClock = microtime( true );
-               $loop = new WaitConditionLoopFakeTime(
-                       function () use ( &$x, &$wallClock ) {
-                               $wallClock += 2;
-                               ++$x;
-
-                               return $x > 2 ? WaitConditionLoop::CONDITION_ABORTED : false;
-                       },
-                       10.0,
-                       $this->newBusyWork( $x, $y, $z, $wallClock )
-               );
-               $loop->setWallClock( $wallClock );
-               $this->assertEquals( $loop::CONDITION_ABORTED, $loop->invoke() );
-       }
-
-       private function newBusyWork(
-               &$x, &$y, &$z, &$wallClock = 1, &$dontCallMe = null, &$badCalls = 0
-       ) {
-               $x = $y = $z = 0;
-               $badCalls = 0;
-
-               $list = [];
-               $list[] = function () use ( &$x, &$wallClock ) {
-                       $wallClock += 1;
-
-                       return ++$x;
-               };
-               $dontCallMe = function () use ( &$badCalls ) {
-                       ++$badCalls;
-                       throw new RuntimeException( "TrollyMcTrollFace" );
-               };
-               $list[] =& $dontCallMe;
-               $list[] = function () use ( &$y, &$wallClock ) {
-                       $wallClock += 15;
-
-                       return ++$y;
-               };
-               $list[] = function () use ( &$z, &$wallClock ) {
-                       $wallClock += 0.1;
-
-                       return ++$z;
-               };
-
-               return $list;
-       }
-}