blob: 5b2ed5ebbb4c1dcca78f0145aac32f0628769e43 [file] [log] [blame]
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/threading/thread_checker_impl.h"
#include "base/check.h"
#include "base/debug/stack_trace.h"
#include "base/sequence_token.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_local.h"
namespace {
bool g_log_stack = false;
}
namespace base {
// static
void ThreadCheckerImpl::EnableStackLogging() {
g_log_stack = true;
}
ThreadCheckerImpl::ThreadCheckerImpl() {
AutoLock auto_lock(lock_);
EnsureAssigned();
}
ThreadCheckerImpl::~ThreadCheckerImpl() = default;
ThreadCheckerImpl::ThreadCheckerImpl(ThreadCheckerImpl&& other) {
// Verify that `other` is called on the correct thread.
// Note: This binds `other` if not already bound.
CHECK(other.CalledOnValidThread());
// Not using `other.lock_` to let TSAN catch racy construct from `other`.
bound_at_ = std::move(other.bound_at_);
thread_ref_ = other.thread_ref_;
task_token_ = other.task_token_;
sequence_token_ = other.sequence_token_;
// `other.bound_at_` was moved from so it's null.
other.thread_ref_ = PlatformThreadRef();
other.task_token_ = internal::TaskToken();
other.sequence_token_ = internal::SequenceToken();
}
ThreadCheckerImpl& ThreadCheckerImpl::operator=(ThreadCheckerImpl&& other) {
CHECK(CalledOnValidThread());
// Verify that `other` is called on the correct thread.
// Note: This binds `other` if not already bound.
CHECK(other.CalledOnValidThread());
// Intentionally not using either |lock_| to let TSAN catch racy assign.
TS_UNCHECKED_READ(thread_ref_) = TS_UNCHECKED_READ(other.thread_ref_);
TS_UNCHECKED_READ(task_token_) = TS_UNCHECKED_READ(other.task_token_);
TS_UNCHECKED_READ(sequence_token_) = TS_UNCHECKED_READ(other.sequence_token_);
TS_UNCHECKED_READ(other.thread_ref_) = PlatformThreadRef();
TS_UNCHECKED_READ(other.task_token_) = internal::TaskToken();
TS_UNCHECKED_READ(other.sequence_token_) = internal::SequenceToken();
return *this;
}
bool ThreadCheckerImpl::CalledOnValidThread(
std::unique_ptr<debug::StackTrace>* out_bound_at) const {
AutoLock auto_lock(lock_);
// If we're detached, bind to current state.
EnsureAssigned();
DCHECK(sequence_token_.IsValid());
// Cases to handle:
//
// 1. Bound outside a task and used on the same thread: return true.
// 2. Used on the same thread, TLS destroyed: return true.
// Note: This case exists for historical reasons and should be
// removed. See details in `SequenceCheckerImpl`.
// 3. Same sequence as when this was bound:
// 3a. Sequence is associated with a thread: return true.
// 3b. Sequence may run on any thread: return false.
// Note: Return false even if this happens on the same thread as when
// this was bound, because that would be fortuitous.
// 4. Different sequence than when this was bound: return false.
if (thread_ref_ == PlatformThread::CurrentRef()) {
// If this runs on the bound thread:
// Return true if the checker was bound outside of a `TaskScope`.
if (!task_token_.IsValid()) {
return true;
}
// Return true if the checker was bound in the same `TaskScope`.
if (task_token_ == internal::TaskToken::GetForCurrentThread()) {
return true;
}
// Return true if TLS has been destroyed.
//
// This exists for historical reasons and can probably be removed. See
// details in `SequenceCheckerImpl::CalledOnValidSequence()`.
if (ThreadLocalStorage::HasBeenDestroyed()) {
return true;
}
// Return true if the checker was bound in the same thread-bound sequence.
// `CurrentTaskIsThreadBound()` avoids returning true when non-thread-bound
// tasks from the same sequence run on the same thread by chance.
if (sequence_token_ == internal::SequenceToken::GetForCurrentThread() &&
internal::CurrentTaskIsThreadBound()) {
return true;
}
}
// On failure, set the `out_bound_at` argument.
if (out_bound_at && bound_at_) {
*out_bound_at = std::make_unique<debug::StackTrace>(*bound_at_);
}
return false;
}
void ThreadCheckerImpl::DetachFromThread() {
AutoLock auto_lock(lock_);
bound_at_ = nullptr;
thread_ref_ = PlatformThreadRef();
task_token_ = internal::TaskToken();
sequence_token_ = internal::SequenceToken();
}
void ThreadCheckerImpl::EnsureAssigned() const {
if (!thread_ref_.is_null()) {
return;
}
if (g_log_stack) {
bound_at_ = std::make_unique<debug::StackTrace>(size_t{10});
}
thread_ref_ = PlatformThread::CurrentRef();
task_token_ = internal::TaskToken::GetForCurrentThread();
sequence_token_ = internal::SequenceToken::GetForCurrentThread();
}
} // namespace base