Improve MultiCurl::waitUntilRequestQuotaAvailable (#1016)

* Include keyword

* Use numeric literal separators

* Improve waitUntilRequestQuotaAvailable using new TimeUtil
This commit is contained in:
Zach Borboa
2025-11-16 14:58:13 -05:00
committed by GitHub
parent ccbac89b97
commit f058672e6f
5 changed files with 142 additions and 20 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ Installation instructions to use the `composer` command can be found on https://
### 📋 Requirements
PHP Curl Class works with PHP 8.4, 8.3, 8.2, 8.1, and 8.0.
PHP Curl Class works with PHP versions 8.4, 8.3, 8.2, 8.1, and 8.0.
### 🚀 Quick Start and Examples
+19 -18
View File
@@ -687,7 +687,7 @@ class MultiCurl extends BaseCurl
// pending requests to have more accurate start times. Without a shorter timeout, it can be nearly a
// full second before available request quota is rechecked and pending requests can be initialized.
if (curl_multi_select($this->multiCurl, 0.2) === -1) {
usleep(100000);
usleep(100_000);
}
curl_multi_exec($this->multiCurl, $active);
@@ -944,27 +944,28 @@ class MultiCurl extends BaseCurl
*/
private function waitUntilRequestQuotaAvailable()
{
$sleep_until = (float)($this->currentStartTime + $this->intervalSeconds);
$sleep_seconds = $sleep_until - microtime(true);
$sleep_until = TimeUtil::getSleepUntilMicrotime(
$this->currentStartTime,
$this->intervalSeconds,
);
// Avoid using time_sleep_until() as it appears to be less precise and not sleep long enough.
// Avoid using usleep(): "Values larger than 1000000 (i.e. sleeping for
// more than a second) may not be supported by the operating system.
// Use sleep() instead."
$sleep_seconds_int = (int)$sleep_seconds;
if ($sleep_seconds_int >= 1) {
sleep($sleep_seconds_int);
$current_microtime = microtime(true);
$sleep_seconds = TimeUtil::getSleepSecondsUntilMicrotime(
$sleep_until,
$current_microtime,
);
list($whole_seconds, $microseconds_remainder) = TimeUtil::getWholeAndRemainderSeconds($sleep_seconds);
if ($whole_seconds >= 1) {
sleep($whole_seconds);
}
if ($microseconds_remainder > 0) {
usleep($microseconds_remainder);
}
// Ensure that enough time has passed as usleep() may not have waited long enough.
$this->currentStartTime = microtime(true);
if ($this->currentStartTime < $sleep_until) {
do {
usleep(1000000 / 4);
$this->currentStartTime = microtime(true);
} while ($this->currentStartTime < $sleep_until);
}
$this->currentRequestCount = 0;
}
+61
View File
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Curl;
class TimeUtil
{
/**
* Get the microtime (in microseconds) at which to sleep until.
*
* @param float $start_time The start time in seconds with microsecond precision.
* @param int $interval_seconds The interval in seconds.
* @return float The microtime (in microseconds) at which to sleep until.
*/
public static function getSleepUntilMicrotime(
float $start_time,
int $interval_seconds,
): float {
$result = $start_time + (float)$interval_seconds;
return $result;
}
/**
* Get the number of seconds to sleep until the specified microtime.
*
* @param float $sleep_until_microtime The microtime (in microseconds) at which to sleep until.
* @param float $current_microtime The current microtime (in microseconds).
* @return float The number of seconds to sleep.
*/
public static function getSleepSecondsUntilMicrotime(
float $sleep_until_microtime,
float $current_microtime,
): float {
$result = $sleep_until_microtime - $current_microtime;
// Always round up with microsecond precision to avoid sleeping less
// than required.
$rounded_up_result = ceil($result * (float)1_000_000) / (float)1_000_000;
return $rounded_up_result;
}
/**
* Get the whole seconds and microseconds remainder from the given sleep
* seconds.
*
* @param float $sleep_seconds The number of seconds to sleep.
* @return array An array containing the whole seconds and microseconds remainder.
*/
public static function getWholeAndRemainderSeconds(
float $sleep_seconds,
): array {
$micros = (int) ceil($sleep_seconds * (float)1_000_000);
$whole_seconds = intdiv($micros, 1_000_000);
$microseconds_remainder = $micros % 1_000_000;
return [
$whole_seconds,
$microseconds_remainder,
];
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace CurlTest;
use Curl\TimeUtil;
class TimeUtilTest extends \PHPUnit\Framework\TestCase
{
public function testGetSleepUntilMicrotime()
{
// Use a fixed start time.
// [...]ime = microtime(true);
$start_time = (float)1_750_000_000.123456;
// Use a fixed current time that occurs after the start time.
// [...]_microtime = (float)1_750_000_045.000001;
$current_microtime = (float)1_750_000_044.999999;
$interval_seconds = 60;
$sleep_until_microtime = TimeUtil::getSleepUntilMicrotime(
$start_time,
$interval_seconds,
);
$this->assertEquals(
(float)1_750_000_060.123456,
$sleep_until_microtime,
);
}
public function testGetSleepSecondsUntilMicrotime()
{
$sleep_until_microtime = (float)1_750_000_060.123456;
$current_microtime = (float)1_750_000_044.999999;
$sleep_seconds = TimeUtil::getSleepSecondsUntilMicrotime(
$sleep_until_microtime,
$current_microtime,
);
$this->assertEquals(
(float)15.123457,
$sleep_seconds,
);
}
public function testGetWholeAndRemainderSeconds()
{
$sleep_seconds = (float)15.123457;
list($whole_seconds, $microseconds_remainder) = TimeUtil::getWholeAndRemainderSeconds(
$sleep_seconds,
);
$this->assertEquals(15, $whole_seconds);
$this->assertEquals(123457, $microseconds_remainder);
}
}
+1 -1
View File
@@ -323,7 +323,7 @@ if ($test === 'http_basic_auth') {
$dots_printed = 0;
while (true) {
usleep(1000000 / 100);
usleep(1_000_000 / 100);
$elapsed = microtime(true) - $start;
$dots_to_print = floor($elapsed) - $dots_printed;