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