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