|  | 
| 6 | 6 | use React\EventLoop\Loop; | 
| 7 | 7 | use React\EventLoop\LoopInterface; | 
| 8 | 8 | use React\Promise\Deferred; | 
|  | 9 | +use React\Promise\Promise; | 
| 9 | 10 | use React\Promise\PromiseInterface; | 
| 10 |  | -use React\Promise\Timer\TimeoutException; | 
| 11 | 11 | use React\Socket\ConnectionInterface; | 
| 12 | 12 | use React\Socket\Connector; | 
| 13 | 13 | use React\Socket\ConnectorInterface; | 
| 14 | 14 | use function React\Promise\reject; | 
| 15 |  | -use function React\Promise\Timer\timeout; | 
| 16 | 15 | 
 | 
| 17 | 16 | /** | 
| 18 | 17 |  * @internal | 
| @@ -175,14 +174,49 @@ function (\Exception $e) use ($redis, $uri) { | 
| 175 | 174 |             return $deferred->promise(); | 
| 176 | 175 |         } | 
| 177 | 176 | 
 | 
| 178 |  | -        return timeout($deferred->promise(), $timeout, $this->loop)->then(null, function (\Throwable $e) use ($uri) { | 
| 179 |  | -            if ($e instanceof TimeoutException) { | 
| 180 |  | -                throw new \RuntimeException( | 
| 181 |  | -                    'Connection to ' . $uri . ' timed out after ' . $e->getTimeout() . ' seconds (ETIMEDOUT)', | 
| 182 |  | -                    defined('SOCKET_ETIMEDOUT') ? SOCKET_ETIMEDOUT : 110 | 
| 183 |  | -                ); | 
|  | 177 | +        $promise = $deferred->promise(); | 
|  | 178 | +        /** @var Promise<StreamingClient> */ | 
|  | 179 | +        return new Promise(function (callable $resolve, callable $reject) use ($timeout, $promise, $uri): void { | 
|  | 180 | +            /** @var ?\React\EventLoop\TimerInterface */ | 
|  | 181 | +            $timer = null; | 
|  | 182 | +            $promise = $promise->then(function (StreamingClient $v) use (&$timer, $resolve): void { | 
|  | 183 | +                if ($timer) { | 
|  | 184 | +                    $this->loop->cancelTimer($timer); | 
|  | 185 | +                } | 
|  | 186 | +                $timer = false; | 
|  | 187 | +                $resolve($v); | 
|  | 188 | +            }, function (\Throwable $e) use (&$timer, $reject): void { | 
|  | 189 | +                if ($timer) { | 
|  | 190 | +                    $this->loop->cancelTimer($timer); | 
|  | 191 | +                } | 
|  | 192 | +                $timer = false; | 
|  | 193 | +                $reject($e); | 
|  | 194 | +            }); | 
|  | 195 | + | 
|  | 196 | +            // promise already settled => no need to start timer | 
|  | 197 | +            if ($timer === false) { | 
|  | 198 | +                return; | 
| 184 | 199 |             } | 
| 185 |  | -            throw $e; | 
|  | 200 | + | 
|  | 201 | +            // start timeout timer which will cancel the pending promise | 
|  | 202 | +            $timer = $this->loop->addTimer($timeout, function () use ($timeout, &$promise, $reject, $uri): void { | 
|  | 203 | +                $reject(new \RuntimeException( | 
|  | 204 | +                    'Connection to ' . $uri . ' timed out after ' . $timeout . ' seconds (ETIMEDOUT)', | 
|  | 205 | +                    \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110 | 
|  | 206 | +                )); | 
|  | 207 | + | 
|  | 208 | +                // Cancel pending connection to clean up any underlying resources and references. | 
|  | 209 | +                // Avoid garbage references in call stack by passing pending promise by reference. | 
|  | 210 | +                \assert($promise instanceof PromiseInterface); | 
|  | 211 | +                $promise->cancel(); | 
|  | 212 | +                $promise = null; | 
|  | 213 | +            }); | 
|  | 214 | +        }, function () use (&$promise): void { | 
|  | 215 | +            // Cancelling this promise will cancel the pending connection, thus triggering the rejection logic above. | 
|  | 216 | +            // Avoid garbage references in call stack by passing pending promise by reference. | 
|  | 217 | +            \assert($promise instanceof PromiseInterface); | 
|  | 218 | +            $promise->cancel(); | 
|  | 219 | +            $promise = null; | 
| 186 | 220 |         }); | 
| 187 | 221 |     } | 
| 188 | 222 | } | 
0 commit comments