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

Commit 4142e26

Browse files
authored
Merge pull request #25 from reactphp-parallel/destruction-edge-cases
Destruction edge cases
2 parents 708e72a + e339ffa commit 4142e26

File tree

7 files changed

+109
-3
lines changed

7 files changed

+109
-3
lines changed

composer-require-checker.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"WyriHaximus\\Constants\\Numeric\\ZERO", "WyriHaximus\\getIn", "WyriHaximus\\Constants\\Numeric\\TWO",
88
"Composer\\Composer", "Composer\\Config", "Composer\\IO\\IOInterface", "Composer\\Script\\Event",
99
"Composer\\EventDispatcher\\EventSubscriberInterface", "Composer\\Package\\RootPackageInterface",
10-
"Composer\\Plugin\\PluginInterface", "Composer\\Script\\ScriptEvents"
10+
"Composer\\Plugin\\PluginInterface", "Composer\\Script\\ScriptEvents",
11+
"Safe\\chmod", "Safe\\file_get_contents", "Safe\\file_put_contents", "Safe\\sprintf"
1112
],
1213
"php-core-extensions" : [
1314
"Core",

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
"psr/log": "^1.1",
2222
"react-parallel/react-parallel": "^1.0",
2323
"rx/operator-extras": "^2.1",
24-
"thecodingmachine/safe": "^1.2",
2524
"wyrihaximus/constants": "^1.5",
2625
"wyrihaximus/iterator-or-array-to-array": "^1.1",
2726
"wyrihaximus/metrics": "dev-master",
2827
"wyrihaximus/string-get-in": "^1.0"
2928
},
3029
"require-dev": {
3130
"monolog/monolog": "^2.1",
31+
"thecodingmachine/safe": "^1.3",
3232
"wyrihaximus/async-test-utilities": "^2.3",
3333
"yuloh/container": "^1.0"
3434
},

composer.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/AbstractGeneratedProxy.php

Lines changed: 10 additions & 0 deletions
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\Existence;
1011
use ReactParallel\ObjectProxy\Message\Notify;
1112

1213
use function spl_object_hash;
@@ -65,4 +66,13 @@ final protected function notifyMainThreadAboutDestruction(): void
6566
// @ignoreException
6667
}
6768
}
69+
70+
final public function notifyMainThreadAboutOurExistence(): void
71+
{
72+
try {
73+
$this->out->send(new Existence($this->hash, spl_object_hash($this)));
74+
} catch (Channel\Error\Closed $closed) {
75+
// @ignoreException
76+
}
77+
}
6878
}

src/Message/Existence.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ReactParallel\ObjectProxy\Message;
6+
7+
final class Existence
8+
{
9+
private string $hash;
10+
private string $objectHash;
11+
12+
public function __construct(string $hash, string $objectHash)
13+
{
14+
$this->hash = $hash;
15+
$this->objectHash = $objectHash;
16+
}
17+
18+
public function hash(): string
19+
{
20+
return $this->hash;
21+
}
22+
23+
public function objectHash(): string
24+
{
25+
return $this->objectHash;
26+
}
27+
}

src/Proxy.php

Lines changed: 24 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\Existence;
1213
use ReactParallel\ObjectProxy\Message\Notify;
1314
use ReactParallel\ObjectProxy\Proxy\Instance;
1415
use WyriHaximus\Metrics\Label;
@@ -20,6 +21,7 @@
2021
use function get_class;
2122
use function is_object;
2223
use function random_bytes;
24+
use function var_export;
2325

2426
final class Proxy extends ProxyList
2527
{
@@ -111,12 +113,22 @@ public function __destruct()
111113
private function setUpHandlers(): void
112114
{
113115
$this->factory->streams()->channel($this->in)->subscribe(function (object $message): void {
116+
if ($message instanceof Existence) {
117+
$this->handleExistence($message);
118+
119+
return;
120+
}
121+
114122
if ($message instanceof Notify) {
115123
$this->handleNotify($message);
124+
125+
return;
116126
}
117127

118128
if ($message instanceof Call) {
119129
$this->handleCall($message);
130+
131+
return;
120132
}
121133

122134
if (! ($message instanceof Destruct)) {
@@ -129,6 +141,18 @@ private function setUpHandlers(): void
129141
});
130142
}
131143

144+
private function handleExistence(Existence $existence): void
145+
{
146+
if (! array_key_exists($existence->hash(), $this->instances)) {
147+
var_export($existence);
148+
149+
return;
150+
}
151+
152+
$instance = $this->instances[$existence->hash()];
153+
$instance->reference($existence->objectHash());
154+
}
155+
132156
private function handleNotify(Notify $notify): void
133157
{
134158
if (! array_key_exists($notify->hash(), $this->instances)) {

tests/ProxyTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use React\Promise\PromiseInterface;
1313
use React\Promise\Timer\TimeoutException;
1414
use ReactParallel\Factory;
15+
use ReactParallel\ObjectProxy\Generated\WyriHaximus__Metrics_RegistryProxy;
1516
use ReactParallel\ObjectProxy\NonExistentInterface;
1617
use ReactParallel\ObjectProxy\Proxy;
1718
use stdClass;
@@ -26,6 +27,7 @@
2627
use function explode;
2728
use function range;
2829
use function React\Promise\all;
30+
use function Safe\sleep;
2931
use function time;
3032

3133
final class ProxyTest extends AsyncTestCase
@@ -169,4 +171,46 @@ public function metricsDestructionTesting(): void
169171
(int) explode("\n", explode('react_parallel_object_proxy_destruct_total{class="WyriHaximus\Metrics\InMemory\Registry",interface="WyriHaximus\Metrics\Registry"}', $txt)[1])[0]
170172
);
171173
}
174+
175+
/**
176+
* @test
177+
*/
178+
public function destructionEdgeCases(): void
179+
{
180+
$loop = EventLoopFactory::create();
181+
$factory = new Factory($loop);
182+
$registry = new InMemmoryRegistry();
183+
$proxy = (new Proxy($factory))->withMetrics($registry);
184+
$limitedPool = $factory->limitedPool(13);
185+
$registryProxy = $proxy->create($registry, Registry::class);
186+
$fn = static function (int $int, WyriHaximus__Metrics_RegistryProxy $registryProxy, int $sleep): int {
187+
$registryProxy->notifyMainThreadAboutOurExistence();
188+
sleep($sleep);
189+
$registryProxy->counter('counter', 'bla bla bla', new Label\Name('name'))->counter(new Label('name', 'value'))->incr();
190+
191+
return $int;
192+
};
193+
194+
$promises = [];
195+
foreach (range(0, 3) as $i) {
196+
$promises[] = $limitedPool->run($fn, [$i, $registryProxy, 0]);
197+
}
198+
199+
$leet = $this->await(
200+
// @phpstan-ignore-next-line
201+
all($promises)->then(static function (array $v) use ($factory): PromiseInterface {
202+
return new Promise(static function (callable $resolve) use ($v, $factory): void {
203+
$factory->loop()->addTimer(3, static function () use ($resolve, $v): void {
204+
$resolve($v);
205+
});
206+
});
207+
})->then(static function () use ($fn, $registryProxy, $limitedPool): PromiseInterface {
208+
return $limitedPool->run($fn, [1337, $registryProxy, 20]);
209+
})->always(static function () use ($limitedPool): void {
210+
$limitedPool->close();
211+
}),
212+
$loop
213+
);
214+
self::assertSame(1337, $leet);
215+
}
172216
}

0 commit comments

Comments
 (0)