2011-05-05 18:55:31 +00:00
|
|
|
// Copyright 2007-2011 the V8 project authors. All rights reserved.
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are
|
|
|
|
// met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above
|
|
|
|
// copyright notice, this list of conditions and the following
|
|
|
|
// disclaimer in the documentation and/or other materials provided
|
|
|
|
// with the distribution.
|
|
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
|
|
// contributors may be used to endorse or promote products derived
|
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
2016-07-25 11:12:42 +00:00
|
|
|
#include <memory>
|
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/v8.h"
|
2011-05-05 18:55:31 +00:00
|
|
|
|
2014-06-30 13:25:46 +00:00
|
|
|
#include "src/base/platform/platform.h"
|
2019-05-21 09:30:15 +00:00
|
|
|
#include "src/codegen/compilation-cache.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/execution.h"
|
|
|
|
#include "src/isolate.h"
|
2017-01-09 13:43:28 +00:00
|
|
|
#include "src/objects-inl.h"
|
2019-05-21 06:38:38 +00:00
|
|
|
#include "src/strings/unicode-inl.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/utils.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
2011-05-05 18:55:31 +00:00
|
|
|
|
2017-09-18 08:30:52 +00:00
|
|
|
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) {}
|
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2017-09-18 08:30:52 +00:00
|
|
|
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_;
|
2017-09-21 03:29:52 +00:00
|
|
|
v8::Persistent<v8::Context> context_;
|
2017-09-18 08:30:52 +00:00
|
|
|
// 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
|
|
|
|
|
2017-09-21 03:29:52 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace test_lockers {
|
|
|
|
|
2017-09-18 08:30:52 +00:00
|
|
|
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);"
|
2019-04-30 11:04:41 +00:00
|
|
|
"%PrepareFunctionForOptimization(f);"
|
2017-09-18 08:30:52 +00:00
|
|
|
"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);"
|
2019-04-30 11:04:41 +00:00
|
|
|
"%PrepareFunctionForOptimization(f);"
|
2017-09-18 08:30:52 +00:00
|
|
|
"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; }"
|
2019-04-30 11:04:41 +00:00
|
|
|
"%PrepareFunctionForOptimization(f);"
|
2017-09-18 08:30:52 +00:00
|
|
|
"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();
|
|
|
|
}
|
2011-05-05 18:55:31 +00:00
|
|
|
|
|
|
|
// Migrating an isolate
|
2014-06-30 13:25:46 +00:00
|
|
|
class KangarooThread : public v8::base::Thread {
|
2011-05-05 18:55:31 +00:00
|
|
|
public:
|
2015-11-18 08:22:07 +00:00
|
|
|
KangarooThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
|
2014-08-12 13:33:35 +00:00
|
|
|
: Thread(Options("KangarooThread")),
|
2013-04-24 14:23:46 +00:00
|
|
|
isolate_(isolate),
|
|
|
|
context_(isolate, context) {}
|
2011-05-05 18:55:31 +00:00
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Locker locker(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2015-11-23 12:38:18 +00:00
|
|
|
CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(isolate_);
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Local<v8::Context>::New(isolate_, context_);
|
|
|
|
v8::Context::Scope context_scope(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
Local<Value> v = CompileRun("getValue()");
|
|
|
|
CHECK(v->IsNumber());
|
2015-11-18 08:22:07 +00:00
|
|
|
CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Locker locker(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(isolate_);
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Local<v8::Context>::New(isolate_, context_);
|
|
|
|
v8::Context::Scope context_scope(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
Local<Value> v = CompileRun("getValue()");
|
|
|
|
CHECK(v->IsNumber());
|
2015-11-18 08:22:07 +00:00
|
|
|
CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
isolate_->Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
v8::Isolate* isolate_;
|
2017-09-21 03:29:52 +00:00
|
|
|
v8::Persistent<v8::Context> context_;
|
2011-05-05 18:55:31 +00:00
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Migrates an isolate from one thread to another
|
|
|
|
TEST(KangarooIsolates) {
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2016-07-25 11:12:42 +00:00
|
|
|
std::unique_ptr<KangarooThread> thread1;
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Locker locker(isolate);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
2013-04-24 14:23:46 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-23 12:38:18 +00:00
|
|
|
CHECK_EQ(isolate, v8::Isolate::GetCurrent());
|
2011-05-05 18:55:31 +00:00
|
|
|
CompileRun("function getValue() { return 30; }");
|
2016-07-25 11:12:42 +00:00
|
|
|
thread1.reset(new KangarooThread(isolate, context));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
2013-04-24 14:23:46 +00:00
|
|
|
thread1->Start();
|
|
|
|
thread1->Join();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2015-11-18 08:22:07 +00:00
|
|
|
static void CalcFibAndCheck(v8::Local<v8::Context> context) {
|
2011-05-05 18:55:31 +00:00
|
|
|
Local<Value> v = CompileRun("function fib(n) {"
|
|
|
|
" if (n <= 2) return 1;"
|
|
|
|
" return fib(n-1) + fib(n-2);"
|
|
|
|
"}"
|
|
|
|
"fib(10)");
|
|
|
|
CHECK(v->IsNumber());
|
2015-11-18 08:22:07 +00:00
|
|
|
CHECK_EQ(55, static_cast<int>(v->NumberValue(context).FromJust()));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class JoinableThread {
|
|
|
|
public:
|
|
|
|
explicit JoinableThread(const char* name)
|
|
|
|
: name_(name),
|
2013-09-02 12:26:06 +00:00
|
|
|
semaphore_(0),
|
2011-05-05 18:55:31 +00:00
|
|
|
thread_(this) {
|
|
|
|
}
|
|
|
|
|
2018-09-13 10:07:40 +00:00
|
|
|
virtual ~JoinableThread() = default;
|
2011-05-05 18:55:31 +00:00
|
|
|
|
|
|
|
void Start() {
|
|
|
|
thread_.Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Join() {
|
2013-09-02 12:26:06 +00:00
|
|
|
semaphore_.Wait();
|
2014-10-08 08:17:04 +00:00
|
|
|
thread_.Join();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Run() = 0;
|
2011-09-08 19:57:14 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
private:
|
2014-06-30 13:25:46 +00:00
|
|
|
class ThreadWithSemaphore : public v8::base::Thread {
|
2011-05-05 18:55:31 +00:00
|
|
|
public:
|
2011-06-10 09:42:08 +00:00
|
|
|
explicit ThreadWithSemaphore(JoinableThread* joinable_thread)
|
2014-08-12 13:33:35 +00:00
|
|
|
: Thread(Options(joinable_thread->name_)),
|
|
|
|
joinable_thread_(joinable_thread) {}
|
2011-05-05 18:55:31 +00:00
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
joinable_thread_->Run();
|
2013-09-02 12:26:06 +00:00
|
|
|
joinable_thread_->semaphore_.Signal();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
JoinableThread* joinable_thread_;
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* name_;
|
2014-06-30 13:25:46 +00:00
|
|
|
v8::base::Semaphore semaphore_;
|
2011-05-05 18:55:31 +00:00
|
|
|
ThreadWithSemaphore thread_;
|
|
|
|
|
|
|
|
friend class ThreadWithSemaphore;
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(JoinableThread);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class IsolateLockingThreadWithLocalContext : public JoinableThread {
|
|
|
|
public:
|
|
|
|
explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate)
|
|
|
|
: JoinableThread("IsolateLockingThread"),
|
|
|
|
isolate_(isolate) {
|
|
|
|
}
|
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Locker locker(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate_);
|
2013-09-19 10:31:04 +00:00
|
|
|
LocalContext local_context(isolate_);
|
2015-11-23 12:38:18 +00:00
|
|
|
CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(local_context.local());
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
v8::Isolate* isolate_;
|
|
|
|
};
|
|
|
|
|
2017-08-29 12:40:31 +00:00
|
|
|
static void StartJoinAndDeleteThreads(
|
|
|
|
const std::vector<JoinableThread*>& threads) {
|
|
|
|
for (const auto& thread : threads) {
|
|
|
|
thread->Start();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
2017-08-29 12:40:31 +00:00
|
|
|
for (const auto& thread : threads) {
|
|
|
|
thread->Join();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
2017-08-29 12:40:31 +00:00
|
|
|
for (const auto& thread : threads) {
|
|
|
|
delete thread;
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Run many threads all locking on the same isolate
|
|
|
|
TEST(IsolateLockingStress) {
|
2015-04-17 09:13:37 +00:00
|
|
|
i::FLAG_always_opt = false;
|
2013-06-28 15:34:48 +00:00
|
|
|
#if V8_TARGET_ARCH_MIPS
|
2011-11-23 08:36:03 +00:00
|
|
|
const int kNThreads = 50;
|
|
|
|
#else
|
2011-05-05 18:55:31 +00:00
|
|
|
const int kNThreads = 100;
|
2011-11-23 08:36:03 +00:00
|
|
|
#endif
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2011-05-05 18:55:31 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(new IsolateLockingThreadWithLocalContext(isolate));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class IsolateNestedLockingThread : public JoinableThread {
|
|
|
|
public:
|
|
|
|
explicit IsolateNestedLockingThread(v8::Isolate* isolate)
|
|
|
|
: JoinableThread("IsolateNestedLocking"), isolate_(isolate) {
|
|
|
|
}
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Locker lock(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate_);
|
2013-09-19 10:31:04 +00:00
|
|
|
LocalContext local_context(isolate_);
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Locker another_lock(isolate_);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(local_context.local());
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Locker another_lock(isolate_);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(local_context.local());
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
v8::Isolate* isolate_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Run many threads with nested locks
|
|
|
|
TEST(IsolateNestedLocking) {
|
2015-04-17 09:13:37 +00:00
|
|
|
i::FLAG_always_opt = false;
|
2013-06-28 15:34:48 +00:00
|
|
|
#if V8_TARGET_ARCH_MIPS
|
2011-11-23 08:36:03 +00:00
|
|
|
const int kNThreads = 50;
|
|
|
|
#else
|
2011-05-05 18:55:31 +00:00
|
|
|
const int kNThreads = 100;
|
2011-11-23 08:36:03 +00:00
|
|
|
#endif
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2011-05-05 18:55:31 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(new IsolateNestedLockingThread(isolate));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
2013-02-12 11:57:51 +00:00
|
|
|
isolate->Dispose();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread {
|
|
|
|
public:
|
|
|
|
SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1,
|
|
|
|
v8::Isolate* isolate2)
|
|
|
|
: JoinableThread("SeparateIsolatesLocksNonexclusiveThread"),
|
|
|
|
isolate1_(isolate1), isolate2_(isolate2) {
|
|
|
|
}
|
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Locker lock(isolate1_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate1_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate1_);
|
2013-09-19 10:31:04 +00:00
|
|
|
LocalContext local_context(isolate1_);
|
2011-05-05 18:55:31 +00:00
|
|
|
|
|
|
|
IsolateLockingThreadWithLocalContext threadB(isolate2_);
|
|
|
|
threadB.Start();
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(local_context.local());
|
2011-05-05 18:55:31 +00:00
|
|
|
threadB.Join();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
v8::Isolate* isolate1_;
|
|
|
|
v8::Isolate* isolate2_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Run parallel threads that lock and access different isolates in parallel
|
|
|
|
TEST(SeparateIsolatesLocksNonexclusive) {
|
2015-04-17 09:13:37 +00:00
|
|
|
i::FLAG_always_opt = false;
|
2016-03-15 18:07:43 +00:00
|
|
|
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
|
2011-07-11 12:17:10 +00:00
|
|
|
const int kNThreads = 50;
|
|
|
|
#else
|
2011-05-05 18:55:31 +00:00
|
|
|
const int kNThreads = 100;
|
2011-07-11 12:17:10 +00:00
|
|
|
#endif
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate1 = v8::Isolate::New(create_params);
|
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2011-05-05 18:55:31 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(
|
|
|
|
new SeparateIsolatesLocksNonexclusiveThread(isolate1, isolate2));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
|
|
|
isolate2->Dispose();
|
|
|
|
isolate1->Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread {
|
|
|
|
public:
|
|
|
|
explicit LockIsolateAndCalculateFibSharedContextThread(
|
2015-11-18 08:22:07 +00:00
|
|
|
v8::Isolate* isolate, v8::Local<v8::Context> context)
|
|
|
|
: JoinableThread("LockIsolateAndCalculateFibThread"),
|
|
|
|
isolate_(isolate),
|
|
|
|
context_(isolate, context) {}
|
2011-05-05 18:55:31 +00:00
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Locker lock(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2017-09-21 03:29:52 +00:00
|
|
|
v8::HandleScope handle_scope(isolate_);
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Local<v8::Context>::New(isolate_, context_);
|
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
v8::Isolate* isolate_;
|
2017-09-21 03:29:52 +00:00
|
|
|
v8::Persistent<v8::Context> context_;
|
2011-05-05 18:55:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class LockerUnlockerThread : public JoinableThread {
|
|
|
|
public:
|
|
|
|
explicit LockerUnlockerThread(v8::Isolate* isolate)
|
|
|
|
: JoinableThread("LockerUnlockerThread"),
|
|
|
|
isolate_(isolate) {
|
|
|
|
}
|
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
Add Isolate::DiscardThreadSpecificMetadata method to embedder API.
If many threads use the same Isolate (or many Isolates) and then
terminate, their PerIsolateThreadData objects are never cleaned
up, resulting in a slow memory leak and, worse, the
PerIsolateThreadData chain getting larger and larger, adversely
affecting performance.
In this situation, embedders will now be encouraged to apply
DiscardThreadSpecificMetadata against any Isolate a thread is
done with, especially if the thread is about to terminate.
Note that it is harmless to run DiscardThreadSpecificMetadata
against an Isolate for which a thread has no thread data and
per-Isolate thread data can be reestablished if a thread starts
using an Isolate again after running DiscardThreadSpecificMetadata
against it.
It is, however, an embedder error to run
DiscardThreadSpecificMetadata against an Isolate in thread with a
Locker for the Isolate in the stack or against an Entered Isolate.
This change cannot cause any change in behavior in existing apps
as the only added coded can only be reached via the new
DiscardThreadSpecificMetadata method.
R=Jakob, jochen
BUG=
Review URL: https://codereview.chromium.org/1522703002
Cr-Commit-Position: refs/heads/master@{#32909}
2015-12-16 15:49:28 +00:00
|
|
|
isolate_->DiscardThreadSpecificMetadata(); // No-op
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
Add Isolate::DiscardThreadSpecificMetadata method to embedder API.
If many threads use the same Isolate (or many Isolates) and then
terminate, their PerIsolateThreadData objects are never cleaned
up, resulting in a slow memory leak and, worse, the
PerIsolateThreadData chain getting larger and larger, adversely
affecting performance.
In this situation, embedders will now be encouraged to apply
DiscardThreadSpecificMetadata against any Isolate a thread is
done with, especially if the thread is about to terminate.
Note that it is harmless to run DiscardThreadSpecificMetadata
against an Isolate for which a thread has no thread data and
per-Isolate thread data can be reestablished if a thread starts
using an Isolate again after running DiscardThreadSpecificMetadata
against it.
It is, however, an embedder error to run
DiscardThreadSpecificMetadata against an Isolate in thread with a
Locker for the Isolate in the stack or against an Entered Isolate.
This change cannot cause any change in behavior in existing apps
as the only added coded can only be reached via the new
DiscardThreadSpecificMetadata method.
R=Jakob, jochen
BUG=
Review URL: https://codereview.chromium.org/1522703002
Cr-Commit-Position: refs/heads/master@{#32909}
2015-12-16 15:49:28 +00:00
|
|
|
v8::Locker lock(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
|
|
|
v8::HandleScope handle_scope(isolate_);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate_);
|
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CalcFibAndCheck(context);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
|
|
|
|
isolate_->Exit();
|
|
|
|
v8::Unlocker unlocker(isolate_);
|
|
|
|
thread.Start();
|
|
|
|
thread.Join();
|
|
|
|
}
|
|
|
|
isolate_->Enter();
|
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CalcFibAndCheck(context);
|
|
|
|
}
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
Add Isolate::DiscardThreadSpecificMetadata method to embedder API.
If many threads use the same Isolate (or many Isolates) and then
terminate, their PerIsolateThreadData objects are never cleaned
up, resulting in a slow memory leak and, worse, the
PerIsolateThreadData chain getting larger and larger, adversely
affecting performance.
In this situation, embedders will now be encouraged to apply
DiscardThreadSpecificMetadata against any Isolate a thread is
done with, especially if the thread is about to terminate.
Note that it is harmless to run DiscardThreadSpecificMetadata
against an Isolate for which a thread has no thread data and
per-Isolate thread data can be reestablished if a thread starts
using an Isolate again after running DiscardThreadSpecificMetadata
against it.
It is, however, an embedder error to run
DiscardThreadSpecificMetadata against an Isolate in thread with a
Locker for the Isolate in the stack or against an Entered Isolate.
This change cannot cause any change in behavior in existing apps
as the only added coded can only be reached via the new
DiscardThreadSpecificMetadata method.
R=Jakob, jochen
BUG=
Review URL: https://codereview.chromium.org/1522703002
Cr-Commit-Position: refs/heads/master@{#32909}
2015-12-16 15:49:28 +00:00
|
|
|
isolate_->DiscardThreadSpecificMetadata();
|
|
|
|
isolate_->DiscardThreadSpecificMetadata(); // No-op
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
2011-09-08 19:57:14 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
private:
|
|
|
|
v8::Isolate* isolate_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Use unlocker inside of a Locker, multiple threads.
|
|
|
|
TEST(LockerUnlocker) {
|
2015-04-17 09:13:37 +00:00
|
|
|
i::FLAG_always_opt = false;
|
2016-03-15 18:07:43 +00:00
|
|
|
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
|
2011-07-11 12:17:10 +00:00
|
|
|
const int kNThreads = 50;
|
|
|
|
#else
|
2011-05-05 18:55:31 +00:00
|
|
|
const int kNThreads = 100;
|
2011-07-11 12:17:10 +00:00
|
|
|
#endif
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2011-05-05 18:55:31 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(new LockerUnlockerThread(isolate));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
class LockTwiceAndUnlockThread : public JoinableThread {
|
|
|
|
public:
|
|
|
|
explicit LockTwiceAndUnlockThread(v8::Isolate* isolate)
|
|
|
|
: JoinableThread("LockTwiceAndUnlockThread"),
|
|
|
|
isolate_(isolate) {
|
|
|
|
}
|
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Locker lock(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate_);
|
2013-04-24 14:23:46 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate_);
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Locker second_lock(isolate_);
|
|
|
|
{
|
2013-04-24 14:23:46 +00:00
|
|
|
LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
|
2011-05-05 18:55:31 +00:00
|
|
|
isolate_->Exit();
|
|
|
|
v8::Unlocker unlocker(isolate_);
|
|
|
|
thread.Start();
|
|
|
|
thread.Join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isolate_->Enter();
|
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-08 19:57:14 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
private:
|
|
|
|
v8::Isolate* isolate_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Use Unlocker inside two Lockers.
|
|
|
|
TEST(LockTwiceAndUnlock) {
|
2015-04-17 09:13:37 +00:00
|
|
|
i::FLAG_always_opt = false;
|
2016-03-15 18:07:43 +00:00
|
|
|
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
|
2011-07-11 12:17:10 +00:00
|
|
|
const int kNThreads = 50;
|
|
|
|
#else
|
2011-05-05 18:55:31 +00:00
|
|
|
const int kNThreads = 100;
|
2011-07-11 12:17:10 +00:00
|
|
|
#endif
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2011-05-05 18:55:31 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(new LockTwiceAndUnlockThread(isolate));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
class LockAndUnlockDifferentIsolatesThread : public JoinableThread {
|
|
|
|
public:
|
|
|
|
LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1,
|
|
|
|
v8::Isolate* isolate2)
|
|
|
|
: JoinableThread("LockAndUnlockDifferentIsolatesThread"),
|
|
|
|
isolate1_(isolate1),
|
|
|
|
isolate2_(isolate2) {
|
|
|
|
}
|
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2016-07-25 11:12:42 +00:00
|
|
|
std::unique_ptr<LockIsolateAndCalculateFibSharedContextThread> thread;
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Locker lock1(isolate1_);
|
|
|
|
CHECK(v8::Locker::IsLocked(isolate1_));
|
|
|
|
CHECK(!v8::Locker::IsLocked(isolate2_));
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate1_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate1_);
|
2015-11-18 08:22:07 +00:00
|
|
|
v8::Local<v8::Context> context1 = v8::Context::New(isolate1_);
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context1);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context1);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
2016-07-25 11:12:42 +00:00
|
|
|
thread.reset(new LockIsolateAndCalculateFibSharedContextThread(isolate1_,
|
|
|
|
context1));
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
v8::Locker lock2(isolate2_);
|
|
|
|
CHECK(v8::Locker::IsLocked(isolate1_));
|
|
|
|
CHECK(v8::Locker::IsLocked(isolate2_));
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate2_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate2_);
|
2015-11-18 08:22:07 +00:00
|
|
|
v8::Local<v8::Context> context2 = v8::Context::New(isolate2_);
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context2);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context2);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
v8::Unlocker unlock1(isolate1_);
|
|
|
|
CHECK(!v8::Locker::IsLocked(isolate1_));
|
|
|
|
CHECK(v8::Locker::IsLocked(isolate2_));
|
|
|
|
v8::Context::Scope context_scope(context2);
|
2013-04-24 14:23:46 +00:00
|
|
|
thread->Start();
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context2);
|
2013-04-24 14:23:46 +00:00
|
|
|
thread->Join();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-08 19:57:14 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
private:
|
|
|
|
v8::Isolate* isolate1_;
|
|
|
|
v8::Isolate* isolate2_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Lock two isolates and unlock one of them.
|
|
|
|
TEST(LockAndUnlockDifferentIsolates) {
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate1 = v8::Isolate::New(create_params);
|
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
2011-05-05 18:55:31 +00:00
|
|
|
LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2);
|
|
|
|
thread.Start();
|
|
|
|
thread.Join();
|
|
|
|
isolate2->Dispose();
|
|
|
|
isolate1->Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
class LockUnlockLockThread : public JoinableThread {
|
|
|
|
public:
|
2015-11-18 08:22:07 +00:00
|
|
|
LockUnlockLockThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
|
|
|
|
: JoinableThread("LockUnlockLockThread"),
|
|
|
|
isolate_(isolate),
|
|
|
|
context_(isolate, context) {}
|
2011-05-05 18:55:31 +00:00
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2011-05-05 18:55:31 +00:00
|
|
|
v8::Locker lock1(isolate_);
|
|
|
|
CHECK(v8::Locker::IsLocked(isolate_));
|
2013-09-19 09:56:09 +00:00
|
|
|
CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate_);
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Local<v8::Context>::New(isolate_, context_);
|
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Unlocker unlock1(isolate_);
|
|
|
|
CHECK(!v8::Locker::IsLocked(isolate_));
|
2013-09-19 09:56:09 +00:00
|
|
|
CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Locker lock2(isolate_);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate_);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate_);
|
2011-05-05 18:55:31 +00:00
|
|
|
CHECK(v8::Locker::IsLocked(isolate_));
|
2013-09-19 09:56:09 +00:00
|
|
|
CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Local<v8::Context>::New(isolate_, context_);
|
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
v8::Isolate* isolate_;
|
|
|
|
v8::Persistent<v8::Context> context_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Locker inside an Unlocker inside a Locker.
|
|
|
|
TEST(LockUnlockLockMultithreaded) {
|
2013-06-28 15:34:48 +00:00
|
|
|
#if V8_TARGET_ARCH_MIPS
|
2011-11-23 08:36:03 +00:00
|
|
|
const int kNThreads = 50;
|
|
|
|
#else
|
2011-05-05 18:55:31 +00:00
|
|
|
const int kNThreads = 100;
|
2011-11-23 08:36:03 +00:00
|
|
|
#endif
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
|
|
|
v8::Locker locker_(isolate);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
2015-11-18 08:22:07 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
2013-04-24 14:23:46 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(new LockUnlockLockThread(isolate, context));
|
2013-04-24 14:23:46 +00:00
|
|
|
}
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
2013-02-12 11:57:51 +00:00
|
|
|
isolate->Dispose();
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class LockUnlockLockDefaultIsolateThread : public JoinableThread {
|
|
|
|
public:
|
2015-11-18 08:22:07 +00:00
|
|
|
explicit LockUnlockLockDefaultIsolateThread(v8::Local<v8::Context> context)
|
2013-04-24 14:23:46 +00:00
|
|
|
: JoinableThread("LockUnlockLockDefaultIsolateThread"),
|
2013-09-19 09:56:09 +00:00
|
|
|
context_(CcTest::isolate(), context) {}
|
2011-05-05 18:55:31 +00:00
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::Locker lock1(CcTest::isolate());
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
2013-09-20 10:52:20 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(CcTest::isolate());
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::HandleScope handle_scope(CcTest::isolate());
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Local<v8::Context> context =
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::Local<v8::Context>::New(CcTest::isolate(), context_);
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
{
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::Unlocker unlock1(CcTest::isolate());
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::Locker lock2(CcTest::isolate());
|
2013-09-20 10:52:20 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(CcTest::isolate());
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::HandleScope handle_scope(CcTest::isolate());
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Local<v8::Context> context =
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::Local<v8::Context>::New(CcTest::isolate(), context_);
|
2013-05-28 10:36:21 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
2015-11-18 08:22:07 +00:00
|
|
|
CalcFibAndCheck(context);
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
v8::Persistent<v8::Context> context_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-05-05 18:55:31 +00:00
|
|
|
// Locker inside an Unlocker inside a Locker for default isolate.
|
2013-09-20 10:52:20 +00:00
|
|
|
TEST(LockUnlockLockDefaultIsolateMultithreaded) {
|
2013-06-28 15:34:48 +00:00
|
|
|
#if V8_TARGET_ARCH_MIPS
|
2011-11-23 08:36:03 +00:00
|
|
|
const int kNThreads = 50;
|
|
|
|
#else
|
2011-05-05 18:55:31 +00:00
|
|
|
const int kNThreads = 100;
|
2011-11-23 08:36:03 +00:00
|
|
|
#endif
|
2013-05-08 07:45:16 +00:00
|
|
|
Local<v8::Context> context;
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2011-05-05 18:55:31 +00:00
|
|
|
{
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::Locker locker_(CcTest::isolate());
|
2013-09-20 10:52:20 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(CcTest::isolate());
|
2013-09-19 09:56:09 +00:00
|
|
|
v8::HandleScope handle_scope(CcTest::isolate());
|
|
|
|
context = v8::Context::New(CcTest::isolate());
|
2013-04-24 14:23:46 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(new LockUnlockLockDefaultIsolateThread(context));
|
2013-04-24 14:23:46 +00:00
|
|
|
}
|
2011-05-05 18:55:31 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
|
|
|
}
|
2011-06-07 18:33:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(Regress1433) {
|
|
|
|
for (int i = 0; i < 10; i++) {
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2011-06-07 18:33:03 +00:00
|
|
|
{
|
|
|
|
v8::Locker lock(isolate);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
2017-09-21 03:29:52 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
2011-06-07 18:33:03 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
2017-09-21 03:29:52 +00:00
|
|
|
v8::Local<v8::String> source = v8_str("1+1");
|
|
|
|
v8::Local<v8::Script> script =
|
2015-11-18 08:22:07 +00:00
|
|
|
v8::Script::Compile(context, source).ToLocalChecked();
|
2017-09-21 03:29:52 +00:00
|
|
|
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value utf8(isolate, result);
|
2011-06-07 18:33:03 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
}
|
2011-11-15 22:48:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
static const char* kSimpleExtensionSource =
|
|
|
|
"(function Foo() {"
|
|
|
|
" return 4;"
|
|
|
|
"})() ";
|
|
|
|
|
|
|
|
class IsolateGenesisThread : public JoinableThread {
|
|
|
|
public:
|
|
|
|
IsolateGenesisThread(int count, const char* extension_names[])
|
|
|
|
: JoinableThread("IsolateGenesisThread"),
|
|
|
|
count_(count),
|
|
|
|
extension_names_(extension_names)
|
|
|
|
{}
|
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
void Run() override {
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
2011-11-15 22:48:55 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::ExtensionConfiguration extensions(count_, extension_names_);
|
2013-05-08 07:45:16 +00:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Context::New(isolate, &extensions);
|
2011-11-15 22:48:55 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
2015-04-29 09:54:34 +00:00
|
|
|
|
2011-11-15 22:48:55 +00:00
|
|
|
private:
|
|
|
|
int count_;
|
|
|
|
const char** extension_names_;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-11-15 22:48:55 +00:00
|
|
|
// Test installing extensions in separate isolates concurrently.
|
|
|
|
// http://code.google.com/p/v8/issues/detail?id=1821
|
|
|
|
TEST(ExtensionsRegistration) {
|
2013-06-28 15:34:48 +00:00
|
|
|
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
|
2011-11-22 18:51:53 +00:00
|
|
|
const int kNThreads = 10;
|
2016-03-15 18:07:43 +00:00
|
|
|
#elif V8_TARGET_ARCH_S390 && V8_TARGET_ARCH_32_BIT
|
|
|
|
const int kNThreads = 10;
|
2011-11-22 18:51:53 +00:00
|
|
|
#else
|
2011-11-15 22:48:55 +00:00
|
|
|
const int kNThreads = 40;
|
2011-11-22 18:51:53 +00:00
|
|
|
#endif
|
2019-01-31 11:36:20 +00:00
|
|
|
const char* extension_names[] = {"test0", "test1", "test2", "test3",
|
|
|
|
"test4", "test5", "test6", "test7"};
|
|
|
|
for (const char* name : extension_names) {
|
|
|
|
v8::RegisterExtension(
|
|
|
|
v8::base::make_unique<v8::Extension>(name, kSimpleExtensionSource));
|
|
|
|
}
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<JoinableThread*> threads;
|
|
|
|
threads.reserve(kNThreads);
|
2011-11-15 22:48:55 +00:00
|
|
|
for (int i = 0; i < kNThreads; i++) {
|
2017-08-29 12:40:31 +00:00
|
|
|
threads.push_back(new IsolateGenesisThread(8, extension_names));
|
2011-11-15 22:48:55 +00:00
|
|
|
}
|
|
|
|
StartJoinAndDeleteThreads(threads);
|
|
|
|
}
|
2017-09-21 03:29:52 +00:00
|
|
|
|
|
|
|
} // namespace test_lockers
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|