[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),
|
||||
array_buffer_allocator(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
|
||||
@ -6959,6 +6960,11 @@ class V8_EXPORT Isolate {
|
||||
* this isolate. This can also be configured via SetAllowAtomicsWait.
|
||||
*/
|
||||
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_;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* RequestGarbageCollectionForTesting.
|
||||
|
30
src/api.cc
30
src/api.cc
@ -233,10 +233,23 @@ template <bool do_callback>
|
||||
class CallDepthScope {
|
||||
public:
|
||||
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.
|
||||
DCHECK(!isolate_->external_caught_exception());
|
||||
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()) {
|
||||
i::Handle<i::Context> env = Utils::OpenHandle(*context);
|
||||
i::HandleScopeImplementer* impl = isolate->handle_scope_implementer();
|
||||
@ -261,6 +274,7 @@ class CallDepthScope {
|
||||
#ifdef V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY
|
||||
if (do_callback) CheckMicrotasksScopesConsistency(isolate_);
|
||||
#endif
|
||||
isolate_->set_next_v8_call_is_safe_for_termination(save_for_termination_);
|
||||
}
|
||||
|
||||
void Escape() {
|
||||
@ -277,6 +291,8 @@ class CallDepthScope {
|
||||
Local<Context> context_;
|
||||
bool escaped_;
|
||||
bool do_callback_;
|
||||
bool save_for_termination_;
|
||||
std::unique_ptr<i::InterruptsScope> interrupts_scope_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -8222,6 +8238,8 @@ void Isolate::Initialize(Isolate* isolate,
|
||||
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) {
|
||||
@ -8335,6 +8353,16 @@ Isolate::SuppressMicrotaskExecutionScope::~SuppressMicrotaskExecutionScope() {
|
||||
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::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
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(int, last_stack_frame_info_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) \
|
||||
inline void set_##name(type v) { thread_local_top_.name##_ = v; } \
|
||||
|
@ -604,6 +604,72 @@ TEST(SafeForTerminateException) {
|
||||
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) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
Loading…
Reference in New Issue
Block a user