diff --git a/classes/util/Loop.php b/classes/util/Loop.php index e8203d00..0657568e 100644 --- a/classes/util/Loop.php +++ b/classes/util/Loop.php @@ -17,20 +17,6 @@ */ class Loop { - /** - * Minimum time that we want to wait, between lock checks. In micro seconds. - * - * @var double - */ - private const MINIMUM_WAIT_US = 1e4; // 0.01 seconds - - /** - * Maximum time that we want to wait, between lock checks. In micro seconds. - * - * @var double - */ - private const MAXIMUM_WAIT_US = 5e5; // 0.50 seconds - /** * @var int The timeout in seconds. */ @@ -92,35 +78,38 @@ public function execute(callable $code) $this->looping = true; // At this time, the lock will time out. - $deadline = microtime(true) + $this->timeout; + $deadlineTs = microtime(true) + $this->timeout; + + $minWaitSecs = 0.1e-3; // 0.1 ms + $maxWaitSecs = max(0.05, min(25, $this->timeout / 120)); // 50 ms to 25 s, based on timeout $result = null; - for ($i = 0; $this->looping && microtime(true) < $deadline; ++$i) { + for ($i = 0; ; ++$i) { $result = $code(); if (!$this->looping) { break; } // Calculate max time remaining, don't sleep any longer than that. - $usecRemaining = intval(($deadline - microtime(true)) * 1e6); - - // We've ran out of time. - if ($usecRemaining <= 0) { - throw TimeoutException::create($this->timeout); + $remainingSecs = $deadlineTs - microtime(true); + if ($remainingSecs <= 0) { + break; } - $min = min( - (int) self::MINIMUM_WAIT_US * 1.25 ** $i, - self::MAXIMUM_WAIT_US + $minSecs = min( + $minWaitSecs * 1.5 ** $i, + max($minWaitSecs, $maxWaitSecs / 2) + ); + $maxSecs = min($minSecs * 2, $maxWaitSecs); + $sleepMicros = min( + max(10, (int)($remainingSecs * 1e6)), + random_int((int)($minSecs * 1e6), (int)($maxSecs * 1e6)) ); - $max = min($min * 2, self::MAXIMUM_WAIT_US); - - $usecToSleep = min($usecRemaining, random_int((int)$min, (int)$max)); - usleep($usecToSleep); + usleep($sleepMicros); } - if (microtime(true) >= $deadline) { + if (microtime(true) >= $deadlineTs) { throw TimeoutException::create($this->timeout); }