Skip to content
This repository was archived by the owner on Mar 29, 2024. It is now read-only.

Commit e4acbe6

Browse files
committed
Properly free isolates on unclean shutdown
This fixes segfaults when isolate entered multiple time or when multiple isolates entered and unclean shutdown performed (when die/exit called or uncaught exception occurs).
1 parent 3bc8588 commit e4acbe6

File tree

4 files changed

+154
-29
lines changed

4 files changed

+154
-29
lines changed

src/php_v8_isolate.cc

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,21 @@ static void php_v8_maybe_terminate_execution(php_v8_isolate_t *php_v8_isolate) {
4444
}
4545

4646
static inline void php_v8_isolate_destroy(php_v8_isolate_t *php_v8_isolate) {
47+
v8::Isolate *isolate = nullptr;
48+
4749
if (php_v8_isolate->isolate) {
4850

4951
php_v8_maybe_terminate_execution(php_v8_isolate);
5052

51-
while (php_v8_isolate->isolate->InContext()) {
52-
v8::Local<v8::Context> context = php_v8_isolate->isolate->GetEnteredContext();
53-
context->Exit();
54-
}
55-
56-
if (php_v8_isolate->isolate == v8::Isolate::GetCurrent()) {
57-
php_v8_isolate->isolate->Exit();
53+
if (CG(unclean_shutdown)) {
54+
// freeing order is not guaranteed upon unclean shutdown, so we explicitly exit all entered isolates,
55+
// to ensure that current one won't remain entered so that we'll properly dispose it below
56+
while ( (isolate = v8::Isolate::GetCurrent())) {
57+
isolate->Exit();
58+
}
5859
}
5960

60-
php_v8_isolate->isolate->Dispose(); // this cause error when we try to call on already entered context
61+
php_v8_isolate->isolate->Dispose(); // this cause error when we try to call on already entered isolate
6162
}
6263
}
6364

tests/.tracking_dtors.php

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,50 @@
1414

1515
namespace v8Tests\TrackingDtors;
1616

17-
class Isolate extends \V8\Isolate {
18-
public function __destruct() {
19-
echo 'Isolate dies now!', PHP_EOL;
17+
18+
trait DestructMessageAwareTrait {
19+
//private $message = '';
20+
21+
//public function setOnDestructMessage($message) {
22+
// $this->message = $message;
23+
//}
24+
25+
public function __destruct()
26+
{
27+
if (isset($this->destructor_test_message)) {
28+
$message = $this->destructor_test_message;
29+
}else {
30+
$message = (new \ReflectionClass($this))->getShortName() . ' dies now!';
31+
}
32+
33+
echo $message, PHP_EOL;
2034
}
2135
}
2236

37+
class Isolate extends \V8\Isolate {
38+
use DestructMessageAwareTrait;
39+
}
40+
2341
class Context extends \V8\Context {
24-
public function __destruct() {
25-
echo 'Context dies now!', PHP_EOL;
26-
}
42+
use DestructMessageAwareTrait;
2743
}
2844

2945
class Script extends \V8\Script {
30-
public function __destruct() {
31-
echo 'Script dies now!', PHP_EOL;
32-
}
46+
use DestructMessageAwareTrait;
3347
}
3448

3549
class FunctionTemplate extends \V8\FunctionTemplate {
36-
public function __destruct() {
37-
echo 'FunctionTemplate dies now!', PHP_EOL;
38-
}
50+
use DestructMessageAwareTrait;
3951
}
4052

4153
class ObjectTemplate extends \V8\ObjectTemplate {
42-
public function __destruct() {
43-
echo 'ObjectTemplate dies now!', PHP_EOL;
44-
}
54+
use DestructMessageAwareTrait;
4555
}
4656

4757
class FunctionObject extends \V8\FunctionObject {
48-
public function __destruct() {
49-
echo 'FunctionObject dies now!', PHP_EOL;
50-
}
58+
use DestructMessageAwareTrait;
5159
}
5260

5361
class Value extends \V8\Value {
54-
public function __destruct() {
55-
echo 'Value dies now!', PHP_EOL;
56-
}
62+
use DestructMessageAwareTrait;
5763
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
V8\FunctionObject - test die() called from different Isolate
3+
--SKIPIF--
4+
<?php if (!extension_loaded("v8")) {
5+
print "skip";
6+
} ?>
7+
--FILE--
8+
<?php
9+
10+
/** @var \Phpv8Testsuite $helper */
11+
$helper = require '.testsuite.php';
12+
13+
require '.v8-helpers.php';
14+
$v8_helper = new PhpV8Helpers($helper);
15+
16+
require '.tracking_dtors.php';
17+
18+
$isolate_inner = new v8Tests\TrackingDtors\Isolate();
19+
$context_inner = new V8\Context($isolate_inner);
20+
21+
$die_func = new v8Tests\TrackingDtors\FunctionObject($context_inner, function (\V8\FunctionCallbackInfo $info) {
22+
echo 'going to die...', PHP_EOL;
23+
die();
24+
});
25+
26+
$context_inner->GlobalObject()->Set($context_inner, new \V8\StringValue($isolate_inner, 'die'), $die_func);
27+
28+
$die_func->destructor_test_message = 'die() function from inner isolate dtored';
29+
$isolate_inner->destructor_test_message = 'inner isolate dtored';
30+
31+
32+
$isolate_outer = new v8Tests\TrackingDtors\Isolate();
33+
$context_outer = new V8\Context($isolate_outer);
34+
35+
36+
$test_other_func = new v8Tests\TrackingDtors\FunctionObject($context_outer, function (\V8\FunctionCallbackInfo $info) use ($context_inner) {
37+
echo 'calling inner...', PHP_EOL;
38+
$isolate_inner = $context_inner->GetIsolate();
39+
$global_inner = $context_inner->GlobalObject();
40+
41+
$global_inner->Get($context_inner, new \V8\StringValue($isolate_inner, 'die'))->Call($context_inner, $global_inner);
42+
});
43+
44+
$test_other_func->destructor_test_message = 'test_other() function from outer isolate dtored';
45+
$isolate_outer->destructor_test_message = 'outer isolate dtored';
46+
47+
48+
$context_outer->GlobalObject()->Set($context_outer, new \V8\StringValue($isolate_outer, 'test_other'), $test_other_func);
49+
50+
51+
$res = $v8_helper->CompileRun($context_outer, 'test_other(); "Script done"');
52+
53+
$helper->pretty_dump('Script result', $res->ToString($context_outer)->Value());
54+
55+
echo 'We are done for now', PHP_EOL;
56+
57+
?>
58+
--EXPECT--
59+
calling inner...
60+
going to die...
61+
test_other() function from outer isolate dtored
62+
inner isolate dtored
63+
die() function from inner isolate dtored
64+
outer isolate dtored
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
V8\FunctionObject - test die() during nested calling
3+
--SKIPIF--
4+
<?php if (!extension_loaded("v8")) print "skip"; ?>
5+
--FILE--
6+
<?php
7+
8+
/** @var \Phpv8Testsuite $helper */
9+
$helper = require '.testsuite.php';
10+
11+
require '.v8-helpers.php';
12+
$v8_helper = new PhpV8Helpers($helper);
13+
14+
require '.tracking_dtors.php';
15+
16+
$isolate1 = new v8Tests\TrackingDtors\Isolate();
17+
$global_template1 = new V8\ObjectTemplate($isolate1);
18+
$context1 = new V8\Context($isolate1, $global_template1);
19+
20+
21+
$die_func = new v8Tests\TrackingDtors\FunctionObject($context1, function (\V8\FunctionCallbackInfo $info) {
22+
echo 'going to die...', PHP_EOL;
23+
die();
24+
});
25+
26+
$die_func->destructor_test_message = 'die() function dtored';
27+
28+
$teste_nested_func = new v8Tests\TrackingDtors\FunctionObject($context1, function (\V8\FunctionCallbackInfo $info) {
29+
echo 'calling nested...', PHP_EOL;
30+
$context = $info->GetContext();
31+
32+
$context->GlobalObject()->Get($context, new \V8\StringValue($context->GetIsolate(), 'die'))->Call($context, $context->GlobalObject());
33+
});
34+
35+
$teste_nested_func->destructor_test_message = 'test_nested() function dtored';
36+
37+
38+
$context1->GlobalObject()->Set($context1, new \V8\StringValue($isolate1, 'die'), $die_func);
39+
$context1->GlobalObject()->Set($context1, new \V8\StringValue($isolate1, 'test_nested'), $teste_nested_func);
40+
41+
42+
$res = $v8_helper->CompileRun($context1, 'test_nested(); "Script done"');
43+
44+
$helper->pretty_dump('Script result', $res->ToString($context1)->Value());
45+
46+
echo 'We are done for now', PHP_EOL;
47+
48+
?>
49+
--EXPECT--
50+
calling nested...
51+
going to die...
52+
test_nested() function dtored
53+
Isolate dies now!
54+
die() function dtored

0 commit comments

Comments
 (0)