
Fixes an error introduced by crrev.com/c/5074212. Bug: 1503967 Change-Id: Ie585bbee398d5efca94d22b4e96ee1b5fefba289 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5193563 Commit-Queue: Francois Pierre Doray <fdoray@chromium.org> Auto-Submit: Francois Pierre Doray <fdoray@chromium.org> Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org> Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com> Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org> Cr-Commit-Position: refs/heads/main@{#1248313}
135 lines
4.3 KiB
C++
135 lines
4.3 KiB
C++
// Copyright 2012 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/sequence_checker_impl.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "base/check.h"
|
|
#include "base/compiler_specific.h"
|
|
#include "base/debug/stack_trace.h"
|
|
#include "base/sequence_token.h"
|
|
#include "base/threading/platform_thread.h"
|
|
#include "base/threading/platform_thread_ref.h"
|
|
#include "base/threading/thread_checker.h"
|
|
#include "base/threading/thread_checker_impl.h"
|
|
#include "base/threading/thread_local_storage.h"
|
|
|
|
namespace base {
|
|
|
|
namespace {
|
|
bool g_log_stack = false;
|
|
}
|
|
|
|
// static
|
|
void SequenceCheckerImpl::EnableStackLogging() {
|
|
g_log_stack = true;
|
|
ThreadChecker::EnableStackLogging();
|
|
}
|
|
|
|
SequenceCheckerImpl::SequenceCheckerImpl() {
|
|
AutoLock auto_lock(lock_);
|
|
EnsureAssigned();
|
|
}
|
|
|
|
SequenceCheckerImpl::~SequenceCheckerImpl() = default;
|
|
|
|
SequenceCheckerImpl::SequenceCheckerImpl(SequenceCheckerImpl&& other) {
|
|
// Verify that `other` is called on the correct thread.
|
|
// Note: This binds `other` if not already bound.
|
|
CHECK(other.CalledOnValidSequence());
|
|
|
|
bound_at_ = std::move(other.bound_at_);
|
|
sequence_token_ = other.sequence_token_;
|
|
thread_ref_ = other.thread_ref_;
|
|
|
|
// `other.bound_at_` was moved from so it's null.
|
|
other.sequence_token_ = internal::SequenceToken();
|
|
other.thread_ref_ = PlatformThreadRef();
|
|
}
|
|
|
|
SequenceCheckerImpl& SequenceCheckerImpl::operator=(
|
|
SequenceCheckerImpl&& other) {
|
|
// Verify that `other` is called on the correct thread.
|
|
// Note: This binds `other` if not already bound.
|
|
CHECK(other.CalledOnValidSequence());
|
|
|
|
TS_UNCHECKED_READ(bound_at_) = std::move(TS_UNCHECKED_READ(other.bound_at_));
|
|
TS_UNCHECKED_READ(sequence_token_) = TS_UNCHECKED_READ(other.sequence_token_);
|
|
TS_UNCHECKED_READ(thread_ref_) = TS_UNCHECKED_READ(other.thread_ref_);
|
|
|
|
// `other.bound_at_` was moved from so it's null.
|
|
TS_UNCHECKED_READ(other.sequence_token_) = internal::SequenceToken();
|
|
TS_UNCHECKED_READ(other.thread_ref_) = PlatformThreadRef();
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool SequenceCheckerImpl::CalledOnValidSequence(
|
|
std::unique_ptr<debug::StackTrace>* out_bound_at) const {
|
|
AutoLock auto_lock(lock_);
|
|
// If we're detached, bind to current state.
|
|
EnsureAssigned();
|
|
|
|
CHECK(!thread_ref_.is_null());
|
|
|
|
// Return true if called from the bound sequence.
|
|
if (sequence_token_ == internal::SequenceToken::GetForCurrentThread()) {
|
|
return true;
|
|
}
|
|
|
|
// Return true if called from the bound thread after TLS destruction.
|
|
//
|
|
// TODO(pbos): This preserves existing behavior that `sequence_token_` is
|
|
// ignored after TLS shutdown. It should either be documented here why that is
|
|
// necessary (shouldn't this destroy on sequence?) or
|
|
// SequenceCheckerTest.FromThreadDestruction should be updated to reflect the
|
|
// expected behavior.
|
|
//
|
|
// crrev.com/682023 added this TLS-check to solve an edge case but that edge
|
|
// case was probably only a problem before TLS-destruction order was fixed in
|
|
// crrev.com/1119244. crrev.com/1117059 further improved TLS-destruction order
|
|
// of tokens by using `thread_local` and making it deterministic.
|
|
//
|
|
// See https://timsong-cpp.github.io/cppwp/n4140/basic.start.term: "If the
|
|
// completion of the constructor or dynamic initialization of an object with
|
|
// thread storage duration is sequenced before that of another, the completion
|
|
// of the destructor of the second is sequenced before the initiation of the
|
|
// destructor of the first."
|
|
if (ThreadLocalStorage::HasBeenDestroyed() &&
|
|
thread_ref_ == PlatformThread::CurrentRef()) {
|
|
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 SequenceCheckerImpl::DetachFromSequence() {
|
|
AutoLock auto_lock(lock_);
|
|
bound_at_.reset();
|
|
sequence_token_ = internal::SequenceToken();
|
|
thread_ref_ = PlatformThreadRef();
|
|
}
|
|
|
|
void SequenceCheckerImpl::EnsureAssigned() const {
|
|
if (sequence_token_.IsValid()) {
|
|
return;
|
|
}
|
|
|
|
if (g_log_stack) {
|
|
bound_at_ = std::make_unique<debug::StackTrace>(size_t{10});
|
|
}
|
|
|
|
sequence_token_ = internal::SequenceToken::GetForCurrentThread();
|
|
DCHECK(sequence_token_.IsValid());
|
|
thread_ref_ = PlatformThread::CurrentRef();
|
|
DCHECK(!thread_ref_.is_null());
|
|
}
|
|
|
|
} // namespace base
|