// 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> #include <memory> #include "src/v8.h" #include "src/api.h" #include "src/base/platform/platform.h" #include "src/compilation-cache.h" #include "src/execution.h" #include "src/isolate.h" #include "src/objects-inl.h" #include "src/unicode-inl.h" #include "src/utils.h" #include "test/cctest/cctest.h" 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_; v8::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 namespace v8 { namespace internal { namespace test_lockers { 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: KangarooThread(v8::Isolate* isolate, v8::Local<v8::Context> context) : Thread(Options("KangarooThread")), isolate_(isolate), context_(isolate, context) {} void Run() { { v8::Locker locker(isolate_); v8::Isolate::Scope isolate_scope(isolate_); CHECK_EQ(isolate_, v8::Isolate::GetCurrent()); v8::HandleScope scope(isolate_); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate_, context_); v8::Context::Scope context_scope(context); Local<Value> v = CompileRun("getValue()"); CHECK(v->IsNumber()); CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust())); } { v8::Locker locker(isolate_); v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope scope(isolate_); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate_, context_); v8::Context::Scope context_scope(context); Local<Value> v = CompileRun("getValue()"); CHECK(v->IsNumber()); CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust())); } isolate_->Dispose(); } private: v8::Isolate* isolate_; v8::Persistent<v8::Context> context_; }; // Migrates an isolate from one thread to another TEST(KangarooIsolates) { v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); std::unique_ptr<KangarooThread> thread1; { v8::Locker locker(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); CHECK_EQ(isolate, v8::Isolate::GetCurrent()); CompileRun("function getValue() { return 30; }"); thread1.reset(new KangarooThread(isolate, context)); } thread1->Start(); thread1->Join(); } static void CalcFibAndCheck(v8::Local<v8::Context> context) { Local<Value> v = CompileRun("function fib(n) {" " if (n <= 2) return 1;" " return fib(n-1) + fib(n-2);" "}" "fib(10)"); CHECK(v->IsNumber()); CHECK_EQ(55, static_cast<int>(v->NumberValue(context).FromJust())); } class JoinableThread { public: explicit JoinableThread(const char* name) : name_(name), semaphore_(0), thread_(this) { } virtual ~JoinableThread() {} void Start() { thread_.Start(); } void Join() { semaphore_.Wait(); thread_.Join(); } virtual void Run() = 0; private: class ThreadWithSemaphore : public v8::base::Thread { public: explicit ThreadWithSemaphore(JoinableThread* joinable_thread) : Thread(Options(joinable_thread->name_)), joinable_thread_(joinable_thread) {} virtual void Run() { joinable_thread_->Run(); joinable_thread_->semaphore_.Signal(); } private: JoinableThread* joinable_thread_; }; const char* name_; v8::base::Semaphore semaphore_; ThreadWithSemaphore thread_; friend class ThreadWithSemaphore; DISALLOW_COPY_AND_ASSIGN(JoinableThread); }; class IsolateLockingThreadWithLocalContext : public JoinableThread { public: explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate) : JoinableThread("IsolateLockingThread"), isolate_(isolate) { } virtual void Run() { v8::Locker locker(isolate_); v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); LocalContext local_context(isolate_); CHECK_EQ(isolate_, v8::Isolate::GetCurrent()); CalcFibAndCheck(local_context.local()); } private: v8::Isolate* isolate_; }; static void StartJoinAndDeleteThreads( const std::vector<JoinableThread*>& threads) { for (const auto& thread : threads) { thread->Start(); } for (const auto& thread : threads) { thread->Join(); } for (const auto& thread : threads) { delete thread; } } // Run many threads all locking on the same isolate TEST(IsolateLockingStress) { i::FLAG_always_opt = false; #if V8_TARGET_ARCH_MIPS const int kNThreads = 50; #else const int kNThreads = 100; #endif std::vector<JoinableThread*> threads; threads.reserve(kNThreads); v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); for (int i = 0; i < kNThreads; i++) { threads.push_back(new IsolateLockingThreadWithLocalContext(isolate)); } StartJoinAndDeleteThreads(threads); isolate->Dispose(); } class IsolateNestedLockingThread : public JoinableThread { public: explicit IsolateNestedLockingThread(v8::Isolate* isolate) : JoinableThread("IsolateNestedLocking"), isolate_(isolate) { } virtual void Run() { v8::Locker lock(isolate_); v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); LocalContext local_context(isolate_); { v8::Locker another_lock(isolate_); CalcFibAndCheck(local_context.local()); } { v8::Locker another_lock(isolate_); CalcFibAndCheck(local_context.local()); } } private: v8::Isolate* isolate_; }; // Run many threads with nested locks TEST(IsolateNestedLocking) { i::FLAG_always_opt = false; #if V8_TARGET_ARCH_MIPS const int kNThreads = 50; #else const int kNThreads = 100; #endif v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); std::vector<JoinableThread*> threads; threads.reserve(kNThreads); for (int i = 0; i < kNThreads; i++) { threads.push_back(new IsolateNestedLockingThread(isolate)); } StartJoinAndDeleteThreads(threads); isolate->Dispose(); } class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread { public: SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1, v8::Isolate* isolate2) : JoinableThread("SeparateIsolatesLocksNonexclusiveThread"), isolate1_(isolate1), isolate2_(isolate2) { } virtual void Run() { v8::Locker lock(isolate1_); v8::Isolate::Scope isolate_scope(isolate1_); v8::HandleScope handle_scope(isolate1_); LocalContext local_context(isolate1_); IsolateLockingThreadWithLocalContext threadB(isolate2_); threadB.Start(); CalcFibAndCheck(local_context.local()); threadB.Join(); } private: v8::Isolate* isolate1_; v8::Isolate* isolate2_; }; // Run parallel threads that lock and access different isolates in parallel TEST(SeparateIsolatesLocksNonexclusive) { i::FLAG_always_opt = false; #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390 const int kNThreads = 50; #else const int kNThreads = 100; #endif 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); std::vector<JoinableThread*> threads; threads.reserve(kNThreads); for (int i = 0; i < kNThreads; i++) { threads.push_back( new SeparateIsolatesLocksNonexclusiveThread(isolate1, isolate2)); } StartJoinAndDeleteThreads(threads); isolate2->Dispose(); isolate1->Dispose(); } class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread { public: explicit LockIsolateAndCalculateFibSharedContextThread( v8::Isolate* isolate, v8::Local<v8::Context> context) : JoinableThread("LockIsolateAndCalculateFibThread"), isolate_(isolate), context_(isolate, context) {} virtual void Run() { v8::Locker lock(isolate_); v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate_, context_); v8::Context::Scope context_scope(context); CalcFibAndCheck(context); } private: v8::Isolate* isolate_; v8::Persistent<v8::Context> context_; }; class LockerUnlockerThread : public JoinableThread { public: explicit LockerUnlockerThread(v8::Isolate* isolate) : JoinableThread("LockerUnlockerThread"), isolate_(isolate) { } virtual void Run() { isolate_->DiscardThreadSpecificMetadata(); // No-op { 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); } } isolate_->DiscardThreadSpecificMetadata(); isolate_->DiscardThreadSpecificMetadata(); // No-op } private: v8::Isolate* isolate_; }; // Use unlocker inside of a Locker, multiple threads. TEST(LockerUnlocker) { i::FLAG_always_opt = false; #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390 const int kNThreads = 50; #else const int kNThreads = 100; #endif std::vector<JoinableThread*> threads; threads.reserve(kNThreads); v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); for (int i = 0; i < kNThreads; i++) { threads.push_back(new LockerUnlockerThread(isolate)); } StartJoinAndDeleteThreads(threads); isolate->Dispose(); } class LockTwiceAndUnlockThread : public JoinableThread { public: explicit LockTwiceAndUnlockThread(v8::Isolate* isolate) : JoinableThread("LockTwiceAndUnlockThread"), isolate_(isolate) { } virtual void Run() { 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); } { v8::Locker second_lock(isolate_); { LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context); isolate_->Exit(); v8::Unlocker unlocker(isolate_); thread.Start(); thread.Join(); } } isolate_->Enter(); { v8::Context::Scope context_scope(context); CalcFibAndCheck(context); } } private: v8::Isolate* isolate_; }; // Use Unlocker inside two Lockers. TEST(LockTwiceAndUnlock) { i::FLAG_always_opt = false; #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390 const int kNThreads = 50; #else const int kNThreads = 100; #endif std::vector<JoinableThread*> threads; threads.reserve(kNThreads); v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); for (int i = 0; i < kNThreads; i++) { threads.push_back(new LockTwiceAndUnlockThread(isolate)); } StartJoinAndDeleteThreads(threads); isolate->Dispose(); } class LockAndUnlockDifferentIsolatesThread : public JoinableThread { public: LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1, v8::Isolate* isolate2) : JoinableThread("LockAndUnlockDifferentIsolatesThread"), isolate1_(isolate1), isolate2_(isolate2) { } virtual void Run() { std::unique_ptr<LockIsolateAndCalculateFibSharedContextThread> thread; v8::Locker lock1(isolate1_); CHECK(v8::Locker::IsLocked(isolate1_)); CHECK(!v8::Locker::IsLocked(isolate2_)); { v8::Isolate::Scope isolate_scope(isolate1_); v8::HandleScope handle_scope(isolate1_); v8::Local<v8::Context> context1 = v8::Context::New(isolate1_); { v8::Context::Scope context_scope(context1); CalcFibAndCheck(context1); } thread.reset(new LockIsolateAndCalculateFibSharedContextThread(isolate1_, context1)); } v8::Locker lock2(isolate2_); CHECK(v8::Locker::IsLocked(isolate1_)); CHECK(v8::Locker::IsLocked(isolate2_)); { v8::Isolate::Scope isolate_scope(isolate2_); v8::HandleScope handle_scope(isolate2_); v8::Local<v8::Context> context2 = v8::Context::New(isolate2_); { v8::Context::Scope context_scope(context2); CalcFibAndCheck(context2); } v8::Unlocker unlock1(isolate1_); CHECK(!v8::Locker::IsLocked(isolate1_)); CHECK(v8::Locker::IsLocked(isolate2_)); v8::Context::Scope context_scope(context2); thread->Start(); CalcFibAndCheck(context2); thread->Join(); } } private: v8::Isolate* isolate1_; v8::Isolate* isolate2_; }; // Lock two isolates and unlock one of them. TEST(LockAndUnlockDifferentIsolates) { 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); LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2); thread.Start(); thread.Join(); isolate2->Dispose(); isolate1->Dispose(); } class LockUnlockLockThread : public JoinableThread { public: LockUnlockLockThread(v8::Isolate* isolate, v8::Local<v8::Context> context) : JoinableThread("LockUnlockLockThread"), isolate_(isolate), context_(isolate, context) {} virtual void Run() { v8::Locker lock1(isolate_); CHECK(v8::Locker::IsLocked(isolate_)); CHECK(!v8::Locker::IsLocked(CcTest::isolate())); { v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate_, context_); v8::Context::Scope context_scope(context); CalcFibAndCheck(context); } { v8::Unlocker unlock1(isolate_); CHECK(!v8::Locker::IsLocked(isolate_)); CHECK(!v8::Locker::IsLocked(CcTest::isolate())); { v8::Locker lock2(isolate_); v8::Isolate::Scope isolate_scope(isolate_); v8::HandleScope handle_scope(isolate_); CHECK(v8::Locker::IsLocked(isolate_)); CHECK(!v8::Locker::IsLocked(CcTest::isolate())); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate_, context_); v8::Context::Scope context_scope(context); CalcFibAndCheck(context); } } } private: v8::Isolate* isolate_; v8::Persistent<v8::Context> context_; }; // Locker inside an Unlocker inside a Locker. TEST(LockUnlockLockMultithreaded) { #if V8_TARGET_ARCH_MIPS const int kNThreads = 50; #else const int kNThreads = 100; #endif v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); std::vector<JoinableThread*> threads; threads.reserve(kNThreads); { v8::Locker locker_(isolate); v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); v8::Local<v8::Context> context = v8::Context::New(isolate); for (int i = 0; i < kNThreads; i++) { threads.push_back(new LockUnlockLockThread(isolate, context)); } } StartJoinAndDeleteThreads(threads); isolate->Dispose(); } class LockUnlockLockDefaultIsolateThread : public JoinableThread { public: explicit LockUnlockLockDefaultIsolateThread(v8::Local<v8::Context> context) : JoinableThread("LockUnlockLockDefaultIsolateThread"), context_(CcTest::isolate(), context) {} virtual void Run() { v8::Locker lock1(CcTest::isolate()); { v8::Isolate::Scope isolate_scope(CcTest::isolate()); v8::HandleScope handle_scope(CcTest::isolate()); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(CcTest::isolate(), context_); v8::Context::Scope context_scope(context); CalcFibAndCheck(context); } { v8::Unlocker unlock1(CcTest::isolate()); { v8::Locker lock2(CcTest::isolate()); v8::Isolate::Scope isolate_scope(CcTest::isolate()); v8::HandleScope handle_scope(CcTest::isolate()); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(CcTest::isolate(), context_); v8::Context::Scope context_scope(context); CalcFibAndCheck(context); } } } private: v8::Persistent<v8::Context> context_; }; // Locker inside an Unlocker inside a Locker for default isolate. TEST(LockUnlockLockDefaultIsolateMultithreaded) { #if V8_TARGET_ARCH_MIPS const int kNThreads = 50; #else const int kNThreads = 100; #endif Local<v8::Context> context; std::vector<JoinableThread*> threads; threads.reserve(kNThreads); { v8::Locker locker_(CcTest::isolate()); v8::Isolate::Scope isolate_scope(CcTest::isolate()); v8::HandleScope handle_scope(CcTest::isolate()); context = v8::Context::New(CcTest::isolate()); for (int i = 0; i < kNThreads; i++) { threads.push_back(new LockUnlockLockDefaultIsolateThread(context)); } } StartJoinAndDeleteThreads(threads); } TEST(Regress1433) { for (int i = 0; i < 10; i++) { v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); { 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); v8::Local<v8::String> source = v8_str("1+1"); v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked(); v8::Local<v8::Value> result = script->Run(context).ToLocalChecked(); v8::String::Utf8Value utf8(isolate, result); } isolate->Dispose(); } } 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) {} virtual void Run() { v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); { v8::Isolate::Scope isolate_scope(isolate); v8::ExtensionConfiguration extensions(count_, extension_names_); v8::HandleScope handle_scope(isolate); v8::Context::New(isolate, &extensions); } isolate->Dispose(); } private: int count_; const char** extension_names_; }; // Test installing extensions in separate isolates concurrently. // http://code.google.com/p/v8/issues/detail?id=1821 TEST(ExtensionsRegistration) { #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS const int kNThreads = 10; #elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT const int kNThreads = 4; #elif V8_TARGET_ARCH_S390 && V8_TARGET_ARCH_32_BIT const int kNThreads = 10; #else const int kNThreads = 40; #endif v8::RegisterExtension(new v8::Extension("test0", kSimpleExtensionSource)); v8::RegisterExtension(new v8::Extension("test1", kSimpleExtensionSource)); v8::RegisterExtension(new v8::Extension("test2", kSimpleExtensionSource)); v8::RegisterExtension(new v8::Extension("test3", kSimpleExtensionSource)); v8::RegisterExtension(new v8::Extension("test4", kSimpleExtensionSource)); v8::RegisterExtension(new v8::Extension("test5", kSimpleExtensionSource)); v8::RegisterExtension(new v8::Extension("test6", kSimpleExtensionSource)); v8::RegisterExtension(new v8::Extension("test7", kSimpleExtensionSource)); const char* extension_names[] = { "test0", "test1", "test2", "test3", "test4", "test5", "test6", "test7" }; std::vector<JoinableThread*> threads; threads.reserve(kNThreads); for (int i = 0; i < kNThreads; i++) { threads.push_back(new IsolateGenesisThread(8, extension_names)); } StartJoinAndDeleteThreads(threads); } } // namespace test_lockers } // namespace internal } // namespace v8