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

Commit 23cf2a0

Browse files
authored
Merge pull request #17 from reactphp-parallel/track-object-instances-in-threads
Track object instances in threads
2 parents 7a6f17d + 3960356 commit 23cf2a0

File tree

7 files changed

+114
-28
lines changed

7 files changed

+114
-28
lines changed

src/AbstractGeneratedProxy.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use ReactParallel\ObjectProxy\Message\Call;
99
use ReactParallel\ObjectProxy\Message\Destruct;
1010

11+
use function spl_object_hash;
12+
1113
abstract class AbstractGeneratedProxy
1214
{
1315
private Channel $out;
@@ -30,6 +32,7 @@ final protected function proxyCallToMainThread(string $interface, string $method
3032
$call = new Call(
3133
$input,
3234
$this->hash,
35+
spl_object_hash($this),
3336
$interface,
3437
$method,
3538
$args,
@@ -44,7 +47,7 @@ final protected function proxyCallToMainThread(string $interface, string $method
4447
final protected function notifyMainThreadAboutDestruction(string $interface): void
4548
{
4649
try {
47-
$this->out->send(new Destruct($this->hash, $interface));
50+
$this->out->send(new Destruct($this->hash, spl_object_hash($this), $interface));
4851
} catch (Channel\Error\Closed $closed) {
4952
// @ignoreException
5053
}

src/Message/Call.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ final class Call
1111
private Channel $channel;
1212

1313
private string $hash;
14+
private string $objectHash;
1415
private string $interface;
1516

1617
private string $method;
@@ -21,13 +22,14 @@ final class Call
2122
/**
2223
* @param mixed[] $args
2324
*/
24-
public function __construct(Channel $channel, string $hash, string $interface, string $method, array $args)
25+
public function __construct(Channel $channel, string $hash, string $objectHash, string $interface, string $method, array $args)
2526
{
26-
$this->channel = $channel;
27-
$this->hash = $hash;
28-
$this->interface = $interface;
29-
$this->method = $method;
30-
$this->args = $args;
27+
$this->channel = $channel;
28+
$this->hash = $hash;
29+
$this->objectHash = $objectHash;
30+
$this->interface = $interface;
31+
$this->method = $method;
32+
$this->args = $args;
3133
}
3234

3335
public function channel(): Channel
@@ -40,6 +42,11 @@ public function hash(): string
4042
return $this->hash;
4143
}
4244

45+
public function objectHash(): string
46+
{
47+
return $this->objectHash;
48+
}
49+
4350
public function interface(): string
4451
{
4552
return $this->interface;

src/Message/Destruct.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,26 @@
77
final class Destruct
88
{
99
private string $hash;
10+
private string $objectHash;
1011
private string $interface;
1112

12-
public function __construct(string $hash, string $interface)
13+
public function __construct(string $hash, string $objectHash, string $interface)
1314
{
14-
$this->hash = $hash;
15-
$this->interface = $interface;
15+
$this->hash = $hash;
16+
$this->objectHash = $objectHash;
17+
$this->interface = $interface;
1618
}
1719

1820
public function hash(): string
1921
{
2022
return $this->hash;
2123
}
2224

25+
public function objectHash(): string
26+
{
27+
return $this->objectHash;
28+
}
29+
2330
public function interface(): string
2431
{
2532
return $this->interface;

src/Proxy.php

Lines changed: 26 additions & 11 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\Proxy\Instance;
1213
use WyriHaximus\Metrics\Label;
1314
use WyriHaximus\Metrics\Registry;
1415
use WyriHaximus\Metrics\Registry\Counters;
@@ -30,7 +31,7 @@ final class Proxy extends ProxyList
3031

3132
private Channel $in;
3233

33-
/** @var array<string, object> */
34+
/** @var array<string, Instance> */
3435
private array $instances = [];
3536

3637
/** @var array<string, string|false> */
@@ -61,6 +62,7 @@ public function withMetrics(Registry $registry): self
6162
$self->counterDestruct = $registry->counter(
6263
'react_parallel_object_proxy_destruct',
6364
'Number of destroyed proxies by the garbage collector',
65+
new Label\Name('class'),
6466
new Label\Name('interface'),
6567
);
6668
$self->in = new Channel(Channel::Infinite);
@@ -80,14 +82,14 @@ public function create(object $object, string $interface): object
8082
throw NonExistentInterface::create($interface);
8183
}
8284

83-
if ($this->counterCreate instanceof Counters) {
84-
$this->counterCreate->counter(new Label('class', get_class($object)), new Label('interface', $interface))->incr();
85-
}
86-
8785
$class = self::KNOWN_INTERFACE[$interface];
8886
$hash = bin2hex(random_bytes(13));
8987

90-
$this->instances[$hash] = $object;
88+
$this->instances[$hash] = new Instance($object, $interface);
89+
90+
if ($this->counterCreate instanceof Counters) {
91+
$this->counterCreate->counter(new Label('class', $this->instances[$hash]->class()), new Label('interface', $interface))->incr();
92+
}
9193

9294
/** @psalm-suppress InvalidStringClass */
9395
return new $class($this->in, $hash);
@@ -117,13 +119,18 @@ private function setUpHandlers(): void
117119

118120
private function handleCall(Call $call): void
119121
{
120-
$object = $this->instances[$call->hash()];
122+
if (! array_key_exists($call->hash(), $this->instances)) {
123+
return;
124+
}
125+
126+
$instance = $this->instances[$call->hash()];
127+
$instance->reference($call->objectHash());
121128
if ($this->counterCall instanceof Counters) {
122-
$this->counterCall->counter(new Label('class', get_class($object)), new Label('interface', $call->interface()))->incr();
129+
$this->counterCall->counter(new Label('class', $instance->class()), new Label('interface', $instance->interface()))->incr();
123130
}
124131

125132
/** @phpstan-ignore-next-line */
126-
$outcome = $object->{$call->method()}(...$call->args());
133+
$outcome = $instance->object()->{$call->method()}(...$call->args());
127134

128135
if (is_object($outcome)) {
129136
$outcomeClass = get_class($outcome);
@@ -142,13 +149,21 @@ private function handleCall(Call $call): void
142149

143150
private function handleDestruct(Destruct $destruct): void
144151
{
145-
unset($this->instances[$destruct->hash()]);
152+
if (! array_key_exists($destruct->hash(), $this->instances)) {
153+
return;
154+
}
155+
156+
$instance = $this->instances[$destruct->hash()];
157+
$count = $instance->dereference($destruct->objectHash());
158+
if ($count === 0) {
159+
unset($this->instances[$destruct->hash()]);
160+
}
146161

147162
if (! ($this->counterDestruct instanceof Counters)) {
148163
return;
149164
}
150165

151-
$this->counterDestruct->counter(new Label('interface', $destruct->interface()))->incr();
166+
$this->counterDestruct->counter(new Label('class', $instance->class()), new Label('interface', $instance->interface()))->incr();
152167
}
153168

154169
/** @phpstan-ignore-next-line */

src/Proxy/Instance.php

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

tests/Message/CallTest.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@ final class CallTest extends AsyncTestCase
2020
*/
2121
public function getters(): void
2222
{
23-
$channel = new Channel(1);
24-
$hash = bin2hex(random_bytes(1024));
25-
$interface = LoggerInterface::class;
26-
$method = 'hammer';
27-
$args = [time()];
23+
$channel = new Channel(1);
24+
$hash = bin2hex(random_bytes(1024));
25+
$objectHash = bin2hex(random_bytes(1024));
26+
$interface = LoggerInterface::class;
27+
$method = 'hammer';
28+
$args = [time()];
2829

29-
$call = new Call($channel, $hash, $interface, $method, $args);
30+
$call = new Call($channel, $hash, $objectHash, $interface, $method, $args);
3031

3132
self::assertSame($channel, $call->channel());
3233
self::assertSame($hash, $call->hash());
34+
self::assertSame($objectHash, $call->objectHash());
3335
self::assertSame($interface, $call->interface());
3436
self::assertSame($method, $call->method());
3537
self::assertSame($args, $call->args());

tests/ProxyTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,6 @@ public function metricsDestructionTesting(): void
149149
self::assertStringContainsString('react_parallel_object_proxy_call_total{class="WyriHaximus\Metrics\InMemory\Registry",interface="WyriHaximus\Metrics\Registry"} 101', $txt);
150150
self::assertStringContainsString('react_parallel_object_proxy_call_total{class="WyriHaximus\Metrics\InMemory\Registry\Counters",interface="WyriHaximus\Metrics\Registry\Counters"} 101', $txt);
151151
self::assertStringContainsString('react_parallel_object_proxy_call_total{class="WyriHaximus\Metrics\InMemory\Counter",interface="WyriHaximus\Metrics\Counter"} 101', $txt);
152-
self::assertStringContainsString('aaareact_parallel_object_proxy_call_total{class="WyriHaximus\Metrics\InMemory\Counter",interface="WyriHaximus\Metrics\Counter"} 101', $txt);
152+
self::assertStringContainsString('react_parallel_object_proxy_destruct_total{class="WyriHaximus\Metrics\InMemory\Registry",interface="WyriHaximus\Metrics\Registry"} 13', $txt);
153153
}
154154
}

0 commit comments

Comments
 (0)