Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /base /sequence_checker_impl.cc
blob: 737041866fef2fac4f78ac454684083c22d09d7f [file] [log] [blame] [edit]
// 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<algorithm>
#include<utility>
#include"base/check.h"
#include"base/compiler_specific.h"
#include"base/containers/contains.h"
#include"base/debug/stack_trace.h"
#include"base/sequence_token.h"
#include"base/synchronization/lock_subtle.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
voidSequenceCheckerImpl::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_;
#if DCHECK_IS_ON()
locks_= std::move(other.locks_);
#endif// DCHECK_IS_ON()
thread_ref_= other.thread_ref_;
// `other.bound_at_` and `other.locks_` were moved so they're null.
DCHECK(!other.bound_at_);
#if DCHECK_IS_ON()
DCHECK(other.locks_.empty());
#endif// DCHECK_IS_ON()
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_);
#if DCHECK_IS_ON()
TS_UNCHECKED_READ(locks_)= std::move(TS_UNCHECKED_READ(other.locks_));
#endif// DCHECK_IS_ON()
TS_UNCHECKED_READ(thread_ref_)= TS_UNCHECKED_READ(other.thread_ref_);
// `other.bound_at_` and `other.locks_` were moved so they're null.
DCHECK(!TS_UNCHECKED_READ(other.bound_at_));
#if DCHECK_IS_ON()
DCHECK(TS_UNCHECKED_READ(other.locks_).empty());
#endif// DCHECK_IS_ON()
TS_UNCHECKED_READ(other.sequence_token_)= internal::SequenceToken();
TS_UNCHECKED_READ(other.thread_ref_)=PlatformThreadRef();
return*this;
}
boolSequenceCheckerImpl::CalledOnValidSequence(
std::unique_ptr<debug::StackTrace>* out_bound_at)const{
AutoLock auto_lock(lock_);
EnsureAssigned();
CHECK(!thread_ref_.is_null());
// Valid if current sequence is the bound sequence.
bool is_valid=
(sequence_token_== internal::SequenceToken::GetForCurrentThread());
// Valid if holding a bound lock.
if(!is_valid){
#if DCHECK_IS_ON()
for(uintptr_t lock: subtle::GetTrackedLocksHeldByCurrentThread()){
if(Contains(locks_, lock)){
is_valid=true;
break;
}
}
#endif// DCHECK_IS_ON()
}
// Valid 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(!is_valid){
is_valid=ThreadLocalStorage::HasBeenDestroyed()&&
thread_ref_==PlatformThread::CurrentRef();
}
if(!is_valid){
// Return false without modifying the state if this call is not guaranteed
// to be mutually exclusive with others that returned true. Not modifying
// the state allows future calls to return true if they are mutually
// exclusive with other calls that returned 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_);
}
returnfalse;
}
// Before returning true, modify the state so future calls only return true if
// they are guaranteed to be mutually exclusive with this one.
#if DCHECK_IS_ON()
// `locks_` must contain locks held at binding time and for all calls to
// `CalledOnValidSequence` that returned true afterwards.
std::erase_if(locks_,[](uintptr_t lock_ptr){
return!Contains(subtle::GetTrackedLocksHeldByCurrentThread(), lock_ptr);
});
#endif// DCHECK_IS_ON()
// `sequence_token_` is reset if this returns true from a different sequence.
if(sequence_token_!= internal::SequenceToken::GetForCurrentThread()){
sequence_token_= internal::SequenceToken();
}
returntrue;
}
voidSequenceCheckerImpl::DetachFromSequence(){
AutoLock auto_lock(lock_);
bound_at_.reset();
sequence_token_= internal::SequenceToken();
#if DCHECK_IS_ON()
locks_.clear();
#endif// DCHECK_IS_ON()
thread_ref_=PlatformThreadRef();
}
voidSequenceCheckerImpl::EnsureAssigned()const{
// Use `thread_ref_` to determine if this checker is already bound, as it is
// always set when bound (unlike `sequence_token_` and `locks_` which may be
// cleared by `CalledOnValidSequence()` while this checker is still bound).
if(!thread_ref_.is_null()){
return;
}
if(g_log_stack){
bound_at_= std::make_unique<debug::StackTrace>(size_t{10});
}
sequence_token_= internal::SequenceToken::GetForCurrentThread();
#if DCHECK_IS_ON()
// Copy all held locks to `locks_`, except `&lock_` (this is an implementation
// detail of `SequenceCheckerImpl` and doesn't provide mutual exclusion
// guarantees to the caller).
DCHECK(locks_.empty());
std::ranges::remove_copy(subtle::GetTrackedLocksHeldByCurrentThread(),
std::back_inserter(locks_),
reinterpret_cast<uintptr_t>(&lock_));
#endif// DCHECK_IS_ON()
DCHECK(sequence_token_.IsValid());
thread_ref_=PlatformThread::CurrentRef();
DCHECK(!thread_ref_.is_null());
}
}// namespace base

[8]ページ先頭

©2009-2025 Movatter.jp