Chromium Code Reviews| Index: test/cctest/test-lockers.cc |
| diff --git a/test/cctest/test-lockers.cc b/test/cctest/test-lockers.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2b495aa96b24b49f734ab65143c3167dceba2633 |
| --- /dev/null |
| +++ b/test/cctest/test-lockers.cc |
| @@ -0,0 +1,606 @@ |
| +// 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 <vector> |
|
Vitaly Repeshko
2011/04/21 21:15:27
Remove unnecessary includes.
|
| + |
| +#include "v8.h" |
| + |
| +#include "api.h" |
| +#include "isolate.h" |
| +#include "compilation-cache.h" |
| +#include "execution.h" |
| +#include "snapshot.h" |
| +#include "platform.h" |
| +#include "utils.h" |
| +#include "cctest.h" |
| +#include "parser.h" |
| +#include "unicode-inl.h" |
| + |
| +using ::v8::AccessorInfo; |
| +using ::v8::Context; |
| +using ::v8::Extension; |
| +using ::v8::Function; |
| +using ::v8::HandleScope; |
| +using ::v8::Local; |
| +using ::v8::Object; |
| +using ::v8::ObjectTemplate; |
| +using ::v8::Persistent; |
| +using ::v8::Script; |
| +using ::v8::String; |
| +using ::v8::Value; |
| +using ::v8::V8; |
| + |
| +namespace i = ::i; |
| + |
| + |
| + |
| + |
| +// Migrating an isolate |
| +class KangarooThread : public v8::internal::Thread { |
| + public: |
| + KangarooThread(v8::Isolate* isolate, |
| + v8::Handle<v8::Context> context, int value) |
| + : Thread(NULL, "KangarooThread"), |
| + isolate_(isolate), context_(context), value_(value) { |
| + } |
| + |
| + void Run() { |
| + { |
| + v8::Locker locker(isolate_); |
| + v8::Isolate::Scope isolate_scope(isolate_); |
| + CHECK_EQ(isolate_, v8::internal::Isolate::Current()); |
| + v8::HandleScope scope; |
| + v8::Context::Scope context_scope(context_); |
| + Local<Value> v = CompileRun("getValue()"); |
| + CHECK(v->IsNumber()); |
| + CHECK_EQ(30, static_cast<int>(v->NumberValue())); |
| + } |
| + { |
| + v8::Locker locker(isolate_); |
| + v8::Isolate::Scope isolate_scope(isolate_); |
| + v8::Context::Scope context_scope(context_); |
| + v8::HandleScope scope; |
| + Local<Value> v = CompileRun("getValue()"); |
| + CHECK(v->IsNumber()); |
| + CHECK_EQ(30, static_cast<int>(v->NumberValue())); |
| + } |
| + isolate_->Dispose(); |
| + } |
| + |
| + private: |
| + v8::Isolate* isolate_; |
| + Persistent<v8::Context> context_; |
| + int value_; |
| +}; |
| + |
| +// Migrates an isolate from one thread to another |
| +TEST(KangarooIsolates) { |
| + v8::Isolate* isolate = v8::Isolate::New(); |
| + Persistent<v8::Context> context; |
| + { |
| + v8::Locker locker(isolate); |
| + v8::Isolate::Scope isolate_scope(isolate); |
| + v8::HandleScope handle_scope; |
| + context = v8::Context::New(); |
| + v8::Context::Scope context_scope(context); |
| + CHECK_EQ(isolate, v8::internal::Isolate::Current()); |
| + CompileRun("function getValue() { return 30; }"); |
| + } |
| + KangarooThread thread1(isolate, context, 1); |
| + thread1.Start(); |
| + thread1.Join(); |
| +} |
| + |
| +static void CalcFibAndCheck() { |
| + 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())); |
| +} |
| + |
| +class JoinableThread { |
| + public: |
| + explicit JoinableThread(const char* name) |
| + : name_(name), |
| + semaphore_(i::OS::CreateSemaphore(0)), |
| + thread_(this) { |
| + } |
| + |
| + virtual ~JoinableThread() { |
| + delete semaphore_; |
| + } |
| + |
| + void Start() { |
| + thread_.Start(); |
| + } |
| + |
| + void Join() { |
| + semaphore_->Wait(); |
| + } |
| + |
| + virtual void Run() = 0; |
| + private: |
| + class ThreadWithSemaphore : public i::Thread { |
| + public: |
| + explicit ThreadWithSemaphore(JoinableThread* joinable_thread) |
| + : Thread(NULL, joinable_thread->name_), |
| + joinable_thread_(joinable_thread) { |
| + } |
| + |
| + virtual void Run() { |
| + joinable_thread_->Run(); |
| + joinable_thread_->semaphore_->Signal(); |
| + } |
| + |
| + private: |
| + JoinableThread* joinable_thread_; |
| + }; |
| + |
| + const char* name_; |
| + i::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; |
| + LocalContext local_context; |
| + CHECK_EQ(isolate_, v8::internal::Isolate::Current()); |
| + CalcFibAndCheck(); |
| + } |
| + private: |
| + v8::Isolate* isolate_; |
| +}; |
| + |
| +static void StartJoinAndDeleteThreads(const i::List<JoinableThread*>& threads) { |
| + for (int i = 0; i < threads.length(); i++) { |
| + threads[i]->Start(); |
| + } |
| + for (int i = 0; i < threads.length(); i++) { |
| + threads[i]->Join(); |
| + } |
| + for (int i = 0; i < threads.length(); i++) { |
| + delete threads[i]; |
| + } |
| +} |
| + |
| + |
| +// Run many threads all locking on the same isolate |
| +TEST(IsolateLockingStress) { |
| + const int kNThreads = 100; |
| + i::List<JoinableThread*> threads(kNThreads); |
| + v8::Isolate* isolate = v8::Isolate::New(); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(new IsolateLockingThreadWithLocalContext(isolate)); |
| + } |
| + StartJoinAndDeleteThreads(threads); |
| + isolate->Dispose(); |
| +} |
| + |
| +class IsolateNonlockingThread : public JoinableThread { |
| + public: |
| + explicit IsolateNonlockingThread() |
| + : JoinableThread("IsolateNonlockingThread") { |
| + } |
| + |
| + virtual void Run() { |
| + v8::Isolate* isolate = v8::Isolate::New(); |
| + { |
| + v8::Isolate::Scope isolate_scope(isolate); |
| + v8::HandleScope handle_scope; |
| + v8::Handle<v8::Context> context = v8::Context::New(); |
| + v8::Context::Scope context_scope(context); |
| + CHECK_EQ(isolate, v8::internal::Isolate::Current()); |
| + CalcFibAndCheck(); |
| + } |
| + isolate->Dispose(); |
| + } |
| + private: |
| +}; |
| + |
| +// Run many threads each accessing its own isolate without locking |
| +TEST(MultithreadedParallelIsolates) { |
| + const int kNThreads = 50; |
| + i::List<JoinableThread*> threads(kNThreads); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(new IsolateNonlockingThread()); |
| + } |
| + StartJoinAndDeleteThreads(threads); |
| +} |
| + |
| + |
| +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; |
| + LocalContext local_context; |
| + { |
| + v8::Locker another_lock(isolate_); |
| + CalcFibAndCheck(); |
| + } |
| + { |
| + v8::Locker another_lock(isolate_); |
| + CalcFibAndCheck(); |
| + } |
| + } |
| + private: |
| + v8::Isolate* isolate_; |
| +}; |
| + |
| +// Run many threads with nested locks |
| +TEST(IsolateNestedLocking) { |
| + const int kNThreads = 100; |
| + v8::Isolate* isolate = v8::Isolate::New(); |
| + i::List<JoinableThread*> threads(kNThreads); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(new IsolateNestedLockingThread(isolate)); |
| + } |
| + StartJoinAndDeleteThreads(threads); |
| +} |
| + |
| + |
| +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; |
| + LocalContext local_context; |
| + |
| + IsolateLockingThreadWithLocalContext threadB(isolate2_); |
| + threadB.Start(); |
| + CalcFibAndCheck(); |
| + threadB.Join(); |
| + } |
| + private: |
| + v8::Isolate* isolate1_; |
| + v8::Isolate* isolate2_; |
| +}; |
| + |
| +// Run parallel threads that lock and access different isolates in parallel |
| +TEST(SeparateIsolatesLocksNonexclusive) { |
| + const int kNThreads = 100; |
| + v8::Isolate* isolate1 = v8::Isolate::New(); |
| + v8::Isolate* isolate2 = v8::Isolate::New(); |
| + i::List<JoinableThread*> threads(kNThreads); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(new SeparateIsolatesLocksNonexclusiveThread(isolate1, |
| + isolate2)); |
| + } |
| + StartJoinAndDeleteThreads(threads); |
| + isolate2->Dispose(); |
| + isolate1->Dispose(); |
| +} |
| + |
| +class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread { |
| + public: |
| + explicit LockIsolateAndCalculateFibSharedContextThread( |
| + v8::Isolate* isolate, v8::Handle<v8::Context> context) |
| + : JoinableThread("LockIsolateAndCalculateFibThread"), |
| + isolate_(isolate), |
| + context_(context) { |
| + } |
| + |
| + virtual void Run() { |
| + v8::Locker lock(isolate_); |
| + v8::Isolate::Scope isolate_scope(isolate_); |
| + HandleScope handle_scope; |
| + v8::Context::Scope context_scope(context_); |
| + CalcFibAndCheck(); |
| + } |
| + private: |
| + v8::Isolate* isolate_; |
| + Persistent<v8::Context> context_; |
| +}; |
| + |
| +class LockerUnlockerThread : public JoinableThread { |
| + public: |
| + explicit LockerUnlockerThread(v8::Isolate* isolate) |
| + : JoinableThread("LockerUnlockerThread"), |
| + isolate_(isolate) { |
| + } |
| + |
| + virtual void Run() { |
| + v8::Locker lock(isolate_); |
| + v8::Isolate::Scope isolate_scope(isolate_); |
| + v8::HandleScope handle_scope; |
| + v8::Handle<v8::Context> context = v8::Context::New(); |
| + { |
| + v8::Context::Scope context_scope(context); |
| + CalcFibAndCheck(); |
| + } |
| + { |
| + isolate_->Exit(); |
| + v8::Unlocker unlocker(isolate_); |
| + LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context); |
| + thread.Start(); |
| + thread.Join(); |
| + } |
| + isolate_->Enter(); |
| + { |
| + v8::Context::Scope context_scope(context); |
| + CalcFibAndCheck(); |
| + } |
| + } |
| + private: |
| + v8::Isolate* isolate_; |
| +}; |
| + |
| +// Use unlocker inside of a Locker, multiple threads. |
| +TEST(LockerUnlocker) { |
| + const int kNThreads = 100; |
| + i::List<JoinableThread*> threads(kNThreads); |
| + v8::Isolate* isolate = v8::Isolate::New(); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(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; |
| + v8::Handle<v8::Context> context = v8::Context::New(); |
| + { |
| + v8::Context::Scope context_scope(context); |
| + CalcFibAndCheck(); |
| + } |
| + { |
| + v8::Locker second_lock(isolate_); |
| + { |
| + isolate_->Exit(); |
| + v8::Unlocker unlocker(isolate_); |
| + LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context); |
| + thread.Start(); |
| + thread.Join(); |
| + } |
| + } |
| + isolate_->Enter(); |
| + { |
| + v8::Context::Scope context_scope(context); |
| + CalcFibAndCheck(); |
| + } |
| + } |
| + private: |
| + v8::Isolate* isolate_; |
| +}; |
| + |
| +// Use Unlocker inside two Lockers. |
| +TEST(LockTwiceAndUnlock) { |
| + const int kNThreads = 100; |
| + i::List<JoinableThread*> threads(kNThreads); |
| + v8::Isolate* isolate = v8::Isolate::New(); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(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() { |
| + Persistent<v8::Context> context1; |
| + Persistent<v8::Context> context2; |
| + v8::Locker lock1(isolate1_); |
| + CHECK(v8::Locker::IsLocked(isolate1_)); |
| + CHECK(!v8::Locker::IsLocked(isolate2_)); |
| + { |
| + v8::Isolate::Scope isolate_scope(isolate1_); |
| + v8::HandleScope handle_scope; |
| + context1 = v8::Context::New(); |
| + { |
| + v8::Context::Scope context_scope(context1); |
| + CalcFibAndCheck(); |
| + } |
| + } |
| + v8::Locker lock2(isolate2_); |
| + CHECK(v8::Locker::IsLocked(isolate1_)); |
| + CHECK(v8::Locker::IsLocked(isolate2_)); |
| + { |
| + v8::Isolate::Scope isolate_scope(isolate2_); |
| + v8::HandleScope handle_scope; |
| + context2 = v8::Context::New(); |
| + { |
| + v8::Context::Scope context_scope(context2); |
| + CalcFibAndCheck(); |
| + } |
| + } |
| + { |
| + v8::Unlocker unlock1(isolate1_); |
| + CHECK(!v8::Locker::IsLocked(isolate1_)); |
| + CHECK(v8::Locker::IsLocked(isolate2_)); |
| + v8::Isolate::Scope isolate_scope(isolate2_); |
| + v8::HandleScope handle_scope; |
| + v8::Context::Scope context_scope(context2); |
| + LockIsolateAndCalculateFibSharedContextThread thread(isolate1_, context1); |
| + thread.Start(); |
| + CalcFibAndCheck(); |
| + thread.Join(); |
| + } |
| + } |
| + private: |
| + v8::Isolate* isolate1_; |
| + v8::Isolate* isolate2_; |
| +}; |
| + |
| +// Lock two isolates and unlock one of them. |
| +TEST(LockAndUnlockDifferentIsolates) { |
| + v8::Isolate* isolate1 = v8::Isolate::New(); |
| + v8::Isolate* isolate2 = v8::Isolate::New(); |
| + LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2); |
| + thread.Start(); |
| + thread.Join(); |
| + isolate2->Dispose(); |
| + isolate1->Dispose(); |
| +} |
| + |
| +class LockUnlockLockThread : public JoinableThread { |
| + public: |
| + LockUnlockLockThread(v8::Isolate* isolate, v8::Handle<v8::Context> context) |
| + : JoinableThread("LockUnlockLockThread"), |
| + isolate_(isolate), |
| + context_(context) { |
| + } |
| + |
| + virtual void Run() { |
| + v8::Locker lock1(isolate_); |
| + CHECK(v8::Locker::IsLocked(isolate_)); |
| + CHECK(!v8::Locker::IsLocked()); |
| + { |
| + v8::Isolate::Scope isolate_scope(isolate_); |
| + v8::HandleScope handle_scope; |
| + v8::Context::Scope context_scope(context_); |
| + CalcFibAndCheck(); |
| + } |
| + { |
| + v8::Unlocker unlock1(isolate_); |
| + CHECK(!v8::Locker::IsLocked(isolate_)); |
| + CHECK(!v8::Locker::IsLocked()); |
| + { |
| + v8::Locker lock2(isolate_); |
| + v8::Isolate::Scope isolate_scope(isolate_); |
| + v8::HandleScope handle_scope; |
| + CHECK(v8::Locker::IsLocked(isolate_)); |
| + CHECK(!v8::Locker::IsLocked()); |
| + v8::Context::Scope context_scope(context_); |
| + CalcFibAndCheck(); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + v8::Isolate* isolate_; |
| + v8::Persistent<v8::Context> context_; |
| +}; |
| + |
| +// Locker inside an Unlocker inside a Locker. |
| +TEST(LockUnlockLockMultithreaded) { |
| + const int kNThreads = 100; |
| + v8::Isolate* isolate = v8::Isolate::New(); |
| + Persistent<v8::Context> context; |
| + { |
| + v8::Locker locker_(isolate); |
| + v8::Isolate::Scope isolate_scope(isolate); |
| + v8::HandleScope handle_scope; |
| + context = v8::Context::New(); |
| + } |
| + i::List<JoinableThread*> threads(kNThreads); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(new LockUnlockLockThread(isolate, context)); |
| + } |
| + StartJoinAndDeleteThreads(threads); |
| +} |
| + |
| +class LockUnlockLockDefaultIsolateThread : public JoinableThread { |
| + public: |
| + LockUnlockLockDefaultIsolateThread(v8::Handle<v8::Context> context) |
| + : JoinableThread("LockUnlockLockDefaultIsolateThread"), |
| + context_(context) { |
| + } |
| + |
| + virtual void Run() { |
| + v8::Locker lock1; |
| + { |
| + v8::HandleScope handle_scope; |
| + v8::Context::Scope context_scope(context_); |
| + CalcFibAndCheck(); |
| + } |
| + { |
| + v8::Unlocker unlock1; |
| + { |
| + v8::Locker lock2; |
| + v8::HandleScope handle_scope; |
| + v8::Context::Scope context_scope(context_); |
| + CalcFibAndCheck(); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + v8::Persistent<v8::Context> context_; |
| +}; |
| + |
| +// Locker inside an Unlocker inside a Locker for default isolate. |
| +TEST(LockUnlockLockDefaultIsolateMultithreaded) { |
| + const int kNThreads = 100; |
| + Persistent<v8::Context> context; |
| + { |
| + v8::Locker locker_; |
| + v8::HandleScope handle_scope; |
| + context = v8::Context::New(); |
| + } |
| + i::List<JoinableThread*> threads(kNThreads); |
| + for (int i = 0; i < kNThreads; i++) { |
| + threads.Add(new LockUnlockLockDefaultIsolateThread(context)); |
| + } |
| + StartJoinAndDeleteThreads(threads); |
| +} |