|  | // Copyright 2012 the V8 project authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/execution/v8threads.h" | 
|  |  | 
|  | #include "src/api/api.h" | 
|  | #include "src/debug/debug.h" | 
|  | #include "src/execution/execution.h" | 
|  | #include "src/execution/isolate-inl.h" | 
|  | #include "src/init/bootstrapper.h" | 
|  | #include "src/objects/visitors.h" | 
|  | #include "src/regexp/regexp-stack.h" | 
|  |  | 
|  | namespace v8 { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Track whether this V8 instance has ever called v8::Locker. This allows the | 
|  | // API code to verify that the lock is always held when V8 is being entered. | 
|  | base::Atomic32 g_locker_was_ever_used_ = 0; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Once the Locker is initialized, the current thread will be guaranteed to have | 
|  | // the lock for a given isolate. | 
|  | void Locker::Initialize(v8::Isolate* isolate) { | 
|  | DCHECK_NOT_NULL(isolate); | 
|  | has_lock_ = false; | 
|  | top_level_ = true; | 
|  | isolate_ = reinterpret_cast<i::Isolate*>(isolate); | 
|  | // Record that the Locker has been used at least once. | 
|  | base::Relaxed_Store(&g_locker_was_ever_used_, 1); | 
|  | // Get the big lock if necessary. | 
|  | if (!isolate_->thread_manager()->IsLockedByCurrentThread()) { | 
|  | isolate_->thread_manager()->Lock(); | 
|  | has_lock_ = true; | 
|  |  | 
|  | // 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()) { | 
|  | top_level_ = false; | 
|  | } | 
|  | } | 
|  | DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread()); | 
|  | } | 
|  |  | 
|  | bool Locker::IsLocked(v8::Isolate* isolate) { | 
|  | DCHECK_NOT_NULL(isolate); | 
|  | i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate); | 
|  | return internal_isolate->thread_manager()->IsLockedByCurrentThread(); | 
|  | } | 
|  |  | 
|  | bool Locker::IsActive() { | 
|  | return !!base::Relaxed_Load(&g_locker_was_ever_used_); | 
|  | } | 
|  |  | 
|  | Locker::~Locker() { | 
|  | DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread()); | 
|  | if (has_lock_) { | 
|  | if (top_level_) { | 
|  | isolate_->thread_manager()->FreeThreadResources(); | 
|  | } else { | 
|  | isolate_->thread_manager()->ArchiveThread(); | 
|  | } | 
|  | isolate_->thread_manager()->Unlock(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Unlocker::Initialize(v8::Isolate* isolate) { | 
|  | DCHECK_NOT_NULL(isolate); | 
|  | isolate_ = reinterpret_cast<i::Isolate*>(isolate); | 
|  | DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread()); | 
|  | isolate_->thread_manager()->ArchiveThread(); | 
|  | isolate_->thread_manager()->Unlock(); | 
|  | } | 
|  |  | 
|  | Unlocker::~Unlocker() { | 
|  | DCHECK(!isolate_->thread_manager()->IsLockedByCurrentThread()); | 
|  | isolate_->thread_manager()->Lock(); | 
|  | isolate_->thread_manager()->RestoreThread(); | 
|  | } | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | void ThreadManager::InitThread(const ExecutionAccess& lock) { | 
|  | isolate_->InitializeThreadLocal(); | 
|  | isolate_->stack_guard()->InitThread(lock); | 
|  | isolate_->debug()->InitThread(lock); | 
|  | } | 
|  |  | 
|  | bool ThreadManager::RestoreThread() { | 
|  | DCHECK(IsLockedByCurrentThread()); | 
|  | // First check whether the current thread has been 'lazily archived', i.e. | 
|  | // 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_ == ThreadId::Current()) { | 
|  | lazily_archived_thread_ = ThreadId::Invalid(); | 
|  | Isolate::PerIsolateThreadData* per_thread = | 
|  | isolate_->FindPerThreadDataForThisThread(); | 
|  | DCHECK_NOT_NULL(per_thread); | 
|  | DCHECK(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_ = nullptr; | 
|  | per_thread->set_thread_state(nullptr); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Make sure that the preemption thread cannot modify the thread state while | 
|  | // it is being archived or restored. | 
|  | ExecutionAccess access(isolate_); | 
|  |  | 
|  | // If there is another thread that was lazily archived then we have to really | 
|  | // archive it now. | 
|  | if (lazily_archived_thread_.IsValid()) { | 
|  | EagerlyArchiveThread(); | 
|  | } | 
|  | Isolate::PerIsolateThreadData* per_thread = | 
|  | isolate_->FindPerThreadDataForThisThread(); | 
|  | if (per_thread == nullptr || per_thread->thread_state() == nullptr) { | 
|  | // This is a new thread. | 
|  | InitThread(access); | 
|  | return false; | 
|  | } | 
|  | ThreadState* state = per_thread->thread_state(); | 
|  | char* from = state->data(); | 
|  | from = isolate_->handle_scope_implementer()->RestoreThread(from); | 
|  | from = isolate_->RestoreThread(from); | 
|  | from = Relocatable::RestoreState(isolate_, from); | 
|  | // Stack guard should be restored before Debug, etc. since Debug etc. might | 
|  | // depend on a correct stack guard. | 
|  | from = isolate_->stack_guard()->RestoreStackGuard(from); | 
|  | from = isolate_->debug()->RestoreDebug(from); | 
|  | from = isolate_->regexp_stack()->RestoreStack(from); | 
|  | from = isolate_->bootstrapper()->RestoreState(from); | 
|  | per_thread->set_thread_state(nullptr); | 
|  | state->set_id(ThreadId::Invalid()); | 
|  | state->Unlink(); | 
|  | state->LinkInto(ThreadState::FREE_LIST); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void ThreadManager::Lock() { | 
|  | mutex_.Lock(); | 
|  | mutex_owner_.store(ThreadId::Current(), std::memory_order_relaxed); | 
|  | DCHECK(IsLockedByCurrentThread()); | 
|  | } | 
|  |  | 
|  | void ThreadManager::Unlock() { | 
|  | mutex_owner_.store(ThreadId::Invalid(), std::memory_order_relaxed); | 
|  | mutex_.Unlock(); | 
|  | } | 
|  |  | 
|  | static int ArchiveSpacePerThread() { | 
|  | return HandleScopeImplementer::ArchiveSpacePerThread() + | 
|  | Isolate::ArchiveSpacePerThread() + Debug::ArchiveSpacePerThread() + | 
|  | StackGuard::ArchiveSpacePerThread() + | 
|  | RegExpStack::ArchiveSpacePerThread() + | 
|  | Bootstrapper::ArchiveSpacePerThread() + | 
|  | Relocatable::ArchiveSpacePerThread(); | 
|  | } | 
|  |  | 
|  | ThreadState::ThreadState(ThreadManager* thread_manager) | 
|  | : id_(ThreadId::Invalid()), | 
|  | data_(nullptr), | 
|  | next_(this), | 
|  | previous_(this), | 
|  | thread_manager_(thread_manager) {} | 
|  |  | 
|  | ThreadState::~ThreadState() { DeleteArray<char>(data_); } | 
|  |  | 
|  | void ThreadState::AllocateSpace() { | 
|  | data_ = NewArray<char>(ArchiveSpacePerThread()); | 
|  | } | 
|  |  | 
|  | void ThreadState::Unlink() { | 
|  | next_->previous_ = previous_; | 
|  | previous_->next_ = next_; | 
|  | } | 
|  |  | 
|  | void ThreadState::LinkInto(List list) { | 
|  | ThreadState* flying_anchor = list == FREE_LIST | 
|  | ? thread_manager_->free_anchor_ | 
|  | : thread_manager_->in_use_anchor_; | 
|  | next_ = flying_anchor->next_; | 
|  | previous_ = flying_anchor; | 
|  | flying_anchor->next_ = this; | 
|  | next_->previous_ = this; | 
|  | } | 
|  |  | 
|  | ThreadState* ThreadManager::GetFreeThreadState() { | 
|  | ThreadState* gotten = free_anchor_->next_; | 
|  | if (gotten == free_anchor_) { | 
|  | ThreadState* new_thread_state = new ThreadState(this); | 
|  | new_thread_state->AllocateSpace(); | 
|  | return new_thread_state; | 
|  | } | 
|  | return gotten; | 
|  | } | 
|  |  | 
|  | // Gets the first in the list of archived threads. | 
|  | ThreadState* ThreadManager::FirstThreadStateInUse() { | 
|  | return in_use_anchor_->Next(); | 
|  | } | 
|  |  | 
|  | ThreadState* ThreadState::Next() { | 
|  | if (next_ == thread_manager_->in_use_anchor_) return nullptr; | 
|  | return next_; | 
|  | } | 
|  |  | 
|  | // Thread ids must start with 1, because in TLS having thread id 0 can't | 
|  | // be distinguished from not having a thread id at all (since NULL is | 
|  | // defined as 0.) | 
|  | ThreadManager::ThreadManager(Isolate* isolate) | 
|  | : mutex_owner_(ThreadId::Invalid()), | 
|  | lazily_archived_thread_(ThreadId::Invalid()), | 
|  | lazily_archived_thread_state_(nullptr), | 
|  | free_anchor_(nullptr), | 
|  | in_use_anchor_(nullptr), | 
|  | isolate_(isolate) { | 
|  | free_anchor_ = new ThreadState(this); | 
|  | in_use_anchor_ = new ThreadState(this); | 
|  | } | 
|  |  | 
|  | ThreadManager::~ThreadManager() { | 
|  | DeleteThreadStateList(free_anchor_); | 
|  | DeleteThreadStateList(in_use_anchor_); | 
|  | } | 
|  |  | 
|  | void ThreadManager::DeleteThreadStateList(ThreadState* anchor) { | 
|  | // The list starts and ends with the anchor. | 
|  | for (ThreadState* current = anchor->next_; current != anchor;) { | 
|  | ThreadState* next = current->next_; | 
|  | delete current; | 
|  | current = next; | 
|  | } | 
|  | delete anchor; | 
|  | } | 
|  |  | 
|  | void ThreadManager::ArchiveThread() { | 
|  | DCHECK_EQ(lazily_archived_thread_, ThreadId::Invalid()); | 
|  | DCHECK(!IsArchived()); | 
|  | DCHECK(IsLockedByCurrentThread()); | 
|  | ThreadState* state = GetFreeThreadState(); | 
|  | state->Unlink(); | 
|  | Isolate::PerIsolateThreadData* per_thread = | 
|  | isolate_->FindOrAllocatePerThreadDataForThisThread(); | 
|  | per_thread->set_thread_state(state); | 
|  | lazily_archived_thread_ = ThreadId::Current(); | 
|  | lazily_archived_thread_state_ = state; | 
|  | DCHECK_EQ(state->id(), ThreadId::Invalid()); | 
|  | state->set_id(CurrentId()); | 
|  | DCHECK_NE(state->id(), ThreadId::Invalid()); | 
|  | } | 
|  |  | 
|  | void ThreadManager::EagerlyArchiveThread() { | 
|  | DCHECK(IsLockedByCurrentThread()); | 
|  | ThreadState* state = lazily_archived_thread_state_; | 
|  | state->LinkInto(ThreadState::IN_USE_LIST); | 
|  | char* to = state->data(); | 
|  | // Ensure that data containing GC roots are archived first, and handle them | 
|  | // in ThreadManager::Iterate(RootVisitor*). | 
|  | to = isolate_->handle_scope_implementer()->ArchiveThread(to); | 
|  | to = isolate_->ArchiveThread(to); | 
|  | to = Relocatable::ArchiveState(isolate_, to); | 
|  | to = isolate_->stack_guard()->ArchiveStackGuard(to); | 
|  | to = isolate_->debug()->ArchiveDebug(to); | 
|  | to = isolate_->regexp_stack()->ArchiveStack(to); | 
|  | to = isolate_->bootstrapper()->ArchiveState(to); | 
|  | lazily_archived_thread_ = ThreadId::Invalid(); | 
|  | lazily_archived_thread_state_ = nullptr; | 
|  | } | 
|  |  | 
|  | void ThreadManager::FreeThreadResources() { | 
|  | DCHECK(!isolate_->has_pending_exception()); | 
|  | DCHECK(!isolate_->external_caught_exception()); | 
|  | DCHECK_NULL(isolate_->try_catch_handler()); | 
|  | isolate_->handle_scope_implementer()->FreeThreadResources(); | 
|  | isolate_->FreeThreadResources(); | 
|  | isolate_->debug()->FreeThreadResources(); | 
|  | isolate_->stack_guard()->FreeThreadResources(); | 
|  | isolate_->regexp_stack()->FreeThreadResources(); | 
|  | isolate_->bootstrapper()->FreeThreadResources(); | 
|  | } | 
|  |  | 
|  | bool ThreadManager::IsArchived() { | 
|  | Isolate::PerIsolateThreadData* data = | 
|  | isolate_->FindPerThreadDataForThisThread(); | 
|  | return data != nullptr && data->thread_state() != nullptr; | 
|  | } | 
|  |  | 
|  | void ThreadManager::Iterate(RootVisitor* v) { | 
|  | // Expecting no threads during serialization/deserialization | 
|  | for (ThreadState* state = FirstThreadStateInUse(); state != nullptr; | 
|  | state = state->Next()) { | 
|  | char* data = state->data(); | 
|  | data = HandleScopeImplementer::Iterate(v, data); | 
|  | data = isolate_->Iterate(v, data); | 
|  | data = Relocatable::Iterate(v, data); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ThreadManager::IterateArchivedThreads(ThreadVisitor* v) { | 
|  | for (ThreadState* state = FirstThreadStateInUse(); state != nullptr; | 
|  | state = state->Next()) { | 
|  | char* data = state->data(); | 
|  | data += HandleScopeImplementer::ArchiveSpacePerThread(); | 
|  | isolate_->IterateThread(v, data); | 
|  | } | 
|  | } | 
|  |  | 
|  | ThreadId ThreadManager::CurrentId() { return ThreadId::Current(); } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |