Skip to content

Commit

Permalink
Limit max. sleep duration per loop iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Dec 16, 2024
1 parent 4fe9681 commit 7e66688
Showing 1 changed file with 20 additions and 19 deletions.
39 changes: 20 additions & 19 deletions src/Util/Loop.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -65,34 +59,41 @@ 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.

return $result;
break;
}

// 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;
}
}

0 comments on commit 7e66688

Please sign in to comment.