v8/src/v8threads.cc
Clemens Hammacher 5f6510825a [cleanup] Fix remaining (D)CHECK macro usages
This CL fixes all occurences that don't require special OWNER reviews,
or can be reviewed by Michi.

After this one, we should be able to reenable the readability/check
cpplint check.

R=mstarzinger@chromium.org

Bug: v8:6837, v8:6921
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng;master.tryserver.v8:v8_linux_noi18n_rel_ng
Change-Id: Ic81d68d5534eaa795b7197fed5c41ed158361d62
Reviewed-on: https://chromium-review.googlesource.com/721120
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48670}
2017-10-18 10:12:31 +00:00

359 lines
11 KiB
C++

// 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/v8threads.h"
#include "src/api.h"
#include "src/bootstrapper.h"
#include "src/debug/debug.h"
#include "src/execution.h"
#include "src/isolate-inl.h"
#include "src/regexp/regexp-stack.h"
#include "src/visitors.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;
} else {
internal::ExecutionAccess access(isolate_);
isolate_->stack_guard()->ClearThread(access);
isolate_->stack_guard()->InitThread(access);
}
}
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 {
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_.Equals(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.
isolate_->stack_guard()->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);
from = isolate_->debug()->RestoreDebug(from);
from = isolate_->stack_guard()->RestoreStackGuard(from);
from = isolate_->regexp_stack()->RestoreStack(from);
from = isolate_->bootstrapper()->RestoreState(from);
per_thread->set_thread_state(nullptr);
if (state->terminate_on_restore()) {
isolate_->stack_guard()->RequestTerminateExecution();
state->set_terminate_on_restore(false);
}
state->set_id(ThreadId::Invalid());
state->Unlink();
state->LinkInto(ThreadState::FREE_LIST);
return true;
}
void ThreadManager::Lock() {
mutex_.Lock();
mutex_owner_ = ThreadId::Current();
DCHECK(IsLockedByCurrentThread());
}
void ThreadManager::Unlock() {
mutex_owner_ = ThreadId::Invalid();
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()),
terminate_on_restore_(false),
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()
: mutex_owner_(ThreadId::Invalid()),
lazily_archived_thread_(ThreadId::Invalid()),
lazily_archived_thread_state_(nullptr),
free_anchor_(nullptr),
in_use_anchor_(nullptr) {
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(lazily_archived_thread_.Equals(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(state->id().Equals(ThreadId::Invalid()));
state->set_id(CurrentId());
DCHECK(!state->id().Equals(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_->debug()->ArchiveDebug(to);
to = isolate_->stack_guard()->ArchiveStackGuard(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();
}
void ThreadManager::TerminateExecution(ThreadId thread_id) {
for (ThreadState* state = FirstThreadStateInUse(); state != nullptr;
state = state->Next()) {
if (thread_id.Equals(state->id())) {
state->set_terminate_on_restore(true);
}
}
}
} // namespace internal
} // namespace v8