Revision: 5170
Author: [email protected]
Date: Mon Aug  2 17:41:48 2010
Log: [Isolates] API implementation (except Lockers) and tests.
http://code.google.com/p/v8/source/detail?r=5170

Modified:
 /branches/experimental/isolates/include/v8.h
 /branches/experimental/isolates/src/api.cc
 /branches/experimental/isolates/src/isolate.cc
 /branches/experimental/isolates/src/isolate.h
 /branches/experimental/isolates/src/v8.cc
 /branches/experimental/isolates/src/v8threads.cc
 /branches/experimental/isolates/src/v8threads.h
 /branches/experimental/isolates/test/cctest/cctest.cc
 /branches/experimental/isolates/test/cctest/test-api.cc

=======================================
--- /branches/experimental/isolates/include/v8.h        Thu Jul 15 20:09:25 2010
+++ /branches/experimental/isolates/include/v8.h        Mon Aug  2 17:41:48 2010
@@ -2449,6 +2449,7 @@
    private:
     Isolate* const isolate_;

+    // Prevent copying of Scope objects.
     Scope(const Scope&);
     Scope& operator=(const Scope&);
   };
@@ -3120,15 +3121,26 @@

 /**
  * Multiple threads in V8 are allowed, but only one thread at a time
- * is allowed to use V8.  The definition of 'using V8' includes
- * accessing handles or holding onto object pointers obtained from V8
- * handles.  It is up to the user of V8 to ensure (perhaps with
- * locking) that this constraint is not violated.
+ * is allowed to use any given V8 isolate. See Isolate class
+ * comments. The definition of 'using V8 isolate' includes
+ * accessing handles or holding onto object pointers obtained
+ * from V8 handles while in the particular V8 isolate.  It is up
+ * to the user of V8 to ensure (perhaps with locking) that this
+ * constraint is not violated.
  *
- * If you wish to start using V8 in a thread you can do this by constructing
- * a v8::Locker object.  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:
+ * 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.
+ *
+ * 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
  * ...
=======================================
--- /branches/experimental/isolates/src/api.cc  Tue Jul 20 11:14:13 2010
+++ /branches/experimental/isolates/src/api.cc  Mon Aug  2 17:41:48 2010
@@ -250,14 +250,29 @@
 // --- S t a t i c s ---


+static bool InitializeHelper() {
+  if (i::Snapshot::Initialize()) return true;
+  return i::V8::Initialize(NULL);
+}
+
+
 static inline bool EnsureInitialized(const char* location) {
-  if (i::V8::IsRunning()) {
-    return true;
-  }
-  if (IsDeadCheck(location)) {
-    return false;
-  }
-  return ApiCheck(v8::V8::Initialize(), location, "Error initializing V8");
+  i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+  if (isolate != NULL) {
+    if (isolate->IsDefaultIsolate()) {
+      if (i::V8::IsRunning()) {
+        return true;
+      }
+      if (IsDeadCheck(location)) {
+        return false;
+      }
+    } else {
+      if (isolate->IsInitialized()) {
+        return true;
+      }
+    }
+  }
+  return ApiCheck(InitializeHelper(), location, "Error initializing V8");
 }


@@ -3101,20 +3116,24 @@

 // --- E n v i r o n m e n t ---

+
 bool v8::V8::Initialize() {
-  if (i::V8::IsRunning()) return true;
-  ENTER_V8;
-  if (i::Isolate::CurrentPerIsolateThreadData() == NULL) {
- // We might be calling Initialize on a thread other than the thread that
-    // we ran the initial static initializers on.
-    i::Isolate::EnterDefaultIsolate();
-  }
-  if (i::Snapshot::Initialize()) return true;
-  return i::V8::Initialize(NULL);
+  i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+ if (isolate != NULL && isolate->IsDefaultIsolate() && i::V8::IsRunning()) {
+    return true;
+  }
+  ENTER_V8;
+  return InitializeHelper();
 }


 bool v8::V8::Dispose() {
+  i::Isolate* isolate = i::Isolate::Current();
+  if (!ApiCheck(isolate != NULL && isolate->IsDefaultIsolate(),
+                "v8::V8::Dispose()",
+                "Use v8::Isolate::Dispose() for a non-default isolate.")) {
+    return false;
+  }
   i::V8::TearDown();
   return true;
 }
@@ -3953,33 +3972,41 @@
 }


+Isolate* Isolate::GetCurrent() {
+  i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+  return reinterpret_cast<Isolate*>(isolate);
+}
+
+
 Isolate* Isolate::New() {
-  UNIMPLEMENTED();
-  return NULL;
-}
-
-
-Isolate* Isolate::GetCurrent() {
-  UNIMPLEMENTED();
-  return NULL;
-}
-
-
+  i::Isolate* isolate = new i::Isolate();
+  return reinterpret_cast<Isolate*>(isolate);
+}
+
+
+void Isolate::Dispose() {
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+  if (!ApiCheck(!isolate->IsInUse(),
+                "v8::Isolate::Dispose()",
+                "Disposing the isolate that is entered by a thread.")) {
+    return;
+  }
+  isolate->TearDown();
+}
+
+
 void Isolate::Enter() {
-  UNIMPLEMENTED();
-}
-
-
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+  isolate->Enter();
+}
+
+
 void Isolate::Exit() {
-  UNIMPLEMENTED();
-}
-
-
-void Isolate::Dispose() {
-  UNIMPLEMENTED();
-}
-
-
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+  isolate->Exit();
+}
+
+
 String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj) {
   EnsureInitialized("v8::String::Utf8Value::Utf8Value()");
   if (obj.IsEmpty()) {
=======================================
--- /branches/experimental/isolates/src/isolate.cc      Fri Jul 16 10:48:57 2010
+++ /branches/experimental/isolates/src/isolate.cc      Mon Aug  2 17:41:48 2010
@@ -168,6 +168,9 @@
 Thread::LocalStorageKey Isolate::isolate_key_;
 Thread::LocalStorageKey Isolate::thread_id_key_;
 Thread::LocalStorageKey Isolate::per_isolate_thread_data_key_;
+#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__)
+Thread::LocalStorageKey Isolate::simulator_key_;
+#endif
 Mutex* Isolate::process_wide_mutex_ = NULL;
 Isolate::ThreadDataTable* Isolate::thread_data_table_ = NULL;
 Isolate::ThreadId Isolate::highest_thread_id_ = 0;
@@ -175,23 +178,18 @@
 class IsolateInitializer {
  public:
   IsolateInitializer() {
-    Isolate::isolate_key_ = Thread::CreateThreadLocalKey();
-    Isolate::thread_id_key_ = Thread::CreateThreadLocalKey();
-    Isolate::per_isolate_thread_data_key_ = Thread::CreateThreadLocalKey();
-    Isolate::process_wide_mutex_ = OS::CreateMutex();
-    Isolate::thread_data_table_ = new Isolate::ThreadDataTable();
-    Isolate::EnterDefaultIsolate();
+    Isolate::EnsureDefaultIsolate();
   }
 };

-bool Isolate::EnsureDefaultIsolateAllocated() {
+static IsolateInitializer* EnsureDefaultIsolateAllocated() {
   // TODO(isolates): Use the system threading API to do this once?
   static IsolateInitializer static_initializer;
-  USE(static_initializer);
-  return true;
+  return &static_initializer;
 }

-static bool static_initialized = Isolate::EnsureDefaultIsolateAllocated();
+// This variable only needed to trigger static intialization.
+static IsolateInitializer* static_initializer = EnsureDefaultIsolateAllocated();


 Isolate::ThreadId Isolate::AllocateThreadId() {
@@ -236,44 +234,44 @@
   }
   return per_thread;
 }
-
-
-bool Isolate::InitializeDefaultIsolate(Deserializer* des) {
-  ASSERT(default_isolate_ != NULL);
-  ASSERT(default_isolate_->state_ != INITIALIZED);
-  CHECK(default_isolate_->Init(des));
-  return true;
-}


 void Isolate::EnsureDefaultIsolate() {
+  // Assume there is only one static-initializing thread.
+  if (process_wide_mutex_ == NULL) {
+    process_wide_mutex_ = OS::CreateMutex();
+  }
+
   ScopedLock lock(process_wide_mutex_);
   if (default_isolate_ == NULL) {
-    USE(static_initialized);
-    Isolate* new_default_isolate = new Isolate();
-    CHECK(new_default_isolate->PreInit());
-    default_isolate_ = new_default_isolate;
-  }
+    isolate_key_ = Thread::CreateThreadLocalKey();
+    thread_id_key_ = Thread::CreateThreadLocalKey();
+    per_isolate_thread_data_key_ = Thread::CreateThreadLocalKey();
+#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__)
+    simulator_key_ = Thread::CreateThreadLocalKey();
+#endif
+    thread_data_table_ = new Isolate::ThreadDataTable();
+    default_isolate_ = new Isolate();
+  }
+  Thread::SetThreadLocal(isolate_key_, default_isolate_);
+  CHECK(default_isolate_->PreInit());
 }


-Isolate* Isolate::EnterDefaultIsolate() {
-  // TODO(isolates): Check that we're not in multiple-isolate mode.
+void Isolate::EnterDefaultIsolate() {
   EnsureDefaultIsolate();
-
   ASSERT(default_isolate_ != NULL);
-  PerIsolateThreadData* per_thread = CurrentPerIsolateThreadData();
-  if (per_thread != NULL) {
-    ASSERT(per_thread->isolate_ == default_isolate_);
-    ASSERT(Current() != NULL);
-    return default_isolate_;
-  }
-  PerIsolateThreadData* new_per_thread =
-      default_isolate_->FindOrAllocatePerThreadDataForThisThread();
-  ASSERT(new_per_thread != NULL);
-  ASSERT(new_per_thread->isolate_ == default_isolate_);
-  Thread::SetThreadLocal(per_isolate_thread_data_key_, new_per_thread);
-  Thread::SetThreadLocal(isolate_key_, default_isolate_);
+
+  PerIsolateThreadData* data = CurrentPerIsolateThreadData();
+  // If not yet in default isolate - enter it.
+  if (data == NULL || data->isolate() != default_isolate_) {
+    default_isolate_->Enter();
+  }
+}
+
+
+Isolate* Isolate::GetDefaultIsolateForLocking() {
+  EnsureDefaultIsolate();
   return default_isolate_;
 }

@@ -316,6 +314,7 @@

 Isolate::Isolate()
     : state_(UNINITIALIZED),
+      entry_stack_(NULL),
       stack_trace_nesting_level_(0),
       incomplete_message_(NULL),
       preallocated_memory_thread_(NULL),
@@ -363,7 +362,6 @@
 #if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__)
   simulator_initialized_ = false;
   simulator_i_cache_ = NULL;
-  simulator_key_ = Thread::CreateThreadLocalKey();
   simulator_redirection_ = NULL;
 #endif

@@ -397,22 +395,15 @@
 }

 void Isolate::TearDown() {
-  if (default_isolate_ != NULL) {
-    delete default_isolate_;
-    default_isolate_ = NULL;
+  Deinit();
+
+  if (!IsDefaultIsolate()) {
+    delete this;
   }
 }

-// TODO(isolates): This is unlikely to be correct.
-void Isolate::TearDownAndRecreateGlobalIsolate() {
-  TearDown();
-
-  default_isolate_ = new Isolate();
-  default_isolate_->PreInit();
-}
-
-
-Isolate::~Isolate() {
+
+void Isolate::Deinit() {
   if (state_ == INITIALIZED) {
     OProfileAgent::TearDown();
     if (FLAG_preemption) {
@@ -431,8 +422,13 @@
     CpuProfiler::TearDown();
     heap_.TearDown();
     logger_->TearDown();
-  }
-
+
+    // The default isolate is re-initializable due to legacy API.
+    state_ = PREINITIALIZED;
+  }
+}
+
+Isolate::~Isolate() {
 #ifdef ENABLE_LOGGING_AND_PROFILING
   delete producer_heap_profile_;
   producer_heap_profile_ = NULL;
@@ -500,27 +496,13 @@
   delete debug_;
   debug_ = NULL;
 #endif
-
-#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__)
-  Thread::DeleteThreadLocalKey(simulator_key_);
-#endif
 }


 bool Isolate::PreInit() {
   if (state_ != UNINITIALIZED) return true;
-  ASSERT(default_isolate_ == NULL);
-
-  // TODO(isolates): Use the entry/exit stack properly here.
-  PerIsolateThreadData* per_thread = CurrentPerIsolateThreadData();
-  // This thread has no business being in an isolate if the global isolate
-  // hasn't yet been initialized!
-  ASSERT_EQ(NULL, per_thread);
-  per_thread = FindOrAllocatePerThreadDataForThisThread();
-  ASSERT(per_thread != NULL);
-  ASSERT(per_thread->isolate_ == this);
-  Thread::SetThreadLocal(per_isolate_thread_data_key_, per_thread);
-  Thread::SetThreadLocal(isolate_key_, this);
+
+  ASSERT(Isolate::Current() == this);

 #ifdef ENABLE_DEBUGGER_SUPPORT
   debug_ = new Debug(this);
@@ -592,7 +574,6 @@


 bool Isolate::Init(Deserializer* des) {
-  ASSERT(default_isolate_ == this);
   ASSERT(state_ != INITIALIZED);

   bool create_heap_objects = des == NULL;
@@ -689,4 +670,76 @@
 }


+void Isolate::Enter() {
+  Isolate* current_isolate = NULL;
+  PerIsolateThreadData* current_data = CurrentPerIsolateThreadData();
+  if (current_data != NULL) {
+    current_isolate = current_data->isolate_;
+    ASSERT(current_isolate != NULL);
+    if (current_isolate == this) {
+      ASSERT(Current() == this);
+      ASSERT(entry_stack_ != NULL);
+      ASSERT(entry_stack_->previous_thread_data == NULL ||
+             entry_stack_->previous_thread_data->thread_id() ==
+                 Thread::GetThreadLocalInt(thread_id_key_));
+      // Same thread re-enters the isolate, no need to re-init anything.
+      entry_stack_->entry_count++;
+      return;
+    }
+  }
+
+ // Threads can have default isolate set into TLS as Current but not yet have
+  // PerIsolateThreadData for it, as it requires more advanced phase of the
+ // initialization. For example, a thread might be the one that system used for + // static initializers - in this case the default isolate is set in TLS but + // the thread did not yet Enter the isolate. If PerisolateThreadData is not
+  // there, use the isolate set in TLS.
+  if (current_isolate == NULL) {
+    current_isolate = Isolate::UncheckedCurrent();
+  }
+
+  PerIsolateThreadData* data = FindOrAllocatePerThreadDataForThisThread();
+  ASSERT(data != NULL);
+  ASSERT(data->isolate_ == this);
+
+  EntryStackItem* item = new EntryStackItem(current_data,
+                                            current_isolate,
+                                            entry_stack_);
+  entry_stack_ = item;
+
+  Thread::SetThreadLocal(per_isolate_thread_data_key_, data);
+  Thread::SetThreadLocal(isolate_key_, this);
+
+  CHECK(PreInit());
+
+  // In case it's the first time some thread enters the isolate.
+  set_thread_id(data->thread_id());
+}
+
+
+void Isolate::Exit() {
+  ASSERT(entry_stack_ != NULL);
+  ASSERT(entry_stack_->previous_thread_data == NULL ||
+         entry_stack_->previous_thread_data->thread_id() ==
+             Thread::GetThreadLocalInt(thread_id_key_));
+
+  if (--entry_stack_->entry_count > 0) return;
+
+  ASSERT(CurrentPerIsolateThreadData() != NULL);
+  ASSERT(CurrentPerIsolateThreadData()->isolate_ == this);
+
+  // Pop the stack.
+  EntryStackItem* item = entry_stack_;
+  entry_stack_ = item->previous_item;
+
+  PerIsolateThreadData* previous_thread_data = item->previous_thread_data;
+  Isolate* previous_isolate = item->previous_isolate;
+
+  delete item;
+
+ // Reinit the current thread for the isolate it was running before this one. + Thread::SetThreadLocal(per_isolate_thread_data_key_, previous_thread_data);
+  Thread::SetThreadLocal(isolate_key_, previous_isolate);
+}
+
 } }  // namespace v8::internal
=======================================
--- /branches/experimental/isolates/src/isolate.h       Fri Jul 16 10:48:57 2010
+++ /branches/experimental/isolates/src/isolate.h       Mon Aug  2 17:41:48 2010
@@ -333,10 +333,12 @@

     friend class Isolate;
     friend class ThreadDataTable;
+    friend class EntryStackItem;

     DISALLOW_COPY_AND_ASSIGN(PerIsolateThreadData);
   };

+
   enum AddressId {
 #define C(name) k_##name,
     ISOLATE_ADDRESS_LIST(C)
@@ -364,24 +366,25 @@
return reinterpret_cast<Isolate*>(Thread::GetThreadLocal(isolate_key_));
   }

-  // Destroy the global isolate.
-  static void TearDown();
-
-  // Destroy the global isolate and create a new one in its place.
-  static void TearDownAndRecreateGlobalIsolate();
-
- // Initializes the default isolate (perhaps using a deserializer). Returns
-  // false on failure.
-  static bool InitializeDefaultIsolate(Deserializer* des);
-
-  static Isolate* default_isolate() { return default_isolate_; }
+  bool Init(Deserializer* des);
+
+  bool IsInitialized() { return state_ == INITIALIZED; }
+
+  // True if at least one thread Enter'ed this isolate.
+  bool IsInUse() { return entry_stack_ != NULL; }
+
+  // Destroys the non-default isolates.
+ // Sets default isolate into "has_been_disposed" state rather then destroying,
+  // for legacy API reasons.
+  void TearDown();

   bool IsDefaultIsolate() const { return this == default_isolate_; }

-  // Ensures that process-wide resources have been allocated. It is only
-  // necessary to call this method if you are using V8 from within the body
-  // of a static initializer.
-  static bool EnsureDefaultIsolateAllocated();
+  // Ensures that process-wide resources and the default isolate have been
+ // allocated. It is only necessary to call this method in rare casses, for + // example if you are using V8 from within the body of a static initializer.
+  // Safe to call multiple times.
+  static void EnsureDefaultIsolate();

   // Returns the key used to store the pointer to the current isolate.
// Used internally for V8 threads that do not execute JavaScript but still
@@ -401,9 +404,8 @@
   // If a client attempts to create a Locker without specifying an isolate,
   // we assume that the client is using legacy behavior. Set up the current
   // thread to be inside the implicit isolate (or fail a check if we have
-  // switched to non-legacy behavior). Returns a pointer to the default
-  // isolate.
-  static Isolate* EnterDefaultIsolate();
+  // switched to non-legacy behavior).
+  static void EnterDefaultIsolate();

   // Debug.
   // Mutex for serializing access to break control structures.
@@ -789,7 +791,7 @@
 #endif

 #if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__)
-  v8::internal::Thread::LocalStorageKey* simulator_key() {
+  static v8::internal::Thread::LocalStorageKey* simulator_key() {
     return &simulator_key_;
   }

@@ -839,6 +841,30 @@
     PerIsolateThreadData* list_;
   };

+ // These items form a stack synchronously with threads Enter'ing and Exit'ing + // the Isolate. The top of the stack points to a thread which is currently + // running the Isolate. When the stack is empty, the Isolate is considered
+  // not entered by any thread and can be Disposed.
+  // If the same thread enters the Isolate more then once, the entry_count_
+  // is incremented rather then a new item pushed to the stack.
+  struct EntryStackItem {
+   public:
+    EntryStackItem(PerIsolateThreadData* previous_thread_data,
+                   Isolate* previous_isolate,
+                   EntryStackItem* previous_item)
+        : entry_count(1),
+          previous_thread_data(previous_thread_data),
+          previous_isolate(previous_isolate),
+          previous_item(previous_item) { }
+
+    int entry_count;
+    PerIsolateThreadData* previous_thread_data;
+    Isolate* previous_isolate;
+    EntryStackItem* previous_item;
+
+    DISALLOW_COPY_AND_ASSIGN(EntryStackItem);
+  };
+
   // This mutex protects highest_thread_id_, thread_data_table_ and
   // default_isolate_.
   static Mutex* process_wide_mutex_;
@@ -852,7 +878,7 @@

   bool PreInit();

-  bool Init(Deserializer* des);
+  void Deinit();

   enum State {
     UNINITIALIZED,    // Some components may not have been allocated.
@@ -861,6 +887,7 @@
   };

   State state_;
+  EntryStackItem* entry_stack_;

   // Allocate and insert PerIsolateThreadData into the ThreadDataTable
   // (regardless of whether such data already exists).
@@ -870,8 +897,20 @@
   // If one does not yet exist, allocate a new one.
   PerIsolateThreadData* FindOrAllocatePerThreadDataForThisThread();

-  // Ensures that the default isolate exists. Safe to call multiple times.
-  static void EnsureDefaultIsolate();
+  // 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();
+
+  // Initializes the current thread to run this Isolate.
+ // Not thread-safe. Multiple threads should not Enter/Exit the same isolate
+  // at the same time, this should be prevented using external locking.
+  void Enter();
+
+  // Exits the current thread. The previosuly entered Isolate is restored
+  // for the thread.
+ // Not thread-safe. Multiple threads should not Enter/Exit the same isolate
+  // at the same time, this should be prevented using external locking.
+  void Exit();

   void PreallocatedMemoryThreadStart();
   void PreallocatedMemoryThreadStop();
@@ -946,7 +985,7 @@

 #if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__)
   // Create one simulator per thread and keep it in thread local storage.
-  v8::internal::Thread::LocalStorageKey simulator_key_;
+  static v8::internal::Thread::LocalStorageKey simulator_key_;
   bool simulator_initialized_;
   HashMap* simulator_i_cache_;
   assembler::arm::Redirection* simulator_redirection_;
@@ -979,6 +1018,8 @@
 #undef GLOBAL_ARRAY_BACKING_STORE

   friend class IsolateInitializer;
+  friend class v8::Isolate;
+  friend class v8::Locker;

   DISALLOW_COPY_AND_ASSIGN(Isolate);
 };
=======================================
--- /branches/experimental/isolates/src/v8.cc   Fri Jul 16 10:48:57 2010
+++ /branches/experimental/isolates/src/v8.cc   Mon Aug  2 17:41:48 2010
@@ -46,15 +46,34 @@
 bool V8::has_fatal_error_ = false;

 bool V8::Initialize(Deserializer* des) {
-  if (has_been_disposed_ || has_fatal_error_) return false;
-  if (IsRunning()) return true;
-
-  is_running_ = true;
-  has_been_setup_ = true;
-  has_fatal_error_ = false;
-  has_been_disposed_ = false;
-
-  return (Isolate::InitializeDefaultIsolate(des) != false);
+  // The current thread may not yet had entered an isolate to run.
+  // Note the Isolate::Current() may be non-null because for various
+ // initialization purposes an initializing thread may be assigned an isolate
+  // but not actually enter it.
+  if (i::Isolate::CurrentPerIsolateThreadData() == NULL) {
+    i::Isolate::EnterDefaultIsolate();
+  }
+
+  ASSERT(i::Isolate::CurrentPerIsolateThreadData() != NULL);
+  ASSERT(i::Isolate::CurrentPerIsolateThreadData()->thread_id() ==
+         i::Thread::GetThreadLocalInt(i::Isolate::thread_id_key()));
+  ASSERT(i::Isolate::CurrentPerIsolateThreadData()->isolate() ==
+         i::Isolate::Current());
+
+  Isolate* isolate = Isolate::Current();
+  if (isolate->IsDefaultIsolate()) {
+    if (has_been_disposed_ || has_fatal_error_) return false;
+    if (IsRunning()) return true;
+
+    is_running_ = true;
+    has_been_setup_ = true;
+    has_fatal_error_ = false;
+    has_been_disposed_ = false;
+  } else {
+    if (isolate->IsInitialized()) return true;
+  }
+
+  return isolate->Init(des);
 }


@@ -65,9 +84,11 @@


 void V8::TearDown() {
-  if (!has_been_setup_ || has_been_disposed_) return;
-
-  Isolate::TearDown();
+  Isolate* isolate = Isolate::Current();
+  ASSERT(isolate->IsDefaultIsolate());
+
+  if (!has_been_setup_ || has_been_disposed_) return;
+  isolate->TearDown();

   is_running_ = false;
   has_been_disposed_ = true;
=======================================
--- /branches/experimental/isolates/src/v8threads.cc Tue Jul 20 11:14:13 2010 +++ /branches/experimental/isolates/src/v8threads.cc Mon Aug 2 17:41:48 2010
@@ -45,14 +45,32 @@
 // 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) {
-  internal::Isolate* isolate =
-      internal::Isolate::EnterDefaultIsolate();
+ // 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);
+
   // 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();
     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().
@@ -70,9 +88,6 @@
     }
   }
   ASSERT(isolate->thread_manager()->IsLockedByCurrentThread());
-
-  // Make sure this thread is assigned a thread id.
-  isolate->thread_manager()->AssignId();
 }


@@ -379,20 +394,6 @@
 int ThreadManager::CurrentId() {
   return Thread::GetThreadLocalInt(Isolate::thread_id_key());
 }
-
-
-void ThreadManager::AssignId() {
-  if (!HasId()) {
-    int new_id = Isolate::AllocateThreadId();
-    ASSERT(new_id > 0);
-    Thread::SetThreadLocalInt(Isolate::thread_id_key(), new_id);
-  }
-}
-
-
-bool ThreadManager::HasId() {
-  return Thread::HasThreadLocal(Isolate::thread_id_key());
-}


 void ThreadManager::TerminateExecution(int thread_id) {
=======================================
--- /branches/experimental/isolates/src/v8threads.h     Tue Jul 20 11:14:13 2010
+++ /branches/experimental/isolates/src/v8threads.h     Mon Aug  2 17:41:48 2010
@@ -102,8 +102,6 @@
   bool IsLockedByCurrentThread() { return mutex_owner_.IsSelf(); }

   int CurrentId();
-  void AssignId();
-  bool HasId();

   void TerminateExecution(int thread_id);

=======================================
--- /branches/experimental/isolates/test/cctest/cctest.cc Fri Jul 16 10:48:57 2010 +++ /branches/experimental/isolates/test/cctest/cctest.cc Mon Aug 2 17:41:48 2010
@@ -38,7 +38,7 @@
: callback_(callback), name_(name), dependency_(dependency), prev_(last_) {
   // Make sure that the global isolate has had a chance to complete its
   // static initialization. (We need this so that things like StrDup work.)
-  v8::internal::Isolate::EnsureDefaultIsolateAllocated();
+  v8::internal::Isolate::EnsureDefaultIsolate();
   CHECK(v8::internal::Isolate::Current() != NULL);

   // Find the base name of this test (const_cast required on Windows).
=======================================
--- /branches/experimental/isolates/test/cctest/test-api.cc Fri Jul 16 10:48:57 2010 +++ /branches/experimental/isolates/test/cctest/test-api.cc Mon Aug 2 17:41:48 2010
@@ -11187,3 +11187,248 @@
   reresult = CompileRun("str2.charCodeAt(2);");
   CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
 }
+
+TEST(DefaultIsolateGetCurrent) {
+  CHECK(v8::Isolate::GetCurrent() != NULL);
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
+  printf("*** %s\n", "DefaultIsolateGetCurrent success");
+}
+
+TEST(IsolateNewDispose) {
+  v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
+  v8::Isolate* isolate = v8::Isolate::New();
+  CHECK(isolate != NULL);
+  CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
+  CHECK(current_isolate != isolate);
+  CHECK(current_isolate == v8::Isolate::GetCurrent());
+
+  v8::V8::SetFatalErrorHandler(StoringErrorCallback);
+  last_location = last_message = NULL;
+  isolate->Dispose();
+  CHECK_EQ(last_location, NULL);
+  CHECK_EQ(last_message, NULL);
+}
+
+TEST(IsolateEnterExitDefault) {
+  v8::HandleScope scope;
+  LocalContext context;
+  v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
+  CHECK(current_isolate != NULL); // Default isolate.
+  ExpectString("'hello'", "hello");
+  current_isolate->Enter();
+  ExpectString("'still working'", "still working");
+  current_isolate->Exit();
+  ExpectString("'still working 2'", "still working 2");
+  current_isolate->Exit();
+  // Default isolate is always, well, 'default current'.
+  CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate);
+  // Still working since default isolate is auto-entering any thread
+  // that has no isolate and attempts to execute V8 APIs.
+  ExpectString("'still working 3'", "still working 3");
+}
+
+TEST(DisposeDefaultIsolate) {
+  v8::V8::SetFatalErrorHandler(StoringErrorCallback);
+
+  // Run some V8 code to trigger default isolate to become 'current'.
+  v8::HandleScope scope;
+  LocalContext context;
+  ExpectString("'run some V8'", "run some V8");
+
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
+  last_location = last_message = NULL;
+  isolate->Dispose();
+  // It is not possible to dispose default isolate via Isolate API.
+  CHECK_NE(last_location, NULL);
+  CHECK_NE(last_message, NULL);
+}
+
+TEST(RunDefaultAndAnotherIsolate) {
+  v8::HandleScope scope;
+  LocalContext context;
+
+  // Enter new isolate.
+  v8::Isolate* isolate = v8::Isolate::New();
+  CHECK(isolate);
+  isolate->Enter();
+  v8::HandleScope scope_new;
+  LocalContext context_new;
+
+  // Run something in new isolate.
+  CompileRun("var foo = 153;");
+  ExpectTrue("function f() { return foo == 153; }; f()");
+  isolate->Exit();
+
+  // This runs automatically in default isolate.
+  // Variables in another isolate should be not available.
+  ExpectTrue("function f() {"
+             "  try {"
+             "    foo;"
+             "    return false;"
+             "  } catch(e) {"
+             "    return true;"
+             "  }"
+             "};"
+             "var bar = 371;"
+             "f()");
+
+  v8::V8::SetFatalErrorHandler(StoringErrorCallback);
+  last_location = last_message = NULL;
+  isolate->Dispose();
+  CHECK_EQ(last_location, NULL);
+  CHECK_EQ(last_message, NULL);
+
+  // Check that default isolate still runs.
+  ExpectTrue("function f() { return bar == 371; }; f()");
+}
+
+TEST(DisposeIsolateWhenInUse) {
+  v8::Isolate* isolate = v8::Isolate::New();
+  CHECK(isolate);
+  isolate->Enter();
+  v8::HandleScope scope;
+  LocalContext context;
+  // Run something in this isolate.
+  ExpectTrue("true");
+  v8::V8::SetFatalErrorHandler(StoringErrorCallback);
+  last_location = last_message = NULL;
+  // Still entered, should fail.
+  isolate->Dispose();
+  CHECK_NE(last_location, NULL);
+  CHECK_NE(last_message, NULL);
+}
+
+TEST(RunTwoIsolatesOnSingleThread) {
+  // Run isolate 1.
+  v8::Isolate* isolate1 = v8::Isolate::New();
+  isolate1->Enter();
+  v8::HandleScope scope1;
+  LocalContext context1;
+
+  // Run something in new isolate.
+  CompileRun("var foo = 'isolate 1';");
+  ExpectString("function f() { return foo; }; f()", "isolate 1");
+
+  // Run isolate 2.
+  v8::Isolate* isolate2 = v8::Isolate::New();
+  isolate2->Enter();
+  v8::HandleScope scope2;
+  LocalContext context2;
+
+  // Run something in new isolate.
+  CompileRun("var foo = 'isolate 2';");
+  ExpectString("function f() { return foo; }; f()", "isolate 2");
+
+  isolate2->Exit();
+
+  // Now again in isolate 1
+
+  ExpectString("function f() { return foo; }; f()", "isolate 1");
+
+  isolate1->Exit();
+
+  // Run some stuff in default isolate.
+  v8::HandleScope scope_default;
+  LocalContext context_default;
+
+  // Variables in other isolates should be not available, verify there
+  // is an exception.
+  ExpectTrue("function f() {"
+             "  try {"
+             "    foo;"
+             "    return false;"
+             "  } catch(e) {"
+             "    return true;"
+             "  }"
+             "};"
+             "var isDefaultIsolate = true;"
+             "f()");
+
+  isolate1->Enter();
+
+  {
+    v8::Isolate::Scope isolate_scope(isolate2);
+    ExpectString("function f() { return foo; }; f()", "isolate 2");
+  }
+
+  ExpectString("function f() { return foo; }; f()", "isolate 1");
+  isolate1->Exit();
+
+  v8::V8::SetFatalErrorHandler(StoringErrorCallback);
+  last_location = last_message = NULL;
+
+  isolate1->Dispose();
+  CHECK_EQ(last_location, NULL);
+  CHECK_EQ(last_message, NULL);
+
+  isolate2->Dispose();
+  CHECK_EQ(last_location, NULL);
+  CHECK_EQ(last_message, NULL);
+
+  // Check that default isolate still runs.
+  ExpectTrue("function f() { return isDefaultIsolate; }; f()");
+}
+
+static int CalcFibonacci(v8::Isolate* isolate, int limit) {
+  v8::Isolate::Scope isolate_scope(isolate);
+  v8::HandleScope scope;
+  LocalContext context;
+  i::ScopedVector<char> code(1024);
+  i::OS::SNPrintF(code, "function fib(n) {"
+                        "  if (n <= 2) return 1;"
+                        "  return fib(n-1) + fib(n-2);"
+                        "}"
+                        "fib(%d)", limit);
+  Local<Value> value = CompileRun(code.start());
+  CHECK(value->IsNumber());
+  return static_cast<int>(value->NumberValue());
+}
+
+class IsolateThread : public v8::internal::Thread {
+ public:
+  explicit IsolateThread(v8::Isolate* isolate, int fib_limit)
+    : Thread(NULL),
+      isolate_(isolate),
+      fib_limit_(fib_limit),
+      result_(0) { }
+
+  void Run() {
+    result_ = CalcFibonacci(isolate_, fib_limit_);
+  }
+
+  int result() { return result_; }
+
+ private:
+  v8::Isolate* isolate_;
+  int fib_limit_;
+  int result_;
+};
+
+TEST(MultipleIsolatesOnIndividualThreads) {
+  v8::Isolate* isolate1 = v8::Isolate::New();
+  v8::Isolate* isolate2 = v8::Isolate::New();
+
+  IsolateThread thread1(isolate1, 21);
+  IsolateThread thread2(isolate2, 12);
+
+  // Compute some fibonacci numbers on 3 threads in 3 isolates.
+  thread1.Start();
+  thread2.Start();
+
+  int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21);
+  int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12);
+
+  thread1.Join();
+  thread2.Join();
+
+  // Compare results.
+  CHECK(result1 == thread1.result());
+  CHECK(result2 == thread2.result());
+
+  isolate1->Dispose();
+  isolate2->Dispose();
+}
+
+

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to