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..ba0fdb22066a89bbba9f3e6c564479bab77ac13f |
--- /dev/null |
+++ b/test/cctest/test-lockers.cc |
@@ -0,0 +1,605 @@ |
+// 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 "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: |
+ explicit 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); |
+} |