Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit 69e76ca

Browse files
authored
Merge pull request #15 from reactphp-parallel/dont-wait-for-void-call-results
Don't wait for void calls results
2 parents d8c1b84 + 977c5ab commit 69e76ca

File tree

7 files changed

+144
-20
lines changed

7 files changed

+144
-20
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ psalm: ## Run static analysis (Psalm)
4040
$(DOCKER_RUN) vendor/bin/psalm --threads=$(shell nproc) --shepherd --stats
4141

4242
unit: ## Run tests
43-
$(DOCKER_RUN) vendor/bin/phpunit --colors=always -c phpunit.xml.dist --coverage-text --coverage-html covHtml --coverage-clover ./build/logs/clover.xml
43+
$(DOCKER_RUN) vendor/bin/phpunit --colors=always -c phpunit.xml.dist --coverage-text --coverage-html covHtml --coverage-clover ./build/logs/clover.xml --filter metricsDestructionTesting
4444

4545
unit-ci: unit
4646
if [ -f ./build/logs/clover.xml ]; then wget https://scrutinizer-ci.com/ocular.phar && sleep 3 && php ocular.phar code-coverage:upload --format=php-clover ./build/logs/clover.xml; fi

src/AbstractGeneratedProxy.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use parallel\Channel;
88
use ReactParallel\ObjectProxy\Message\Call;
99
use ReactParallel\ObjectProxy\Message\Destruct;
10+
use ReactParallel\ObjectProxy\Message\Notify;
1011

1112
use function spl_object_hash;
1213

@@ -24,7 +25,7 @@ final public function __construct(Channel $out, string $hash)
2425
/**
2526
* @param mixed[] $args
2627
*
27-
* @return mixed
28+
* @return mixed|void
2829
*/
2930
final protected function proxyCallToMainThread(string $method, array $args)
3031
{
@@ -43,6 +44,20 @@ final protected function proxyCallToMainThread(string $method, array $args)
4344
return $result;
4445
}
4546

47+
/**
48+
* @param mixed[] $args
49+
*/
50+
final protected function proxyNotifyMainThread(string $interface, string $method, array $args): void
51+
{
52+
$this->out->send(new Notify(
53+
$this->hash,
54+
spl_object_hash($this),
55+
$interface,
56+
$method,
57+
$args,
58+
));
59+
}
60+
4661
final protected function notifyMainThreadAboutDestruction(): void
4762
{
4863
try {

src/Composer/Installer.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
use Composer\Plugin\PluginInterface;
1414
use Composer\Script\Event;
1515
use Composer\Script\ScriptEvents;
16+
use PhpParser\Lexer\Emulative;
1617
use PhpParser\ParserFactory;
1718
use PhpParser\PrettyPrinter\Standard;
19+
use Psr\Log\LoggerInterface;
1820
use ReflectionClass;
1921
use Rx\Observable;
2022
use Throwable;
@@ -75,6 +77,7 @@ public static function generateProxies(Event $event): void
7577
$start = microtime(true);
7678
$io = $event->getIO();
7779
$composer = $event->getComposer();
80+
$rootPath = self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage());
7881

7982
if (! function_exists('React\Promise\Resolve')) {
8083
/** @psalm-suppress UnresolvableInclude */
@@ -124,7 +127,7 @@ public static function generateProxies(Event $event): void
124127
$io->write('<info>react-parallel/object-proxy:</info> Locating interfaces');
125128

126129
$installPath = self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage()) . '/src/Generated/';
127-
$proxies = self::getProxies($composer, $io);
130+
$proxies = self::getProxies($composer, $io, $rootPath);
128131

129132
$io->write('<info>react-parallel/object-proxy:</info> Found ' . count($proxies) . ' interface(s) and generated a proxy for each of them');
130133

@@ -141,7 +144,7 @@ public static function generateProxies(Event $event): void
141144
"['%s']",
142145
'%s',
143146
file_get_contents(
144-
self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage()) . '/etc/ProxyList.php'
147+
$rootPath . '/etc/ProxyList.php'
145148
)
146149
),
147150
var_export($interfaces, TRUE_)
@@ -174,9 +177,9 @@ private static function locateRootPackageInstallPath(
174177
/**
175178
* @return array<InterfaceProxier>
176179
*/
177-
private static function getProxies(Composer $composer, IOInterface $io): array
180+
private static function getProxies(Composer $composer, IOInterface $io, string $rootPath): array
178181
{
179-
$phpParser = $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
182+
$phpParser = $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Emulative(['comments' => true]));
180183

181184
$result = [];
182185
$packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages();
@@ -189,14 +192,22 @@ private static function getProxies(Composer $composer, IOInterface $io): array
189192
static fn (array $interfaces): Observable => observableFromArray(array_unique(array_values($interfaces)))
190193
)->map(
191194
/** @phpstan-ignore-next-line */
192-
static function (string $interface) use ($io, $phpParser): ?array {
195+
static function (string $interface) use ($io, $phpParser, $rootPath): ?array {
193196
$io->write(sprintf('<info>react-parallel/object-proxy:</info> Creating proxy for %s', $interface));
194197

195198
/**
196199
* @psalm-suppress ArgumentTypeCoercion
197200
* @phpstan-ignore-next-line
198201
*/
199-
return $phpParser->parse(file_get_contents((new ReflectionClass($interface))->getFileName()));
202+
$fileName = (new ReflectionClass($interface))->getFileName();
203+
if ($interface === LoggerInterface::class) {
204+
$fileName = $rootPath . '/vendor/psr/log/Psr/Log/LoggerInterface.php';
205+
}
206+
207+
/**
208+
* @phpstan-ignore-next-line
209+
*/
210+
return $phpParser->parse(file_get_contents($fileName));
200211
}
201212
)->filter(
202213
static fn (?array $ast): bool => is_array($ast)

src/Composer/InterfaceProxier.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace ReactParallel\ObjectProxy\Composer;
66

77
use PhpParser\Builder\Method;
8-
use PhpParser\Comment\Doc;
8+
use PhpParser\Comment;
99
use PhpParser\Node;
1010
use ReactParallel\ObjectProxy\AbstractGeneratedProxy;
1111

@@ -15,6 +15,9 @@
1515
use function is_array;
1616
use function property_exists;
1717
use function str_replace;
18+
use function strpos;
19+
20+
use const WyriHaximus\Constants\Boolean\FALSE_;
1821

1922
final class InterfaceProxier
2023
{
@@ -180,7 +183,7 @@ private function populateMethod(Node\Stmt\ClassMethod $method): Node\Stmt\ClassM
180183

181184
$methodBody = new Node\Expr\MethodCall(
182185
new Node\Expr\Variable('this'),
183-
'proxyCallToMainThread',
186+
$this->isMethodVoid($method) ? 'proxyNotifyMainThread' : 'proxyCallToMainThread',
184187
[
185188
new Node\Arg(
186189
new Node\Expr\ConstFetch(
@@ -199,21 +202,32 @@ private function populateMethod(Node\Stmt\ClassMethod $method): Node\Stmt\ClassM
199202
$this->wrapMethodBody($method, $methodBody),
200203
];
201204

202-
$method->setDocComment(new Doc(''));
203-
204205
return $method;
205206
}
206207

207208
private function wrapMethodBody(Node\Stmt\ClassMethod $method, Node\Expr\MethodCall $methodBody): Node\Stmt
209+
{
210+
if ($this->isMethodVoid($method)) {
211+
return new Node\Stmt\Expression($methodBody);
212+
}
213+
214+
return new Node\Stmt\Return_($methodBody);
215+
}
216+
217+
private function isMethodVoid(Node\Stmt\ClassMethod $method): bool
208218
{
209219
/**
210220
* @psalm-suppress PossiblyInvalidCast
211221
* @phpstan-ignore-next-line
212222
*/
213-
if ((string) $method->getReturnType() !== 'void') {
214-
return new Node\Stmt\Return_($methodBody);
215-
}
223+
return (string) $method->getReturnType() === 'void' ? true : $this->isMethodVoidFromDocBlock($method);
224+
}
216225

217-
return new Node\Stmt\Expression($methodBody);
226+
private function isMethodVoidFromDocBlock(Node\Stmt\ClassMethod $method): bool
227+
{
228+
/**
229+
* @psalm-suppress PossiblyNullReference
230+
*/
231+
return $method->getDocComment() instanceof Comment && strpos($method->getDocComment()->getText(), '@return void') !== FALSE_;
218232
}
219233
}

src/Message/Notify.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ReactParallel\ObjectProxy\Message;
6+
7+
final class Notify
8+
{
9+
private string $hash;
10+
private string $objectHash;
11+
private string $interface;
12+
13+
private string $method;
14+
15+
/** @var mixed[] */
16+
private array $args;
17+
18+
/**
19+
* @param mixed[] $args
20+
*/
21+
public function __construct(string $hash, string $objectHash, string $interface, string $method, array $args)
22+
{
23+
$this->hash = $hash;
24+
$this->objectHash = $objectHash;
25+
$this->interface = $interface;
26+
$this->method = $method;
27+
$this->args = $args;
28+
}
29+
30+
public function hash(): string
31+
{
32+
return $this->hash;
33+
}
34+
35+
public function objectHash(): string
36+
{
37+
return $this->objectHash;
38+
}
39+
40+
public function interface(): string
41+
{
42+
return $this->interface;
43+
}
44+
45+
public function method(): string
46+
{
47+
return $this->method;
48+
}
49+
50+
/**
51+
* @return mixed[]
52+
*/
53+
public function args(): array
54+
{
55+
return $this->args;
56+
}
57+
}

src/Proxy.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use ReactParallel\ObjectProxy\Generated\ProxyList;
1010
use ReactParallel\ObjectProxy\Message\Call;
1111
use ReactParallel\ObjectProxy\Message\Destruct;
12+
use ReactParallel\ObjectProxy\Message\Notify;
1213
use ReactParallel\ObjectProxy\Proxy\Instance;
1314
use WyriHaximus\Metrics\Label;
1415
use WyriHaximus\Metrics\Registry;
@@ -26,6 +27,7 @@ final class Proxy extends ProxyList
2627

2728
private Factory $factory;
2829
private ?Counters $counterCreate = null;
30+
private ?Counters $counterNotify = null;
2931
private ?Counters $counterCall = null;
3032
private ?Counters $counterDestruct = null;
3133

@@ -59,6 +61,12 @@ public function withMetrics(Registry $registry): self
5961
new Label\Name('class'),
6062
new Label\Name('interface'),
6163
);
64+
$self->counterNotify = $registry->counter(
65+
'react_parallel_object_proxy_notify',
66+
'The number of notifications from worker threads through proxies to the main thread',
67+
new Label\Name('class'),
68+
new Label\Name('interface'),
69+
);
6270
$self->counterDestruct = $registry->counter(
6371
'react_parallel_object_proxy_destruct',
6472
'Number of destroyed proxies by the garbage collector',
@@ -103,6 +111,10 @@ public function __destruct()
103111
private function setUpHandlers(): void
104112
{
105113
$this->factory->streams()->channel($this->in)->subscribe(function (object $message): void {
114+
if ($message instanceof Notify) {
115+
$this->handleNotify($message);
116+
}
117+
106118
if ($message instanceof Call) {
107119
$this->handleCall($message);
108120
}
@@ -117,6 +129,22 @@ private function setUpHandlers(): void
117129
});
118130
}
119131

132+
private function handleNotify(Notify $notify): void
133+
{
134+
if (! array_key_exists($notify->hash(), $this->instances)) {
135+
return;
136+
}
137+
138+
$instance = $this->instances[$notify->hash()];
139+
$instance->reference($notify->objectHash());
140+
if ($this->counterNotify instanceof Counters) {
141+
$this->counterNotify->counter(new Label('class', $instance->class()), new Label('interface', $instance->interface()))->incr();
142+
}
143+
144+
/** @phpstan-ignore-next-line */
145+
$instance->object()->{$notify->method()}(...$notify->args());
146+
}
147+
120148
private function handleCall(Call $call): void
121149
{
122150
if (! array_key_exists($call->hash(), $this->instances)) {

tests/ProxyTest.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function logger(): void
5050

5151
return $time;
5252
}, [$loggerProxy, $time])->always(static function () use ($factory): void {
53-
$factory->lowLevelPool()->kill();
53+
$factory->lowLevelPool()->close();
5454
}),
5555
$loop
5656
);
@@ -87,7 +87,7 @@ public function loggerThroughContainer(): void
8787

8888
return $time;
8989
}, [$containerProxy, $time])->always(static function () use ($factory): void {
90-
$factory->lowLevelPool()->kill();
90+
$factory->lowLevelPool()->close();
9191
}),
9292
$loop
9393
);
@@ -133,8 +133,7 @@ public function metricsDestructionTesting(): void
133133
all($promises)->always(static function () use ($limitedPool): void {
134134
$limitedPool->close();
135135
}),
136-
$loop,
137-
30
136+
$loop
138137
);
139138
self::assertCount(count($promises), $results);
140139
} catch (TimeoutException $timeoutException) {

0 commit comments

Comments
 (0)