|
13 | 13 | */ |
14 | 14 | class Loop |
15 | 15 | { |
16 | | - /** Minimum time to wait between lock checks. In micro seconds. */ |
17 | | - private const MINIMUM_WAIT_US = 10_000; |
18 | | - |
19 | | - /** Maximum time to wait between lock checks. In micro seconds. */ |
20 | | - private const MAXIMUM_WAIT_US = 500_000; |
21 | | - |
22 | 16 | /** True while code execution is repeating */ |
23 | 17 | private bool $looping = false; |
24 | 18 |
|
@@ -65,34 +59,41 @@ public function execute(callable $code, float $timeout) |
65 | 59 | // At this time, the lock will timeout. |
66 | 60 | $deadlineTs = microtime(true) + $timeout; |
67 | 61 |
|
| 62 | + $minWaitSecs = 0.1e-3; // 0.1 ms |
| 63 | + $maxWaitSecs = max(0.05, min(25, $timeout / 120)); // 50 ms to 25 s, based on timeout |
| 64 | + |
68 | 65 | $result = null; |
69 | | - for ($i = 0; $this->looping && microtime(true) < $deadlineTs; ++$i) { // @phpstan-ignore booleanAnd.leftAlwaysTrue |
| 66 | + for ($i = 0;; ++$i) { |
70 | 67 | $result = $code(); |
71 | 68 | if (!$this->looping) { // @phpstan-ignore booleanNot.alwaysFalse |
72 | 69 | // The $code callback has called $this->end() and the lock has been acquired. |
73 | 70 |
|
74 | | - return $result; |
| 71 | + break; |
75 | 72 | } |
76 | 73 |
|
77 | 74 | // Calculate max time remaining, don't sleep any longer than that. |
78 | | - $usecRemaining = LockUtil::getInstance()->castFloatToInt(($deadlineTs - microtime(true)) * 1e6); |
79 | | - |
80 | | - // We've ran out of time. |
81 | | - if ($usecRemaining <= 0) { |
| 75 | + $remainingSecs = $deadlineTs - microtime(true); |
| 76 | + if ($remainingSecs <= 0) { |
82 | 77 | break; |
83 | 78 | } |
84 | 79 |
|
85 | | - $min = min( |
86 | | - self::MINIMUM_WAIT_US * 1.25 ** $i, |
87 | | - self::MAXIMUM_WAIT_US |
| 80 | + $minSecs = min( |
| 81 | + $minWaitSecs * 1.5 ** $i, |
| 82 | + max($minWaitSecs, $maxWaitSecs / 2) |
| 83 | + ); |
| 84 | + $maxSecs = min($minSecs * 2, $maxWaitSecs); |
| 85 | + $sleepMicros = min( |
| 86 | + max(10, LockUtil::getInstance()->castFloatToInt($remainingSecs * 1e6)), |
| 87 | + random_int(LockUtil::getInstance()->castFloatToInt($minSecs * 1e6), LockUtil::getInstance()->castFloatToInt($maxSecs * 1e6)) |
88 | 88 | ); |
89 | | - $max = min($min * 2, self::MAXIMUM_WAIT_US); |
90 | 89 |
|
91 | | - $usecToSleep = min($usecRemaining, random_int((int) $min, (int) $max)); |
| 90 | + usleep($sleepMicros); |
| 91 | + } |
92 | 92 |
|
93 | | - usleep($usecToSleep); |
| 93 | + if (microtime(true) >= $deadlineTs) { |
| 94 | + throw LockAcquireTimeoutException::create($timeout); |
94 | 95 | } |
95 | 96 |
|
96 | | - throw LockAcquireTimeoutException::create($timeout); |
| 97 | + return $result; |
97 | 98 | } |
98 | 99 | } |
0 commit comments