From d5d119fcd9728f1554560f78ecbecfa61e76a380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 16 Dec 2024 01:41:49 +0100 Subject: [PATCH] Limit max. sleep duration per loop iteration --- src/Util/Loop.php | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/Util/Loop.php b/src/Util/Loop.php index 5a2cca4..88c1e13 100644 --- a/src/Util/Loop.php +++ b/src/Util/Loop.php @@ -13,12 +13,6 @@ */ class Loop { - /** Minimum time to wait between lock checks. In micro seconds. */ - private const MINIMUM_WAIT_US = 10_000; - - /** Maximum time to wait between lock checks. In micro seconds. */ - private const MAXIMUM_WAIT_US = 500_000; - /** True while code execution is repeating */ private bool $looping = false; @@ -65,8 +59,11 @@ public function execute(callable $code, float $timeout) // At this time, the lock will timeout. $deadlineTs = microtime(true) + $timeout; + $minWaitSecs = 0.1e-3; // 0.1 ms + $maxWaitSecs = max(0.05, min(25, $timeout / 120)); // 50 ms to 25 s, based on timeout + $result = null; - for ($i = 0; $this->looping && microtime(true) < $deadlineTs; ++$i) { // @phpstan-ignore booleanAnd.leftAlwaysTrue + for ($i = 0;; ++$i) { $result = $code(); if (!$this->looping) { // @phpstan-ignore booleanNot.alwaysFalse // The $code callback has called $this->end() and the lock has been acquired. @@ -75,24 +72,28 @@ public function execute(callable $code, float $timeout) } // Calculate max time remaining, don't sleep any longer than that. - $usecRemaining = LockUtil::getInstance()->castFloatToInt(($deadlineTs - microtime(true)) * 1e6); - - // We've ran out of time. - if ($usecRemaining <= 0) { + $remainingSecs = $deadlineTs - microtime(true); + if ($remainingSecs <= 0) { break; } - $min = min( - 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, LockUtil::getInstance()->castFloatToInt($remainingSecs * 1e6)), + random_int(LockUtil::getInstance()->castFloatToInt($minSecs * 1e6), LockUtil::getInstance()->castFloatToInt($maxSecs * 1e6)) ); - $max = min($min * 2, self::MAXIMUM_WAIT_US); - $usecToSleep = min($usecRemaining, random_int((int) $min, (int) $max)); + usleep($sleepMicros); + } - usleep($usecToSleep); + if (microtime(true) >= $deadlineTs) { + throw LockAcquireTimeoutException::create($timeout); } - throw LockAcquireTimeoutException::create($timeout); + return $result; } }