This implements per-isolate locking and unlocking, including tests
authordslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 4 May 2011 21:04:32 +0000 (21:04 +0000)
committerdslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 4 May 2011 21:04:32 +0000 (21:04 +0000)
BUG=
TEST=

Committed: http://code.google.com/p/v8/source/detail?r=7734

Review URL: http://codereview.chromium.org/6788023

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7784 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

15 files changed:
include/v8.h
src/api.cc
src/api.h
src/execution.cc
src/isolate.cc
src/isolate.h
src/objects.cc
src/objects.h
src/top.cc
src/v8threads.cc
test/cctest/SConscript
test/cctest/cctest.gyp
test/cctest/test-api.cc
test/cctest/test-lockers.cc [new file with mode: 0644]
test/cctest/test-thread-termination.cc

index 78ee7e6a0b63396da9262534a8696838f7e99b8f..4921823115126fd240909b657b5735373383e8b0 100644 (file)
@@ -3393,26 +3393,20 @@ class V8EXPORT Context {
  * to the user of V8 to ensure (perhaps with locking) that this
  * constraint is not violated.
  *
- * More then one thread and multiple V8 isolates can be used
- * without any locking if each isolate is created and accessed
- * by a single thread only. For example, one thread can use
- * multiple isolates or multiple threads can each create and run
- * their own isolate.
+ * v8::Locker is a scoped lock object. While it's
+ * active (i.e. between its construction and destruction) the current thread is
+ * allowed to use the locked isolate. V8 guarantees that an isolate can be locked
+ * by at most one thread at any time. In other words, the scope of a v8::Locker is
+ * a critical section.
  *
- * If you wish to start using V8 isolate in more then one thread
- * you can do this by constructing a v8::Locker object to guard
- * access to the isolate. After the code using V8 has completed
- * for the current thread you can call the destructor.  This can
- * be combined with C++ scope-based construction as follows
- * (assumes the default isolate that is used if not specified as
- * a parameter for the Locker):
- *
- * \code
+ * Sample usage:
+* \code
  * ...
  * {
- *   v8::Locker locker;
+ *   v8::Locker locker(isolate);
+ *   v8::Isolate::Scope isolate_scope(isolate);
  *   ...
- *   // Code using V8 goes here.
+ *   // Code using V8 and isolate goes here.
  *   ...
  * } // Destructor called here
  * \endcode
@@ -3423,11 +3417,13 @@ class V8EXPORT Context {
  *
  * \code
  * {
- *   v8::Unlocker unlocker;
+ *   isolate->Exit();
+ *   v8::Unlocker unlocker(isolate);
  *   ...
  *   // Code not using V8 goes here while V8 can run in another thread.
  *   ...
  * } // Destructor called here.
+ * isolate->Enter();
  * \endcode
  *
  * The Unlocker object is intended for use in a long-running callback
@@ -3447,32 +3443,45 @@ class V8EXPORT Context {
  * \code
  * // V8 not locked.
  * {
- *   v8::Locker locker;
+ *   v8::Locker locker(isolate);
+ *   Isolate::Scope isolate_scope(isolate);
  *   // V8 locked.
  *   {
- *     v8::Locker another_locker;
+ *     v8::Locker another_locker(isolate);
  *     // V8 still locked (2 levels).
  *     {
- *       v8::Unlocker unlocker;
+ *       isolate->Exit();
+ *       v8::Unlocker unlocker(isolate);
  *       // V8 not locked.
  *     }
+ *     isolate->Enter();
  *     // V8 locked again (2 levels).
  *   }
  *   // V8 still locked (1 level).
  * }
  * // V8 Now no longer locked.
  * \endcode
+ *
+ * 
  */
 class V8EXPORT Unlocker {
  public:
-  Unlocker();
+  /**
+   * Initialize Unlocker for a given Isolate. NULL means default isolate.
+   */
+  explicit Unlocker(Isolate* isolate = NULL);
   ~Unlocker();
+ private:
+  internal::Isolate* isolate_;
 };
 
 
 class V8EXPORT Locker {
  public:
-  Locker();
+  /**
+   * Initialize Locker for a given Isolate. NULL means default isolate.
+   */
+  explicit Locker(Isolate* isolate = NULL);
   ~Locker();
 
   /**
@@ -3490,9 +3499,10 @@ class V8EXPORT Locker {
   static void StopPreemption();
 
   /**
-   * Returns whether or not the locker is locked by the current thread.
+   * Returns whether or not the locker for a given isolate, or default isolate if NULL is given,
+   * is locked by the current thread.
    */
-  static bool IsLocked();
+  static bool IsLocked(Isolate* isolate = NULL);
 
   /**
    * Returns whether v8::Locker is being used by this V8 instance.
@@ -3502,6 +3512,7 @@ class V8EXPORT Locker {
  private:
   bool has_lock_;
   bool top_level_;
+  internal::Isolate* isolate_;
 
   static bool active_;
 
index c72857da644eedb55f4ce93002f73ef7e20b29bf..c5c66a799fd5c07e59074f279fb0b480189e65a6 100644 (file)
@@ -53,7 +53,6 @@
 
 #define LOG_API(isolate, expr) LOG(isolate, ApiEntryCall(expr))
 
-// TODO(isolates): avoid repeated TLS reads in function prologues.
 #ifdef ENABLE_VMSTATE_TRACKING
 #define ENTER_V8(isolate)                                        \
   ASSERT((isolate)->IsInitialized());                           \
@@ -290,6 +289,7 @@ static inline bool EnsureInitializedForIsolate(i::Isolate* isolate,
   if (isolate != NULL) {
     if (isolate->IsInitialized()) return true;
   }
+  ASSERT(isolate == i::Isolate::Current());
   return ApiCheck(InitializeHelper(), location, "Error initializing V8");
 }
 
@@ -5739,9 +5739,8 @@ void HandleScopeImplementer::FreeThreadResources() {
 
 
 char* HandleScopeImplementer::ArchiveThread(char* storage) {
-  Isolate* isolate = Isolate::Current();
   v8::ImplementationUtilities::HandleScopeData* current =
-      isolate->handle_scope_data();
+      isolate_->handle_scope_data();
   handle_scope_data_ = *current;
   memcpy(storage, this, sizeof(*this));
 
@@ -5759,7 +5758,7 @@ int HandleScopeImplementer::ArchiveSpacePerThread() {
 
 char* HandleScopeImplementer::RestoreThread(char* storage) {
   memcpy(this, storage, sizeof(*this));
-  *Isolate::Current()->handle_scope_data() = handle_scope_data_;
+  *isolate_->handle_scope_data() = handle_scope_data_;
   return storage + ArchiveSpacePerThread();
 }
 
@@ -5785,7 +5784,7 @@ void HandleScopeImplementer::IterateThis(ObjectVisitor* v) {
 
 void HandleScopeImplementer::Iterate(ObjectVisitor* v) {
   v8::ImplementationUtilities::HandleScopeData* current =
-      Isolate::Current()->handle_scope_data();
+      isolate_->handle_scope_data();
   handle_scope_data_ = *current;
   IterateThis(v);
 }
index cd0205b2cc374b68612d965f30a8b13ae1e51f80..90cada7a3b2eb08ba06f8fe5556865f41901a018 100644 (file)
--- a/src/api.h
+++ b/src/api.h
@@ -399,8 +399,9 @@ class StringTracker {
 ISOLATED_CLASS HandleScopeImplementer {
  public:
 
-  HandleScopeImplementer()
-      : blocks_(0),
+  explicit HandleScopeImplementer(Isolate* isolate)
+      : isolate_(isolate),
+        blocks_(0),
         entered_contexts_(0),
         saved_contexts_(0),
         spare_(NULL),
@@ -466,6 +467,7 @@ ISOLATED_CLASS HandleScopeImplementer {
     ASSERT(call_depth_ == 0);
   }
 
+  Isolate* isolate_;
   List<internal::Object**> blocks_;
   // Used as a stack to keep track of entered contexts.
   List<Handle<Object> > entered_contexts_;
index 850dec561f8151b66abd818831964c939e176a3e..5215bf7d79c5aa12be0380821d6e659a4e5a18f5 100644 (file)
@@ -481,8 +481,9 @@ void StackGuard::ClearThread(const ExecutionAccess& lock) {
 
 void StackGuard::InitThread(const ExecutionAccess& lock) {
   if (thread_local_.Initialize()) isolate_->heap()->SetStackLimits();
-  uintptr_t stored_limit =
-      Isolate::CurrentPerIsolateThreadData()->stack_limit();
+  Isolate::PerIsolateThreadData* per_thread =
+      isolate_->FindOrAllocatePerThreadDataForThisThread();
+  uintptr_t stored_limit = per_thread->stack_limit();
   // You should hold the ExecutionAccess lock when you call this.
   if (stored_limit != 0) {
     StackGuard::SetStackLimit(stored_limit);
index 6fc671c50c404d66b11c7bfe92eed7883ac698e9..0b0f0bf58259cd68c20ddb508a274e8d5dc879ca 100644 (file)
@@ -311,6 +311,17 @@ Isolate::PerIsolateThreadData*
 }
 
 
+Isolate::PerIsolateThreadData* Isolate::FindPerThreadDataForThisThread() {
+  ThreadId thread_id = ThreadId::Current();
+  PerIsolateThreadData* per_thread = NULL;
+  {
+    ScopedLock lock(process_wide_mutex_);
+    per_thread = thread_data_table_->Lookup(this, thread_id);
+  }
+  return per_thread;
+}
+
+
 void Isolate::EnsureDefaultIsolate() {
   ScopedLock lock(process_wide_mutex_);
   if (default_isolate_ == NULL) {
@@ -322,7 +333,9 @@ void Isolate::EnsureDefaultIsolate() {
   }
   // Can't use SetIsolateThreadLocals(default_isolate_, NULL) here
   // becase a non-null thread data may be already set.
-  Thread::SetThreadLocal(isolate_key_, default_isolate_);
+  if (Thread::GetThreadLocal(isolate_key_) == NULL) {
+    Thread::SetThreadLocal(isolate_key_, default_isolate_);
+  }
   CHECK(default_isolate_->PreInit());
 }
 
@@ -458,6 +471,11 @@ Isolate::Isolate()
   zone_.isolate_ = this;
   stack_guard_.isolate_ = this;
 
+  // ThreadManager is initialized early to support locking an isolate
+  // before it is entered.
+  thread_manager_ = new ThreadManager();
+  thread_manager_->isolate_ = this;
+
 #if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
     defined(V8_TARGET_ARCH_MIPS) && !defined(__mips__)
   simulator_initialized_ = false;
@@ -643,7 +661,6 @@ bool Isolate::PreInit() {
   TRACE_ISOLATE(preinit);
 
   ASSERT(Isolate::Current() == this);
-
 #ifdef ENABLE_DEBUGGER_SUPPORT
   debug_ = new Debug(this);
   debugger_ = new Debugger();
@@ -671,8 +688,6 @@ bool Isolate::PreInit() {
 
   string_tracker_ = new StringTracker();
   string_tracker_->isolate_ = this;
-  thread_manager_ = new ThreadManager();
-  thread_manager_->isolate_ = this;
   compilation_cache_ = new CompilationCache(this);
   transcendental_cache_ = new TranscendentalCache();
   keyed_lookup_cache_ = new KeyedLookupCache();
@@ -683,7 +698,7 @@ bool Isolate::PreInit() {
   write_input_buffer_ = new StringInputBuffer();
   global_handles_ = new GlobalHandles(this);
   bootstrapper_ = new Bootstrapper();
-  handle_scope_implementer_ = new HandleScopeImplementer();
+  handle_scope_implementer_ = new HandleScopeImplementer(this);
   stub_cache_ = new StubCache(this);
   ast_sentinels_ = new AstSentinels();
   regexp_stack_ = new RegExpStack();
@@ -700,6 +715,7 @@ bool Isolate::PreInit() {
 
 
 void Isolate::InitializeThreadLocal() {
+  thread_local_top_.isolate_ = this;
   thread_local_top_.Initialize();
   clear_pending_exception();
   clear_pending_message();
index 44190c2adcedf4018e36ee3c08380b343a206f88..dc4e87826a633ac587b665c1d3ffa53a236f0d8c 100644 (file)
@@ -224,6 +224,7 @@ class ThreadLocalTop BASE_EMBEDDED {
     ASSERT(try_catch_handler_address_ == NULL);
   }
 
+  Isolate* isolate_;
   // The context where the current execution method is created and for variable
   // lookups.
   Context* context_;
@@ -487,6 +488,10 @@ class Isolate {
   // Safe to call multiple times.
   static void EnsureDefaultIsolate();
 
+  // Find the PerThread for this particular (isolate, thread) combination
+  // If one does not yet exist, return null.
+  PerIsolateThreadData* FindPerThreadDataForThisThread();
+
 #ifdef ENABLE_DEBUGGER_SUPPORT
   // Get the debugger from the default isolate. Preinitializes the
   // default isolate if needed.
@@ -1066,7 +1071,7 @@ class Isolate {
   // If one does not yet exist, allocate a new one.
   PerIsolateThreadData* FindOrAllocatePerThreadDataForThisThread();
 
-  // PreInits and returns a default isolate. Needed when a new thread tries
+// PreInits and returns a default isolate. Needed when a new thread tries
   // to create a Locker for the first time (the lock itself is in the isolate).
   static Isolate* GetDefaultIsolateForLocking();
 
@@ -1198,9 +1203,12 @@ class Isolate {
 
   friend class ExecutionAccess;
   friend class IsolateInitializer;
+  friend class ThreadManager;
+  friend class StackGuard;
   friend class ThreadId;
   friend class v8::Isolate;
   friend class v8::Locker;
+  friend class v8::Unlocker;
 
   DISALLOW_COPY_AND_ASSIGN(Isolate);
 };
index f42be65a1c9d48e7b3bbf18e2da28acc1fb9177a..519909c914185aa899860cb77ee185304db815d9 100644 (file)
@@ -4957,8 +4957,7 @@ int Relocatable::ArchiveSpacePerThread() {
 
 
 // Archive statics that are thread local.
-char* Relocatable::ArchiveState(char* to) {
-  Isolate* isolate = Isolate::Current();
+char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
   *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
   isolate->set_relocatable_top(NULL);
   return to + ArchiveSpacePerThread();
@@ -4966,8 +4965,7 @@ char* Relocatable::ArchiveState(char* to) {
 
 
 // Restore statics that are thread local.
-char* Relocatable::RestoreState(char* from) {
-  Isolate* isolate = Isolate::Current();
+char* Relocatable::RestoreState(Isolate* isolate, char* from) {
   isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
   return from + ArchiveSpacePerThread();
 }
index 556aaa2b75603f55cbbbc5642a96944d8c997f0b..1e62ce0110f74dcb9ed66d8ba04574770a49f4d2 100644 (file)
@@ -5924,8 +5924,8 @@ class Relocatable BASE_EMBEDDED {
 
   static void PostGarbageCollectionProcessing();
   static int ArchiveSpacePerThread();
-  static char* ArchiveState(char* to);
-  static char* RestoreState(char* from);
+  static char* ArchiveState(Isolate* isolate, char* to);
+  static char* RestoreState(Isolate* isolate, char* from);
   static void Iterate(ObjectVisitor* v);
   static void Iterate(ObjectVisitor* v, Relocatable* top);
   static char* Iterate(ObjectVisitor* v, char* t);
index f44de7ad68d41c308f27fa9674925d91d08294ad..6d68231351a2ed66d168a0d56f8c3e5cf1ba40c9 100644 (file)
@@ -77,9 +77,9 @@ void ThreadLocalTop::Initialize() {
   InitializeInternal();
 #ifdef USE_SIMULATOR
 #ifdef V8_TARGET_ARCH_ARM
-  simulator_ = Simulator::current(Isolate::Current());
+  simulator_ = Simulator::current(isolate_);
 #elif V8_TARGET_ARCH_MIPS
-  simulator_ = Simulator::current(Isolate::Current());
+  simulator_ = Simulator::current(isolate_);
 #endif
 #endif
   thread_id_ = ThreadId::Current();
index 4b033fcf0bd703cf12b5bdccfb116784b5a31d9a..26169b50be6c51c590737d003d3a924a14642d2d 100644 (file)
@@ -43,90 +43,94 @@ bool Locker::active_ = false;
 
 
 // Constructor for the Locker object.  Once the Locker is constructed the
-// current thread will be guaranteed to have the big V8 lock.
-Locker::Locker() : has_lock_(false), top_level_(true) {
-  // TODO(isolates): When Locker has Isolate parameter and it is provided, grab
-  // that one instead of using the current one.
-  // We pull default isolate for Locker constructor w/o p[arameter.
-  // A thread should not enter an isolate before acquiring a lock,
-  // in cases which mandate using Lockers.
-  // So getting a lock is the first thing threads do in a scenario where
-  // multple threads share an isolate. Hence, we need to access
-  // 'locking isolate' before we can actually enter into default isolate.
-  internal::Isolate* isolate = internal::Isolate::GetDefaultIsolateForLocking();
-  ASSERT(isolate != NULL);
-
+// current thread will be guaranteed to have the lock for a given isolate.
+Locker::Locker(v8::Isolate* isolate)
+  : has_lock_(false),
+    top_level_(false),
+    isolate_(reinterpret_cast<i::Isolate*>(isolate)) {
+  if (isolate_ == NULL) {
+    isolate_ = i::Isolate::GetDefaultIsolateForLocking();
+  }
   // Record that the Locker has been used at least once.
   active_ = true;
   // Get the big lock if necessary.
-  if (!isolate->thread_manager()->IsLockedByCurrentThread()) {
-    isolate->thread_manager()->Lock();
+  if (!isolate_->thread_manager()->IsLockedByCurrentThread()) {
+    isolate_->thread_manager()->Lock();
     has_lock_ = true;
 
-    if (isolate->IsDefaultIsolate()) {
-      // This only enters if not yet entered.
-      internal::Isolate::EnterDefaultIsolate();
-    }
-
-    ASSERT(internal::Thread::HasThreadLocal(
-        internal::Isolate::thread_id_key()));
-
     // Make sure that V8 is initialized.  Archiving of threads interferes
     // with deserialization by adding additional root pointers, so we must
     // initialize here, before anyone can call ~Locker() or Unlocker().
-    if (!isolate->IsInitialized()) {
+    if (!isolate_->IsInitialized()) {
+      isolate_->Enter();
       V8::Initialize();
+      isolate_->Exit();
     }
+
     // This may be a locker within an unlocker in which case we have to
     // get the saved state for this thread and restore it.
-    if (isolate->thread_manager()->RestoreThread()) {
+    if (isolate_->thread_manager()->RestoreThread()) {
       top_level_ = false;
     } else {
-      internal::ExecutionAccess access(isolate);
-      isolate->stack_guard()->ClearThread(access);
-      isolate->stack_guard()->InitThread(access);
+      internal::ExecutionAccess access(isolate_);
+      isolate_->stack_guard()->ClearThread(access);
+      isolate_->stack_guard()->InitThread(access);
+    }
+    if (isolate_->IsDefaultIsolate()) {
+      // This only enters if not yet entered.
+      internal::Isolate::EnterDefaultIsolate();
     }
   }
-  ASSERT(isolate->thread_manager()->IsLockedByCurrentThread());
+  ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
 }
 
 
-bool Locker::IsLocked() {
-  return internal::Isolate::Current()->thread_manager()->
-      IsLockedByCurrentThread();
+bool Locker::IsLocked(v8::Isolate* isolate) {
+  i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+  if (internal_isolate == NULL) {
+    internal_isolate = i::Isolate::GetDefaultIsolateForLocking();
+  }
+  return internal_isolate->thread_manager()->IsLockedByCurrentThread();
 }
 
 
 Locker::~Locker() {
-  // TODO(isolate): this should use a field storing the isolate it
-  // locked instead.
-  internal::Isolate* isolate = internal::Isolate::Current();
-  ASSERT(isolate->thread_manager()->IsLockedByCurrentThread());
+  ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
   if (has_lock_) {
+    if (isolate_->IsDefaultIsolate()) {
+      isolate_->Exit();
+    }
     if (top_level_) {
-      isolate->thread_manager()->FreeThreadResources();
+      isolate_->thread_manager()->FreeThreadResources();
     } else {
-      isolate->thread_manager()->ArchiveThread();
+      isolate_->thread_manager()->ArchiveThread();
     }
-    isolate->thread_manager()->Unlock();
+    isolate_->thread_manager()->Unlock();
   }
 }
 
 
-Unlocker::Unlocker() {
-  internal::Isolate* isolate = internal::Isolate::Current();
-  ASSERT(isolate->thread_manager()->IsLockedByCurrentThread());
-  isolate->thread_manager()->ArchiveThread();
-  isolate->thread_manager()->Unlock();
+Unlocker::Unlocker(v8::Isolate* isolate)
+  : isolate_(reinterpret_cast<i::Isolate*>(isolate)) {
+  if (isolate_ == NULL) {
+    isolate_ = i::Isolate::GetDefaultIsolateForLocking();
+  }
+  ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
+  if (isolate_->IsDefaultIsolate()) {
+    isolate_->Exit();
+  }
+  isolate_->thread_manager()->ArchiveThread();
+  isolate_->thread_manager()->Unlock();
 }
 
 
 Unlocker::~Unlocker() {
-  // TODO(isolates): check it's the isolate we unlocked.
-  internal::Isolate* isolate = internal::Isolate::Current();
-  ASSERT(!isolate->thread_manager()->IsLockedByCurrentThread());
-  isolate->thread_manager()->Lock();
-  isolate->thread_manager()->RestoreThread();
+  ASSERT(!isolate_->thread_manager()->IsLockedByCurrentThread());
+  isolate_->thread_manager()->Lock();
+  isolate_->thread_manager()->RestoreThread();
+  if (isolate_->IsDefaultIsolate()) {
+    isolate_->Enter();
+  }
 }
 
 
@@ -144,17 +148,20 @@ namespace internal {
 
 
 bool ThreadManager::RestoreThread() {
+  ASSERT(IsLockedByCurrentThread());
   // First check whether the current thread has been 'lazily archived', ie
   // not archived at all.  If that is the case we put the state storage we
   // had prepared back in the free list, since we didn't need it after all.
   if (lazily_archived_thread_.Equals(ThreadId::Current())) {
     lazily_archived_thread_ = ThreadId::Invalid();
-    ASSERT(Isolate::CurrentPerIsolateThreadData()->thread_state() ==
-           lazily_archived_thread_state_);
+    Isolate::PerIsolateThreadData* per_thread =
+        isolate_->FindPerThreadDataForThisThread();
+    ASSERT(per_thread != NULL);
+    ASSERT(per_thread->thread_state() == lazily_archived_thread_state_);
     lazily_archived_thread_state_->set_id(ThreadId::Invalid());
     lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
     lazily_archived_thread_state_ = NULL;
-    Isolate::CurrentPerIsolateThreadData()->set_thread_state(NULL);
+    per_thread->set_thread_state(NULL);
     return true;
   }
 
@@ -168,7 +175,7 @@ bool ThreadManager::RestoreThread() {
     EagerlyArchiveThread();
   }
   Isolate::PerIsolateThreadData* per_thread =
-      Isolate::CurrentPerIsolateThreadData();
+      isolate_->FindPerThreadDataForThisThread();
   if (per_thread == NULL || per_thread->thread_state() == NULL) {
     // This is a new thread.
     isolate_->stack_guard()->InitThread(access);
@@ -178,7 +185,7 @@ bool ThreadManager::RestoreThread() {
   char* from = state->data();
   from = isolate_->handle_scope_implementer()->RestoreThread(from);
   from = isolate_->RestoreThread(from);
-  from = Relocatable::RestoreState(from);
+  from = Relocatable::RestoreState(isolate_, from);
 #ifdef ENABLE_DEBUGGER_SUPPORT
   from = isolate_->debug()->RestoreDebug(from);
 #endif
@@ -300,9 +307,12 @@ ThreadManager::~ThreadManager() {
 void ThreadManager::ArchiveThread() {
   ASSERT(lazily_archived_thread_.Equals(ThreadId::Invalid()));
   ASSERT(!IsArchived());
+  ASSERT(IsLockedByCurrentThread());
   ThreadState* state = GetFreeThreadState();
   state->Unlink();
-  Isolate::CurrentPerIsolateThreadData()->set_thread_state(state);
+  Isolate::PerIsolateThreadData* per_thread =
+      isolate_->FindOrAllocatePerThreadDataForThisThread();
+  per_thread->set_thread_state(state);
   lazily_archived_thread_ = ThreadId::Current();
   lazily_archived_thread_state_ = state;
   ASSERT(state->id().Equals(ThreadId::Invalid()));
@@ -312,6 +322,7 @@ void ThreadManager::ArchiveThread() {
 
 
 void ThreadManager::EagerlyArchiveThread() {
+  ASSERT(IsLockedByCurrentThread());
   ThreadState* state = lazily_archived_thread_state_;
   state->LinkInto(ThreadState::IN_USE_LIST);
   char* to = state->data();
@@ -319,7 +330,7 @@ void ThreadManager::EagerlyArchiveThread() {
   // in ThreadManager::Iterate(ObjectVisitor*).
   to = isolate_->handle_scope_implementer()->ArchiveThread(to);
   to = isolate_->ArchiveThread(to);
-  to = Relocatable::ArchiveState(to);
+  to = Relocatable::ArchiveState(isolate_, to);
 #ifdef ENABLE_DEBUGGER_SUPPORT
   to = isolate_->debug()->ArchiveDebug(to);
 #endif
@@ -344,11 +355,11 @@ void ThreadManager::FreeThreadResources() {
 
 
 bool ThreadManager::IsArchived() {
-  Isolate::PerIsolateThreadData* data = Isolate::CurrentPerIsolateThreadData();
+  Isolate::PerIsolateThreadData* data =
+      isolate_->FindPerThreadDataForThisThread();
   return data != NULL && data->thread_state() != NULL;
 }
 
-
 void ThreadManager::Iterate(ObjectVisitor* v) {
   // Expecting no threads during serialization/deserialization
   for (ThreadState* state = FirstThreadStateInUse();
index f4cb4a9a60b3d7df0d907406f86f5261c27d5cd2..e2b0fc9f51d40a83c8532d5a0edeb2c606929030 100644 (file)
@@ -64,6 +64,7 @@ SOURCES = {
     'test-list.cc',
     'test-liveedit.cc',
     'test-lock.cc',
+    'test-lockers.cc',
     'test-log-utils.cc',
     'test-log.cc',
     'test-mark-compact.cc',
index aa2b355831ad90139dd09a6b5c1355310a299246..51a5fc7dec2a1c155de69120f9d0f96121654739 100644 (file)
@@ -93,6 +93,7 @@
         'test-list.cc',
         'test-liveedit.cc',
         'test-lock.cc',
+        'test-lockers.cc',
         'test-log.cc',
         'test-log-utils.cc',
         'test-mark-compact.cc',
index c6affe53ced6eb32b2503dbda2780d7f667307a2..52adfa06d51603391dd8348d21b1364f4de872bf 100644 (file)
@@ -30,6 +30,7 @@
 #include "v8.h"
 
 #include "api.h"
+#include "isolate.h"
 #include "compilation-cache.h"
 #include "execution.h"
 #include "snapshot.h"
@@ -13449,6 +13450,28 @@ TEST(MultipleIsolatesOnIndividualThreads) {
   isolate2->Dispose();
 }
 
+TEST(IsolateDifferentContexts) {
+  v8::Isolate* isolate = v8::Isolate::New();
+  Persistent<v8::Context> context;
+  {
+    v8::Isolate::Scope isolate_scope(isolate);
+    v8::HandleScope handle_scope;
+    context = v8::Context::New();
+    v8::Context::Scope context_scope(context);
+    Local<Value> v = CompileRun("2");
+    CHECK(v->IsNumber());
+    CHECK_EQ(2, static_cast<int>(v->NumberValue()));
+  }
+  {
+    v8::Isolate::Scope isolate_scope(isolate);
+    v8::HandleScope handle_scope;
+    context = v8::Context::New();
+    v8::Context::Scope context_scope(context);
+    Local<Value> v = CompileRun("22");
+    CHECK(v->IsNumber());
+    CHECK_EQ(22, static_cast<int>(v->NumberValue()));
+  }
+}
 
 class InitDefaultIsolateThread : public v8::internal::Thread {
  public:
diff --git a/test/cctest/test-lockers.cc b/test/cctest/test-lockers.cc
new file mode 100644 (file)
index 0000000..ba0fdb2
--- /dev/null
@@ -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);
+}
index 5635b1761c639829b4bce3ff555e6689fb899a87..2cd2e54667fc1764149aa587a03ff8f3aedaab22 100644 (file)
@@ -221,29 +221,37 @@ class LoopingThread : public v8::internal::Thread {
 };
 
 
-// Test that multiple threads using V8 can be terminated from another
-// thread when using Lockers and preemption.
-TEST(TerminateMultipleV8Threads) {
+// Test that multiple threads using default isolate can be terminated
+// from another thread when using Lockers and preemption.
+TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
   {
     v8::Locker locker;
     v8::V8::Initialize();
     v8::Locker::StartPreemption(1);
     semaphore = v8::internal::OS::CreateSemaphore(0);
   }
-  LoopingThread thread1(i::Isolate::Current());
-  thread1.Start();
-  LoopingThread thread2(i::Isolate::Current());
-  thread2.Start();
-  // Wait until both threads have signaled the semaphore.
-  semaphore->Wait();
-  semaphore->Wait();
+  const int kThreads = 10;
+  i::List<LoopingThread*> threads(kThreads);
+  for (int i = 0; i < kThreads; i++) {
+    threads.Add(new LoopingThread(i::Isolate::Current()));
+  }
+  for (int i = 0; i < kThreads; i++) {
+    threads[i]->Start();
+  }
+  // Wait until all threads have signaled the semaphore.
+  for (int i = 0; i < kThreads; i++) {
+    semaphore->Wait();
+  }
   {
     v8::Locker locker;
-    v8::V8::TerminateExecution(thread1.GetV8ThreadId());
-    v8::V8::TerminateExecution(thread2.GetV8ThreadId());
+    for (int i = 0; i < kThreads; i++) {
+      v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
+    }
+  }
+  for (int i = 0; i < kThreads; i++) {
+    threads[i]->Join();
+    delete threads[i];
   }
-  thread1.Join();
-  thread2.Join();
 
   delete semaphore;
   semaphore = NULL;