[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:
Alexey Kozyatinskiy 2018-04-25 09:35:56 -07:00 committed by Commit Bot
parent dcdabdc86a
commit e81b0db787
4 changed files with 123 additions and 3 deletions

View File

@ -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.

View File

@ -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();

View File

@ -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; } \

View File

@ -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);