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 5, 2024
1 parent 3fd576b commit 70bc168
Showing 1 changed file with 21 additions and 24 deletions.
45 changes: 21 additions & 24 deletions src/util/Loop.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,6 @@
*/
class Loop
{
/**
* Minimum time that we want to wait, between lock checks. In micro seconds.
*/
private const MINIMUM_WAIT_US = 1e4; // 0.01 seconds

/**
* Maximum time that we want to wait, between lock checks. In micro seconds.
*/
private const MAXIMUM_WAIT_US = 5e5; // 0.50 seconds

/** @var float The timeout in seconds */
private $timeout;

Expand Down Expand Up @@ -80,10 +70,13 @@ 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) { // @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.
Expand All @@ -92,24 +85,28 @@ public function execute(callable $code)
}

// Calculate max time remaining, don't sleep any longer than that.
$usecRemaining = (int) (($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($sleepMicros);
}

usleep($usecToSleep);
if (microtime(true) >= $deadlineTs) {
throw TimeoutException::create($this->timeout);
}

throw TimeoutException::create($this->timeout);
return $result;
}
}

0 comments on commit 70bc168

Please sign in to comment.