Skip to content
Snippets Groups Projects
Commit d9328fa7 authored by Mateusz Charytoniuk's avatar Mateusz Charytoniuk
Browse files

fix: race condition in the DI container

parent 94ca91a0
Branches
Tags
No related merge requests found
...@@ -14,7 +14,6 @@ use ReflectionFunction; ...@@ -14,7 +14,6 @@ use ReflectionFunction;
use RuntimeException; use RuntimeException;
use Swoole\Coroutine\WaitGroup; use Swoole\Coroutine\WaitGroup;
use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run; use function Swoole\Coroutine\run;
readonly class DependencyInjectionContainer readonly class DependencyInjectionContainer
...@@ -78,31 +77,10 @@ readonly class DependencyInjectionContainer ...@@ -78,31 +77,10 @@ readonly class DependencyInjectionContainer
*/ */
public function make(string $className): object 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 * WaitGroup is not necesary here since `run` is going to wait for all
...@@ -110,26 +88,19 @@ readonly class DependencyInjectionContainer ...@@ -110,26 +88,19 @@ readonly class DependencyInjectionContainer
* *
* @var bool $coroutineResult * @var bool $coroutineResult
*/ */
$coroutineResult = run(function () use ($parameters, $reflectionClass) { $coroutineResult = run(function () use ($className, &$ret) {
foreach (new SingletonConstructorParametersIterator($reflectionClass) as $name => $typeClassName) { $ret = $this->doMake($className);
$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');
}
}
}); });
if (!$coroutineResult) { if (!$coroutineResult) {
throw new RuntimeException('Container event loop failed'); 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 public function registerSingletons(): void
...@@ -176,6 +147,42 @@ readonly class DependencyInjectionContainer ...@@ -176,6 +147,42 @@ readonly class DependencyInjectionContainer
$this->collections->get($collectionName)->add($providedClassName); $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 * @template TSingleton
* *
......
...@@ -18,6 +18,7 @@ use Nette\Schema\Schema; ...@@ -18,6 +18,7 @@ use Nette\Schema\Schema;
* db_index: int, * db_index: int,
* host: string, * host: string,
* password: string, * password: string,
* pool_size: int,
* port: int, * port: int,
* prefix: string, * prefix: string,
* timeout: int, * timeout: int,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment