Create New Item
×
Item Type
File
Folder
Item Name
×
Search file in folder and subfolders...
File Manager
/
wp-content
/
plugins
/
vibes
/
includes
/
libraries
/
lock
/
util
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php declare(strict_types=1); namespace malkusch\lock\util; use LengthException; use malkusch\lock\exception\TimeoutException; /** * Repeats executing a code until it was successful. * * @author Markus Malkusch <markus@malkusch.de> * @link bitcoin:1P5FAZ4QhXCuwYPnLZdk3PJsqePbu1UDDA Donations * @license WTFPL * @internal */ class Loop { /** * Minimum time that we want to wait, between lock checks. In micro seconds. * * @var double */ private const MINIMUM_WAIT_US = 1e4; // 0.01 seconds /** * Maximum time that we want to wait, between lock checks. In micro seconds. * * @var double */ private const MAXIMUM_WAIT_US = 5e5; // 0.50 seconds /** * @var int The timeout in seconds. */ private $timeout; /** * @var bool True while code execution is repeating. */ private $looping; /** * Sets the timeout. The default is 3 seconds. * * @param int $timeout The timeout in seconds. The default is 3 seconds. * @throws \LengthException The timeout must be greater than 0. */ public function __construct(int $timeout = 3) { if ($timeout <= 0) { throw new LengthException(\sprintf( 'The timeout must be greater than 0. %d was given.', $timeout )); } $this->timeout = $timeout; $this->looping = false; } /** * Notifies that this was the last iteration. * * @return void */ public function end(): void { $this->looping = false; } /** * Repeats executing a code until it was successful. * * The code has to be designed in a way that it can be repeated without any * side effects. When execution was successful it should notify that event * by calling {@link \malkusch\lock\util\Loop::end()}. I.e. the only side * effects of the code may happen after a successful execution. * * If the code throws an exception it will stop repeating the execution. * * @param callable $code The to be executed code callback. * @throws \Exception The execution callback threw an exception. * @throws \malkusch\lock\exception\TimeoutException The timeout has been * reached. * @return mixed The return value of the executed code callback. * */ public function execute(callable $code) { $this->looping = true; // At this time, the lock will time out. $deadline = microtime(true) + $this->timeout; $result = null; for ($i = 0; $this->looping && microtime(true) < $deadline; ++$i) { $result = $code(); if (!$this->looping) { // @phpstan-ignore-line break; } // Calculate max time remaining, don't sleep any longer than that. $usecRemaining = intval(($deadline - microtime(true)) * 1e6); // We've ran out of time. if ($usecRemaining <= 0) { throw TimeoutException::create($this->timeout); } $min = min( (int) self::MINIMUM_WAIT_US * 1.25 ** $i, self::MAXIMUM_WAIT_US ); $max = min($min * 2, self::MAXIMUM_WAIT_US); $usecToSleep = min($usecRemaining, random_int((int)$min, (int)$max)); usleep($usecToSleep); } if (microtime(true) >= $deadline) { throw TimeoutException::create($this->timeout); } return $result; } }