This implements per-isolate locking and unlocking, including tests
authordslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 5 May 2011 18:55:31 +0000 (18:55 +0000)
committerdslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 5 May 2011 18:55:31 +0000 (18:55 +0000)
BUG=
TEST=

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

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

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

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

23 files changed:
include/v8.h
src/api.cc
src/api.h
src/arm/simulator-arm.cc
src/arm/simulator-arm.h
src/assembler.h
src/execution.cc
src/execution.h
src/ia32/simulator-ia32.h
src/isolate.cc
src/isolate.h
src/mips/simulator-mips.cc
src/mips/simulator-mips.h
src/objects.cc
src/objects.h
src/top.cc
src/v8threads.cc
src/x64/simulator-x64.h
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 78ee7e6..4921823 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 c72857d..c5c66a7 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 cd0205b..90cada7 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 93933ba..6af5355 100644 (file)
@@ -719,20 +719,21 @@ void Simulator::CheckICache(v8::internal::HashMap* i_cache,
 }
 
 
-void Simulator::Initialize() {
-  if (Isolate::Current()->simulator_initialized()) return;
-  Isolate::Current()->set_simulator_initialized(true);
-  ::v8::internal::ExternalReference::set_redirector(&RedirectExternalReference);
+void Simulator::Initialize(Isolate* isolate) {
+  if (isolate->simulator_initialized()) return;
+  isolate->set_simulator_initialized(true);
+  ::v8::internal::ExternalReference::set_redirector(isolate,
+                                                    &RedirectExternalReference);
 }
 
 
-Simulator::Simulator() : isolate_(Isolate::Current()) {
+Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
   i_cache_ = isolate_->simulator_i_cache();
   if (i_cache_ == NULL) {
     i_cache_ = new v8::internal::HashMap(&ICacheMatch);
     isolate_->set_simulator_i_cache(i_cache_);
   }
-  Initialize();
+  Initialize(isolate);
   // Setup simulator support first. Some of this information is needed to
   // setup the architecture state.
   size_t stack_size = 1 * 1024*1024;  // allocate 1MB for stack
@@ -848,17 +849,13 @@ void* Simulator::RedirectExternalReference(void* external_function,
 // Get the active Simulator for the current thread.
 Simulator* Simulator::current(Isolate* isolate) {
   v8::internal::Isolate::PerIsolateThreadData* isolate_data =
-      Isolate::CurrentPerIsolateThreadData();
-  if (isolate_data == NULL) {
-    Isolate::EnterDefaultIsolate();
-    isolate_data = Isolate::CurrentPerIsolateThreadData();
-  }
+      isolate->FindOrAllocatePerThreadDataForThisThread();
   ASSERT(isolate_data != NULL);
 
   Simulator* sim = isolate_data->simulator();
   if (sim == NULL) {
     // TODO(146): delete the simulator object when a thread/isolate goes away.
-    sim = new Simulator();
+    sim = new Simulator(isolate);
     isolate_data->set_simulator(sim);
   }
   return sim;
index 40c9d6d..391ef69 100644 (file)
@@ -68,7 +68,9 @@ typedef int (*arm_regexp_matcher)(String*, int, const byte*, const byte*,
 // just use the C stack limit.
 class SimulatorStack : public v8::internal::AllStatic {
  public:
-  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+  static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
+                                            uintptr_t c_limit) {
+    USE(isolate);
     return c_limit;
   }
 
@@ -143,7 +145,7 @@ class Simulator {
     num_d_registers = 16
   };
 
-  Simulator();
+  explicit Simulator(Isolate* isolate);
   ~Simulator();
 
   // The currently executing Simulator instance. Potentially there can be one
@@ -179,7 +181,7 @@ class Simulator {
   void Execute();
 
   // Call on program start.
-  static void Initialize();
+  static void Initialize(Isolate* isolate);
 
   // V8 generally calls into generated JS code with 5 parameters and into
   // generated RegExp code with 7 parameters. This is a convenience function,
@@ -408,8 +410,9 @@ class Simulator {
 // trouble down the line.
 class SimulatorStack : public v8::internal::AllStatic {
  public:
-  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
-    return Simulator::current(Isolate::Current())->StackLimit();
+  static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
+                                            uintptr_t c_limit) {
+    return Simulator::current(isolate)->StackLimit();
   }
 
   static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
index ef4ddc0..d2807ac 100644 (file)
@@ -673,10 +673,11 @@ class ExternalReference BASE_EMBEDDED {
 
   // This lets you register a function that rewrites all external references.
   // Used by the ARM simulator to catch calls to external references.
-  static void set_redirector(ExternalReferenceRedirector* redirector) {
+  static void set_redirector(Isolate* isolate,
+                             ExternalReferenceRedirector* redirector) {
     // We can't stack them.
-    ASSERT(Isolate::Current()->external_reference_redirector() == NULL);
-    Isolate::Current()->set_external_reference_redirector(
+    ASSERT(isolate->external_reference_redirector() == NULL);
+    isolate->set_external_reference_redirector(
         reinterpret_cast<ExternalReferenceRedirectorPointer*>(redirector));
   }
 
index 850dec5..4ab3e78 100644 (file)
@@ -296,7 +296,7 @@ void StackGuard::SetStackLimit(uintptr_t limit) {
   ExecutionAccess access(isolate_);
   // If the current limits are special (eg due to a pending interrupt) then
   // leave them alone.
-  uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(limit);
+  uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, limit);
   if (thread_local_.jslimit_ == thread_local_.real_jslimit_) {
     thread_local_.jslimit_ = jslimit;
   }
@@ -452,7 +452,7 @@ void StackGuard::ThreadLocal::Clear() {
 }
 
 
-bool StackGuard::ThreadLocal::Initialize() {
+bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) {
   bool should_set_stack_limits = false;
   if (real_climit_ == kIllegalLimit) {
     // Takes the address of the limit variable in order to find out where
@@ -460,8 +460,8 @@ bool StackGuard::ThreadLocal::Initialize() {
     const uintptr_t kLimitSize = FLAG_stack_size * KB;
     uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize;
     ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
-    real_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
-    jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
+    real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
+    jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
     real_climit_ = limit;
     climit_ = limit;
     should_set_stack_limits = true;
@@ -480,9 +480,10 @@ 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();
+  if (thread_local_.Initialize(isolate_)) isolate_->heap()->SetStackLimits();
+  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);
@@ -705,13 +706,13 @@ static Object* RuntimePreempt() {
     isolate->debug()->PreemptionWhileInDebugger();
   } else {
     // Perform preemption.
-    v8::Unlocker unlocker;
+    v8::Unlocker unlocker(reinterpret_cast<v8::Isolate*>(isolate));
     Thread::YieldCPU();
   }
 #else
   { // NOLINT
     // Perform preemption.
-    v8::Unlocker unlocker;
+    v8::Unlocker unlocker(reinterpret_cast<v8::Isolate*>(isolate));
     Thread::YieldCPU();
   }
 #endif
index e89d6ba..8f83b89 100644 (file)
@@ -254,7 +254,7 @@ class StackGuard {
     void Clear();
 
     // Returns true if the heap's stack limits should be set, false if not.
-    bool Initialize();
+    bool Initialize(Isolate* isolate);
 
     // The stack limit is split into a JavaScript and a C++ stack limit. These
     // two are the same except when running on a simulator where the C++ and
index cb660cd..13ddf35 100644 (file)
@@ -56,7 +56,9 @@ typedef int (*regexp_matcher)(String*, int, const byte*,
 // just use the C stack limit.
 class SimulatorStack : public v8::internal::AllStatic {
  public:
-  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+  static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+                                            uintptr_t c_limit) {
+    USE(isolate);
     return c_limit;
   }
 
index 6fc671c..4871702 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();
@@ -757,7 +773,7 @@ bool Isolate::Init(Deserializer* des) {
   // Initialize other runtime facilities
 #if defined(USE_SIMULATOR)
 #if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
-  Simulator::Initialize();
+  Simulator::Initialize(this);
 #endif
 #endif
 
index 44190c2..088c247 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,13 @@ class Isolate {
 
   friend class ExecutionAccess;
   friend class IsolateInitializer;
+  friend class ThreadManager;
+  friend class Simulator;
+  friend class StackGuard;
   friend class ThreadId;
   friend class v8::Isolate;
   friend class v8::Locker;
+  friend class v8::Unlocker;
 
   DISALLOW_COPY_AND_ASSIGN(Isolate);
 };
index 50ad7a1..803cd9e 100644 (file)
@@ -738,20 +738,21 @@ void Simulator::CheckICache(v8::internal::HashMap* i_cache,
 }
 
 
-void Simulator::Initialize() {
-  if (Isolate::Current()->simulator_initialized()) return;
-  Isolate::Current()->set_simulator_initialized(true);
-  ::v8::internal::ExternalReference::set_redirector(&RedirectExternalReference);
+void Simulator::Initialize(Isolate* isolate) {
+  if (isolate->simulator_initialized()) return;
+  isolate->set_simulator_initialized(true);
+  ::v8::internal::ExternalReference::set_redirector(isolate,
+                                                    &RedirectExternalReference);
 }
 
 
-Simulator::Simulator() : isolate_(Isolate::Current()) {
+Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
   i_cache_ = isolate_->simulator_i_cache();
   if (i_cache_ == NULL) {
     i_cache_ = new v8::internal::HashMap(&ICacheMatch);
     isolate_->set_simulator_i_cache(i_cache_);
   }
-  Initialize();
+  Initialize(isolate);
   // Setup simulator support first. Some of this information is needed to
   // setup the architecture state.
   stack_size_ = 1 * 1024*1024;  // allocate 1MB for stack
@@ -852,17 +853,14 @@ void* Simulator::RedirectExternalReference(void* external_function,
 // Get the active Simulator for the current thread.
 Simulator* Simulator::current(Isolate* isolate) {
   v8::internal::Isolate::PerIsolateThreadData* isolate_data =
-      Isolate::CurrentPerIsolateThreadData();
-  if (isolate_data == NULL) {
-    Isolate::EnterDefaultIsolate();
-    isolate_data = Isolate::CurrentPerIsolateThreadData();
-  }
+       isolate->FindOrAllocatePerThreadDataForThisThread();
+  ASSERT(isolate_data != NULL);
   ASSERT(isolate_data != NULL);
 
   Simulator* sim = isolate_data->simulator();
   if (sim == NULL) {
     // TODO(146): delete the simulator object when a thread/isolate goes away.
-    sim = new Simulator();
+    sim = new Simulator(isolate);
     isolate_data->set_simulator(sim);
   }
   return sim;
index 0cd9bbe..b10eeab 100644 (file)
@@ -68,7 +68,8 @@ typedef int (*mips_regexp_matcher)(String*, int, const byte*, const byte*,
 // just use the C stack limit.
 class SimulatorStack : public v8::internal::AllStatic {
  public:
-  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+  static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+                                            uintptr_t c_limit) {
     return c_limit;
   }
 
@@ -170,7 +171,7 @@ class Simulator {
     kNumFPURegisters
   };
 
-  Simulator();
+  explicit Simulator(Isolate* isolate);
   ~Simulator();
 
   // The currently executing Simulator instance. Potentially there can be one
@@ -205,7 +206,7 @@ class Simulator {
   void Execute();
 
   // Call on program start.
-  static void Initialize();
+  static void Initialize(Isolate* isolate);
 
   // V8 generally calls into generated JS code with 5 parameters and into
   // generated RegExp code with 7 parameters. This is a convenience function,
@@ -373,8 +374,9 @@ reinterpret_cast<Object*>(Simulator::current(Isolate::Current())->Call( \
 // trouble down the line.
 class SimulatorStack : public v8::internal::AllStatic {
  public:
-  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
-    return Simulator::current(Isolate::Current())->StackLimit();
+  static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+                                            uintptr_t c_limit) {
+    return Simulator::current(isolate)->StackLimit();
   }
 
   static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
index f42be65..519909c 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 556aaa2..1e62ce0 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 f44de7a..6d68231 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 4b033fc..26169b5 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 cfaa5b8..df8423a 100644 (file)
@@ -55,7 +55,8 @@ typedef int (*regexp_matcher)(String*, int, const byte*,
 // just use the C stack limit.
 class SimulatorStack : public v8::internal::AllStatic {
  public:
-  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+  static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+                                            uintptr_t c_limit) {
     return c_limit;
   }
 
index f4cb4a9..e2b0fc9 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 aa2b355..51a5fc7 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 c6affe5..52adfa0 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 5635b17..1e8a627 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 = 2;
+  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;