Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b353cf3
benchmark: fix punycode test for --without-intl
TimothyGu Oct 17, 2017
218b8fd
fs: fix `createReadStream(…, {end: n})` for non-seekable fds
addaleax Mar 13, 2018
986fd40
util: escaping object keys in util.inspect()
zheyz Nov 13, 2017
f0f7c2b
http: free the parser before emitting 'upgrade'
lpinca Jan 17, 2018
f068918
stream: remove unreachable code
lpinca Jan 18, 2018
9a56ad9
test: use correct size in test-stream-buffer-list
lpinca Jan 18, 2018
b665068
stream: simplify `src._readableState` to `state`
Jan 20, 2018
325dc27
doc: capitalize non-primitive types
vsemozhetbyt Jan 12, 2018
ddb42b5
doc: warn about GCM authenticity
tniessen Jan 25, 2018
9f02f29
doc: fix manpage warnings
silverwind Jan 29, 2018
c28a64a
doc: reorder section on updating PR branch
ofrobots Jan 24, 2018
67f9df9
url: simplify loop in parser
tniessen Jan 30, 2018
06c1ef7
doc: add Gibson Fahnestock to TSC
Trott Jan 31, 2018
de63954
test: fix flaky test-http-dns-error
bengl Oct 26, 2017
06a28aa
src: free memory before re-setting URLHost value
prog1dev Jan 23, 2018
4f1939b
tools: auto fix custom eslint rule
shobhitchittora Oct 31, 2017
9b0d337
doc: improve stream documentation
Jan 25, 2018
02eab11
test: speed up parallel/test-tls-session-cache
addaleax Jan 29, 2018
9f158ac
lib: remove debugger dead code
liqyan Jan 22, 2018
f4ccb9d
deps: update node-inspect to 1.11.3
Jan 24, 2018
cad0bbb
async_hooks: clean up comments
ofrobots Jan 30, 2018
b8d3c40
doc: move Brian White to TSC Emeriti list
Trott Jan 31, 2018
091ab5b
v8: add missing ',' in OpenBSD's 'sources' section.
qbit Jan 30, 2018
d594a68
build: add cflags for OpenBSD, remove stray comma.
qbit Jan 30, 2018
e6d6042
doc: streamline README intro
Trott Jan 31, 2018
5e86f20
string_decoder: reset decoder on end
jridgewell Feb 1, 2018
c570180
test: add test for tls benchmarks
apapirovski Feb 1, 2018
6ef1fa3
test: verify the shell option works properly on execFile
jvelezpo Jan 25, 2018
21441e8
win, build: fix intl-none option
poiru Jan 22, 2018
07f9147
doc: improve http.request documentation
Zarel Jan 8, 2018
0a5b0d5
doc: shell option for the execFile and execFileSync functions
jvelezpo Jan 18, 2018
89c1bf3
build: allow x86_64 as a dest_cpu alias for x64
rvagg Jan 9, 2018
2a18de3
test: remove destructor from node_test_fixture
danbev Feb 2, 2018
af3e5f2
net: remove redundant code from _writeGeneric()
lpinca Jan 29, 2018
433be0d
tools: add check for using process.binding crypto
danbev Dec 26, 2017
a3111ba
build: refine static and shared lib build
yhwang Dec 5, 2017
5590088
errors: remove ERR_OUTOFMEMORY
tniessen Dec 27, 2017
059a803
test: fix spelling in test case comments
tniessen Jan 6, 2018
63b0de2
lib: fix spelling in comments
tniessen Jan 6, 2018
790ef2f
test: update references to archived repository
tniessen Dec 31, 2017
8c25003
readline: update references to archived repository
tniessen Dec 31, 2017
f687bb1
test: move tmpdir to submodule of common
Trott Dec 25, 2017
9f380dc
tools: add .mjs linting for Windows
vsemozhetbyt Feb 4, 2018
88e0f23
test: add assertions for TextEncoder/Decoder
Jan 13, 2018
d503427
doc: linkify missing types
vsemozhetbyt Jan 29, 2018
bc66f08
deps: V8: backport 596d55a from upstream
MylesBorins Sep 18, 2017
44cb0d3
crypto: reuse variable instead of reevaluation
tniessen Dec 18, 2017
6ba7fd0
tools: non-Ascii linter for /lib only
Jan 8, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
deps: V8: backport 596d55a from upstream
Did not remove ActivationsFinder from `src/runtime/runtime-compiler.cc`
as in the original commit as the Class is still being used prior to
f0acede landing

Original Commit Message:

    Deoptimization and multithreading.

    When using Lockers and Unlockers it is possible to create a
    scenario where multiple threads point to the same optimized
    code object. When that happens, if one of the threads triggers
    deoptimization, then the stack replacement needs to happen in
    the stacks of all threads.
    With this CL, the deoptimizer visits all threads to do so.
    The CL also adds three tests where V8 used to crash due to this
    issue.

    Bug: v8:6563
    Change-Id: I74e9af472d4833aa8d13e579df45133791f6a503
    Reviewed-on: https://chromium-review.googlesource.com/670783
    Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
    Commit-Queue: Juliana Patricia Vicente Franco <jupvfranco@google.com>
    Cr-Commit-Position: refs/heads/master@{#48060}

PR-URL: #19477
Fixes: #19274
Refs: v8/v8@596d55a
Refs: v8/v8@f0acede
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Yang Guo <yangguo@chromium.org>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
  • Loading branch information
MylesBorins committed Mar 28, 2018
commit bc66f08057ba84548080b74dfa6434d80d80b233
77 changes: 57 additions & 20 deletions deps/v8/src/deoptimizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,50 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
generator.Generate();
}

namespace {
class ActivationsFinder : public ThreadVisitor {
public:
explicit ActivationsFinder(std::set<Code*>* codes,
Code* topmost_optimized_code,
bool safe_to_deopt_topmost_optimized_code)
: codes_(codes) {
#ifdef DEBUG
topmost_ = topmost_optimized_code;
safe_to_deopt_ = safe_to_deopt_topmost_optimized_code;
#endif
}

// Find the frames with activations of codes marked for deoptimization, search
// for the trampoline to the deoptimizer call respective to each code, and use
// it to replace the current pc on the stack.
void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
if (it.frame()->type() == StackFrame::OPTIMIZED) {
Code* code = it.frame()->LookupCode();
if (code->kind() == Code::OPTIMIZED_FUNCTION &&
code->marked_for_deoptimization()) {
codes_->erase(code);
// Obtain the trampoline to the deoptimizer call.
SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc());
int trampoline_pc = safepoint.trampoline_pc();
DCHECK_IMPLIES(code == topmost_, safe_to_deopt_);
// Replace the current pc on the stack with the trampoline.
it.frame()->set_pc(code->instruction_start() + trampoline_pc);
}
}
}
}

private:
std::set<Code*>* codes_;

#ifdef DEBUG
Code* topmost_;
bool safe_to_deopt_;
#endif
};
} // namespace

void Deoptimizer::VisitAllOptimizedFunctionsForContext(
Context* context, OptimizedFunctionVisitor* visitor) {
DisallowHeapAllocation no_allocation;
Expand Down Expand Up @@ -264,9 +308,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
VisitAllOptimizedFunctionsForContext(context, &unlinker);

Isolate* isolate = context->GetHeap()->isolate();
#ifdef DEBUG
Code* topmost_optimized_code = NULL;
bool safe_to_deopt_topmost_optimized_code = false;
#ifdef DEBUG
// Make sure all activations of optimized code can deopt at their current PC.
// The topmost optimized code has special handling because it cannot be
// deoptimized due to weak object dependency.
Expand Down Expand Up @@ -304,6 +348,10 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
}
#endif

// We will use this set to mark those Code objects that are marked for
// deoptimization and have not been found in stack frames.
std::set<Code*> codes;

// Move marked code from the optimized code list to the deoptimized
// code list.
// Walk over all optimized code objects in this native context.
Expand Down Expand Up @@ -335,25 +383,14 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
element = next;
}

// Finds the with activations of codes marked for deoptimization, search for
// the trampoline to the deoptimizer call respective to each code, and use it
// to replace the current pc on the stack.
for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done();
it.Advance()) {
if (it.frame()->type() == StackFrame::OPTIMIZED) {
Code* code = it.frame()->LookupCode();
if (code->kind() == Code::OPTIMIZED_FUNCTION &&
code->marked_for_deoptimization()) {
// Obtain the trampoline to the deoptimizer call.
SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc());
int trampoline_pc = safepoint.trampoline_pc();
DCHECK_IMPLIES(code == topmost_optimized_code,
safe_to_deopt_topmost_optimized_code);
// Replace the current pc on the stack with the trampoline.
it.frame()->set_pc(code->instruction_start() + trampoline_pc);
}
}
}
ActivationsFinder visitor(&codes, topmost_optimized_code,
safe_to_deopt_topmost_optimized_code);
// Iterate over the stack of this thread.
visitor.VisitThread(isolate, isolate->thread_local_top());
// In addition to iterate over the stack of this thread, we also
// need to consider all the other threads as they may also use
// the code currently beings deoptimized.
isolate->thread_manager()->IterateArchivedThreads(&visitor);
}


Expand Down
238 changes: 238 additions & 0 deletions deps/v8/test/cctest/test-lockers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,244 @@ using ::v8::Value;
using ::v8::V8;


namespace {

class DeoptimizeCodeThread : public v8::base::Thread {
public:
DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local<v8::Context> context,
const char* trigger)
: Thread(Options("DeoptimizeCodeThread")),
isolate_(isolate),
context_(isolate, context),
source_(trigger) {}

void Run() {
v8::Locker locker(isolate_);
isolate_->Enter();
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, context_);
v8::Context::Scope context_scope(context);
CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
// This code triggers deoptimization of some function that will be
// used in a different thread.
CompileRun(source_);
isolate_->Exit();
}

private:
v8::Isolate* isolate_;
Persistent<v8::Context> context_;
// The code that triggers the deoptimization.
const char* source_;
};

void UnlockForDeoptimization(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
// Gets the pointer to the thread that will trigger the deoptimization of the
// code.
DeoptimizeCodeThread* deoptimizer =
reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
{
// Exits and unlocks the isolate.
isolate->Exit();
v8::Unlocker unlocker(isolate);
// Starts the deoptimizing thread.
deoptimizer->Start();
// Waits for deoptimization to finish.
deoptimizer->Join();
}
// The deoptimizing thread has finished its work, and the isolate
// will now be used by the current thread.
isolate->Enter();
}

void UnlockForDeoptimizationIfReady(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
bool* ready_to_deoptimize = reinterpret_cast<bool*>(isolate->GetData(1));
if (*ready_to_deoptimize) {
// The test should enter here only once, so put the flag back to false.
*ready_to_deoptimize = false;
// Gets the pointer to the thread that will trigger the deoptimization of
// the code.
DeoptimizeCodeThread* deoptimizer =
reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
{
// Exits and unlocks the thread.
isolate->Exit();
v8::Unlocker unlocker(isolate);
// Starts the thread that deoptimizes the function.
deoptimizer->Start();
// Waits for the deoptimizing thread to finish.
deoptimizer->Join();
}
// The deoptimizing thread has finished its work, and the isolate
// will now be used by the current thread.
isolate->Enter();
}
}
} // namespace

TEST(LazyDeoptimizationMultithread) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
const char* trigger_deopt = "obj = { y: 0, x: 1 };";

// We use the isolate to pass arguments to the UnlockForDeoptimization
// function. Namely, we pass a pointer to the deoptimizing thread.
DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
isolate->SetData(0, &deoptimize_thread);
v8::Context::Scope context_scope(context);

// Create the function templace for C++ code that is invoked from
// JavaScript code.
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, UnlockForDeoptimization);
Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
CHECK(context->Global()
->Set(context, v8_str("unlock_for_deoptimization"), fun)
.FromJust());

// Optimizes a function f, which will be deoptimized in another
// thread.
CompileRun(
"var b = false; var obj = { x: 1 };"
"function f() { g(); return obj.x; }"
"function g() { if (b) { unlock_for_deoptimization(); } }"
"%NeverOptimizeFunction(g);"
"f(); f(); %OptimizeFunctionOnNextCall(f);"
"f();");

// Trigger the unlocking.
Local<Value> v = CompileRun("b = true; f();");

// Once the isolate has been unlocked, the thread will wait for the
// other thread to finish its task. Once this happens, this thread
// continues with its execution, that is, with the execution of the
// function g, which then returns to f. The function f should have
// also been deoptimized. If the replacement did not happen on this
// thread's stack, then the test will fail here.
CHECK(v->IsNumber());
CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
}
isolate->Dispose();
}

TEST(LazyDeoptimizationMultithreadWithNatives) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
const char* trigger_deopt = "%DeoptimizeFunction(f);";

// We use the isolate to pass arguments to the UnlockForDeoptimization
// function. Namely, we pass a pointer to the deoptimizing thread.
DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
isolate->SetData(0, &deoptimize_thread);
bool ready_to_deopt = false;
isolate->SetData(1, &ready_to_deopt);
v8::Context::Scope context_scope(context);

// Create the function templace for C++ code that is invoked from
// JavaScript code.
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
CHECK(context->Global()
->Set(context, v8_str("unlock_for_deoptimization"), fun)
.FromJust());

// Optimizes a function f, which will be deoptimized in another
// thread.
CompileRun(
"var obj = { x: 1 };"
"function f() { g(); return obj.x;}"
"function g() { "
" unlock_for_deoptimization(); }"
"%NeverOptimizeFunction(g);"
"f(); f(); %OptimizeFunctionOnNextCall(f);");

// Trigger the unlocking.
ready_to_deopt = true;
isolate->SetData(1, &ready_to_deopt);
Local<Value> v = CompileRun("f();");

// Once the isolate has been unlocked, the thread will wait for the
// other thread to finish its task. Once this happens, this thread
// continues with its execution, that is, with the execution of the
// function g, which then returns to f. The function f should have
// also been deoptimized. Otherwise, the test will fail here.
CHECK(v->IsNumber());
CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
}
isolate->Dispose();
}

TEST(EagerDeoptimizationMultithread) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
const char* trigger_deopt = "f({y: 0, x: 1});";

// We use the isolate to pass arguments to the UnlockForDeoptimization
// function. Namely, we pass a pointer to the deoptimizing thread.
DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
isolate->SetData(0, &deoptimize_thread);
bool ready_to_deopt = false;
isolate->SetData(1, &ready_to_deopt);
v8::Context::Scope context_scope(context);

// Create the function templace for C++ code that is invoked from
// JavaScript code.
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
CHECK(context->Global()
->Set(context, v8_str("unlock_for_deoptimization"), fun)
.FromJust());

// Optimizes a function f, which will be deoptimized by another thread.
CompileRun(
"function f(obj) { unlock_for_deoptimization(); return obj.x; }"
"f({x: 1}); f({x: 1});"
"%OptimizeFunctionOnNextCall(f);"
"f({x: 1});");

// Trigger the unlocking.
ready_to_deopt = true;
isolate->SetData(1, &ready_to_deopt);
Local<Value> v = CompileRun("f({x: 1});");

// Once the isolate has been unlocked, the thread will wait for the
// other thread to finish its task. Once this happens, this thread
// continues with its execution, that is, with the execution of the
// function g, which then returns to f. The function f should have
// also been deoptimized. Otherwise, the test will fail here.
CHECK(v->IsNumber());
CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
}
isolate->Dispose();
}

// Migrating an isolate
class KangarooThread : public v8::base::Thread {
public:
Expand Down