[runtime] added v8::Isolate::SafeForTerminationScope and isolate flag
When only_terminate_in_safe_scope flag is passed as CreateParams for v8::Isolate, V8 does not trigger intrruption for termination if there is no explicit SafeForTerminationeScope. Scope enables termination only in direct v8 calls, any recursive calls require explicit SafeForTerminationScope. R=yangguo@chromium.org Bug: chromium:820640 Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng Change-Id: Iac17e30a4b47aa84e70e9218ca0adca9d07f726e Reviewed-on: https://chromium-review.googlesource.com/1025390 Reviewed-by: Yang Guo <yangguo@chromium.org> Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org> Cr-Commit-Position: refs/heads/master@{#52793}
This commit is contained in:
parent
dcdabdc86a
commit
e81b0db787
26
include/v8.h
26
include/v8.h
@ -6896,7 +6896,8 @@ class V8_EXPORT Isolate {
|
|||||||
add_histogram_sample_callback(nullptr),
|
add_histogram_sample_callback(nullptr),
|
||||||
array_buffer_allocator(nullptr),
|
array_buffer_allocator(nullptr),
|
||||||
external_references(nullptr),
|
external_references(nullptr),
|
||||||
allow_atomics_wait(true) {}
|
allow_atomics_wait(true),
|
||||||
|
only_terminate_in_safe_scope(false) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The optional entry_hook allows the host application to provide the
|
* The optional entry_hook allows the host application to provide the
|
||||||
@ -6959,6 +6960,11 @@ class V8_EXPORT Isolate {
|
|||||||
* this isolate. This can also be configured via SetAllowAtomicsWait.
|
* this isolate. This can also be configured via SetAllowAtomicsWait.
|
||||||
*/
|
*/
|
||||||
bool allow_atomics_wait;
|
bool allow_atomics_wait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Termination is postponed when there is no active SafeForTerminationScope.
|
||||||
|
*/
|
||||||
|
bool only_terminate_in_safe_scope;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -7043,6 +7049,24 @@ class V8_EXPORT Isolate {
|
|||||||
internal::Isolate* const isolate_;
|
internal::Isolate* const isolate_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This scope allows terminations inside direct V8 API calls and forbid them
|
||||||
|
* inside any recursice API calls without explicit SafeForTerminationScope.
|
||||||
|
*/
|
||||||
|
class V8_EXPORT SafeForTerminationScope {
|
||||||
|
public:
|
||||||
|
explicit SafeForTerminationScope(v8::Isolate* isolate);
|
||||||
|
~SafeForTerminationScope();
|
||||||
|
|
||||||
|
// Prevent copying of Scope objects.
|
||||||
|
SafeForTerminationScope(const SafeForTerminationScope&) = delete;
|
||||||
|
SafeForTerminationScope& operator=(const SafeForTerminationScope&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
internal::Isolate* isolate_;
|
||||||
|
bool prev_value_;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of garbage collections that can be requested via
|
* Types of garbage collections that can be requested via
|
||||||
* RequestGarbageCollectionForTesting.
|
* RequestGarbageCollectionForTesting.
|
||||||
|
30
src/api.cc
30
src/api.cc
@ -233,10 +233,23 @@ template <bool do_callback>
|
|||||||
class CallDepthScope {
|
class CallDepthScope {
|
||||||
public:
|
public:
|
||||||
explicit CallDepthScope(i::Isolate* isolate, Local<Context> context)
|
explicit CallDepthScope(i::Isolate* isolate, Local<Context> context)
|
||||||
: isolate_(isolate), context_(context), escaped_(false) {
|
: isolate_(isolate),
|
||||||
|
context_(context),
|
||||||
|
escaped_(false),
|
||||||
|
save_for_termination_(isolate->next_v8_call_is_safe_for_termination()) {
|
||||||
// TODO(dcarney): remove this when blink stops crashing.
|
// TODO(dcarney): remove this when blink stops crashing.
|
||||||
DCHECK(!isolate_->external_caught_exception());
|
DCHECK(!isolate_->external_caught_exception());
|
||||||
isolate_->handle_scope_implementer()->IncrementCallDepth();
|
isolate_->handle_scope_implementer()->IncrementCallDepth();
|
||||||
|
isolate_->set_next_v8_call_is_safe_for_termination(false);
|
||||||
|
if (isolate_->only_terminate_in_safe_scope()) {
|
||||||
|
if (save_for_termination_) {
|
||||||
|
interrupts_scope_.reset(new i::SafeForInterruptsScope(
|
||||||
|
isolate_, i::StackGuard::TERMINATE_EXECUTION));
|
||||||
|
} else {
|
||||||
|
interrupts_scope_.reset(new i::PostponeInterruptsScope(
|
||||||
|
isolate_, i::StackGuard::TERMINATE_EXECUTION));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!context.IsEmpty()) {
|
if (!context.IsEmpty()) {
|
||||||
i::Handle<i::Context> env = Utils::OpenHandle(*context);
|
i::Handle<i::Context> env = Utils::OpenHandle(*context);
|
||||||
i::HandleScopeImplementer* impl = isolate->handle_scope_implementer();
|
i::HandleScopeImplementer* impl = isolate->handle_scope_implementer();
|
||||||
@ -261,6 +274,7 @@ class CallDepthScope {
|
|||||||
#ifdef V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY
|
#ifdef V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY
|
||||||
if (do_callback) CheckMicrotasksScopesConsistency(isolate_);
|
if (do_callback) CheckMicrotasksScopesConsistency(isolate_);
|
||||||
#endif
|
#endif
|
||||||
|
isolate_->set_next_v8_call_is_safe_for_termination(save_for_termination_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Escape() {
|
void Escape() {
|
||||||
@ -277,6 +291,8 @@ class CallDepthScope {
|
|||||||
Local<Context> context_;
|
Local<Context> context_;
|
||||||
bool escaped_;
|
bool escaped_;
|
||||||
bool do_callback_;
|
bool do_callback_;
|
||||||
|
bool save_for_termination_;
|
||||||
|
std::unique_ptr<i::InterruptsScope> interrupts_scope_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -8222,6 +8238,8 @@ void Isolate::Initialize(Isolate* isolate,
|
|||||||
i::PrintF("[Initializing isolate from scratch took %0.3f ms]\n", ms);
|
i::PrintF("[Initializing isolate from scratch took %0.3f ms]\n", ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i_isolate->set_only_terminate_in_safe_scope(
|
||||||
|
params.only_terminate_in_safe_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
Isolate* Isolate::New(const Isolate::CreateParams& params) {
|
Isolate* Isolate::New(const Isolate::CreateParams& params) {
|
||||||
@ -8335,6 +8353,16 @@ Isolate::SuppressMicrotaskExecutionScope::~SuppressMicrotaskExecutionScope() {
|
|||||||
isolate_->handle_scope_implementer()->DecrementCallDepth();
|
isolate_->handle_scope_implementer()->DecrementCallDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Isolate::SafeForTerminationScope::SafeForTerminationScope(v8::Isolate* isolate)
|
||||||
|
: isolate_(reinterpret_cast<i::Isolate*>(isolate)),
|
||||||
|
prev_value_(isolate_->next_v8_call_is_safe_for_termination()) {
|
||||||
|
isolate_->set_next_v8_call_is_safe_for_termination(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Isolate::SafeForTerminationScope::~SafeForTerminationScope() {
|
||||||
|
isolate_->set_next_v8_call_is_safe_for_termination(prev_value_);
|
||||||
|
}
|
||||||
|
|
||||||
i::Object** Isolate::GetDataFromSnapshotOnce(size_t index) {
|
i::Object** Isolate::GetDataFromSnapshotOnce(size_t index) {
|
||||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
||||||
i::FixedArray* list = i_isolate->heap()->serialized_objects();
|
i::FixedArray* list = i_isolate->heap()->serialized_objects();
|
||||||
|
@ -440,7 +440,9 @@ typedef std::vector<HeapObject*> DebugObjectCache;
|
|||||||
V(debug::TypeProfile::Mode, type_profile_mode, debug::TypeProfile::kNone) \
|
V(debug::TypeProfile::Mode, type_profile_mode, debug::TypeProfile::kNone) \
|
||||||
V(int, last_stack_frame_info_id, 0) \
|
V(int, last_stack_frame_info_id, 0) \
|
||||||
V(int, last_console_context_id, 0) \
|
V(int, last_console_context_id, 0) \
|
||||||
V(v8_inspector::V8Inspector*, inspector, nullptr)
|
V(v8_inspector::V8Inspector*, inspector, nullptr) \
|
||||||
|
V(bool, next_v8_call_is_safe_for_termination, false) \
|
||||||
|
V(bool, only_terminate_in_safe_scope, false)
|
||||||
|
|
||||||
#define THREAD_LOCAL_TOP_ACCESSOR(type, name) \
|
#define THREAD_LOCAL_TOP_ACCESSOR(type, name) \
|
||||||
inline void set_##name(type v) { thread_local_top_.name##_ = v; } \
|
inline void set_##name(type v) { thread_local_top_.name##_ = v; } \
|
||||||
|
@ -604,6 +604,72 @@ TEST(SafeForTerminateException) {
|
|||||||
AssertFinishedCodeRun(isolate);
|
AssertFinishedCodeRun(isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RequestTermianteAndCallAPI(
|
||||||
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
|
args.GetIsolate()->TerminateExecution();
|
||||||
|
AssertFinishedCodeRun(args.GetIsolate());
|
||||||
|
}
|
||||||
|
|
||||||
|
UNINITIALIZED_TEST(IsolateSafeForTerminationMode) {
|
||||||
|
v8::Isolate::CreateParams create_params;
|
||||||
|
create_params.only_terminate_in_safe_scope = true;
|
||||||
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
||||||
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
||||||
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||||
|
{
|
||||||
|
v8::Isolate::Scope isolate_scope(isolate);
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
|
||||||
|
global->Set(v8_str("terminateAndCallAPI"),
|
||||||
|
v8::FunctionTemplate::New(isolate, RequestTermianteAndCallAPI));
|
||||||
|
v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
|
||||||
|
// Should postpone termination without safe scope.
|
||||||
|
isolate->TerminateExecution();
|
||||||
|
AssertFinishedCodeRun(isolate);
|
||||||
|
{
|
||||||
|
v8::Isolate::SafeForTerminationScope safe_scope(isolate);
|
||||||
|
AssertTerminatedCodeRun(isolate);
|
||||||
|
}
|
||||||
|
AssertFinishedCodeRun(isolate);
|
||||||
|
|
||||||
|
{
|
||||||
|
isolate->TerminateExecution();
|
||||||
|
AssertFinishedCodeRun(isolate);
|
||||||
|
i::PostponeInterruptsScope p1(i_isolate,
|
||||||
|
i::StackGuard::TERMINATE_EXECUTION);
|
||||||
|
{
|
||||||
|
// SafeForTermination overrides postpone.
|
||||||
|
v8::Isolate::SafeForTerminationScope safe_scope(isolate);
|
||||||
|
AssertTerminatedCodeRun(isolate);
|
||||||
|
}
|
||||||
|
AssertFinishedCodeRun(isolate);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
v8::Isolate::SafeForTerminationScope safe_scope(isolate);
|
||||||
|
// Request terminate and call API recursively.
|
||||||
|
CompileRun("terminateAndCallAPI()");
|
||||||
|
AssertTerminatedCodeRun(isolate);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i::PostponeInterruptsScope p1(i_isolate,
|
||||||
|
i::StackGuard::TERMINATE_EXECUTION);
|
||||||
|
// Request terminate and call API recursively.
|
||||||
|
CompileRun("terminateAndCallAPI()");
|
||||||
|
AssertFinishedCodeRun(isolate);
|
||||||
|
}
|
||||||
|
AssertFinishedCodeRun(isolate);
|
||||||
|
{
|
||||||
|
v8::Isolate::SafeForTerminationScope safe_scope(isolate);
|
||||||
|
AssertTerminatedCodeRun(isolate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isolate->Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ErrorObjectAfterTermination) {
|
TEST(ErrorObjectAfterTermination) {
|
||||||
v8::Isolate* isolate = CcTest::isolate();
|
v8::Isolate* isolate = CcTest::isolate();
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
|
Loading…
Reference in New Issue
Block a user