From d9328fa79ea7afb80cbc798616697fc60408baf7 Mon Sep 17 00:00:00 2001 From: Mateusz Charytoniuk <mateusz.charytoniuk@protonmail.com> Date: Mon, 16 Oct 2023 21:04:08 +0200 Subject: [PATCH] fix: race condition in the DI container --- src/DependencyInjectionContainer.php | 83 ++++++++++--------- .../RedisConfigurationProvider.php | 1 + 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/DependencyInjectionContainer.php b/src/DependencyInjectionContainer.php index 4afb9a59..06550b49 100644 --- a/src/DependencyInjectionContainer.php +++ b/src/DependencyInjectionContainer.php @@ -14,7 +14,6 @@ use ReflectionFunction; use RuntimeException; use Swoole\Coroutine\WaitGroup; -use function Swoole\Coroutine\go; use function Swoole\Coroutine\run; readonly class DependencyInjectionContainer @@ -78,31 +77,10 @@ readonly class DependencyInjectionContainer */ public function make(string $className): object { - if ($this->singletons->has($className)) { - return $this->singletons->get($className); - } - - if ($this->providers->hasKey($className)) { - /** - * @var bool $coroutineResult - */ - $coroutineResult = run(function () use ($className) { - $this->makeSingleton($className, new Set()); - }); - - if (!$coroutineResult) { - throw new RuntimeException('Container event loop failed'); - } - - return $this->singletons->get($className); - } - - $reflectionClass = new ReflectionClass($className); - /** - * @var Map<string,object> $parameters + * @var null|TSingleton */ - $parameters = new Map(); + $ret = null; /** * WaitGroup is not necesary here since `run` is going to wait for all @@ -110,26 +88,19 @@ readonly class DependencyInjectionContainer * * @var bool $coroutineResult */ - $coroutineResult = run(function () use ($parameters, $reflectionClass) { - foreach (new SingletonConstructorParametersIterator($reflectionClass) as $name => $typeClassName) { - $cid = go(function () use ($name, $parameters, $typeClassName) { - $parameters->put( - $name, - $this->makeSingleton($typeClassName, new Set()), - ); - }); - - if (!is_int($cid)) { - throw new RuntimeException('Unable to start Container coroutine'); - } - } + $coroutineResult = run(function () use ($className, &$ret) { + $ret = $this->doMake($className); }); if (!$coroutineResult) { throw new RuntimeException('Container event loop failed'); } - return $reflectionClass->newInstance(...$parameters->toArray()); + if (!($ret instanceof $className)) { + throw new LogicException('Unable to make an instance of '.$className); + } + + return $ret; } public function registerSingletons(): void @@ -176,6 +147,42 @@ readonly class DependencyInjectionContainer $this->collections->get($collectionName)->add($providedClassName); } + /** + * @template TSingleton + * + * @param class-string<TSingleton> $className + * + * @return TSingleton + */ + private function doMake(string $className): object + { + if ($this->singletons->has($className)) { + return $this->singletons->get($className); + } + + if ($this->providers->hasKey($className)) { + $this->makeSingleton($className, new Set()); + + return $this->singletons->get($className); + } + + $reflectionClass = new ReflectionClass($className); + + /** + * @var Map<string,object> $parameters + */ + $parameters = new Map(); + + foreach (new SingletonConstructorParametersIterator($reflectionClass) as $name => $typeClassName) { + $parameters->put( + $name, + $this->makeSingleton($typeClassName, new Set()), + ); + } + + return $reflectionClass->newInstance(...$parameters->toArray()); + } + /** * @template TSingleton * diff --git a/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php index fb95a128..3f202034 100644 --- a/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php +++ b/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php @@ -18,6 +18,7 @@ use Nette\Schema\Schema; * db_index: int, * host: string, * password: string, + * pool_size: int, * port: int, * prefix: string, * timeout: int, -- GitLab