1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/sequence_checker_impl.h" 6 7 #include <utility> 8 9 #include "base/check.h" 10 #include "base/compiler_specific.h" 11 #include "base/debug/stack_trace.h" 12 #include "base/sequence_token.h" 13 #include "base/threading/platform_thread.h" 14 #include "base/threading/platform_thread_ref.h" 15 #include "base/threading/thread_checker.h" 16 #include "base/threading/thread_checker_impl.h" 17 #include "base/threading/thread_local_storage.h" 18 19 namespace base { 20 21 namespace { 22 bool g_log_stack = false; 23 } 24 25 // static EnableStackLogging()26void SequenceCheckerImpl::EnableStackLogging() { 27 g_log_stack = true; 28 ThreadChecker::EnableStackLogging(); 29 } 30 SequenceCheckerImpl()31SequenceCheckerImpl::SequenceCheckerImpl() { 32 AutoLock auto_lock(lock_); 33 EnsureAssigned(); 34 } 35 36 SequenceCheckerImpl::~SequenceCheckerImpl() = default; 37 SequenceCheckerImpl(SequenceCheckerImpl && other)38SequenceCheckerImpl::SequenceCheckerImpl(SequenceCheckerImpl&& other) { 39 // Verify that `other` is called on the correct thread. 40 // Note: This binds `other` if not already bound. 41 CHECK(other.CalledOnValidSequence()); 42 43 bound_at_ = std::move(other.bound_at_); 44 sequence_token_ = other.sequence_token_; 45 thread_ref_ = other.thread_ref_; 46 47 // `other.bound_at_` was moved from so it's null. 48 other.sequence_token_ = internal::SequenceToken(); 49 other.thread_ref_ = PlatformThreadRef(); 50 } 51 operator =(SequenceCheckerImpl && other)52SequenceCheckerImpl& SequenceCheckerImpl::operator=( 53 SequenceCheckerImpl&& other) { 54 // Verify that `other` is called on the correct thread. 55 // Note: This binds `other` if not already bound. 56 CHECK(other.CalledOnValidSequence()); 57 58 TS_UNCHECKED_READ(bound_at_) = std::move(TS_UNCHECKED_READ(other.bound_at_)); 59 TS_UNCHECKED_READ(sequence_token_) = TS_UNCHECKED_READ(other.sequence_token_); 60 TS_UNCHECKED_READ(thread_ref_) = TS_UNCHECKED_READ(other.thread_ref_); 61 62 // `other.bound_at_` was moved from so it's null. 63 TS_UNCHECKED_READ(other.sequence_token_) = internal::SequenceToken(); 64 TS_UNCHECKED_READ(other.thread_ref_) = PlatformThreadRef(); 65 66 return *this; 67 } 68 CalledOnValidSequence(std::unique_ptr<debug::StackTrace> * out_bound_at) const69bool SequenceCheckerImpl::CalledOnValidSequence( 70 std::unique_ptr<debug::StackTrace>* out_bound_at) const { 71 AutoLock auto_lock(lock_); 72 // If we're detached, bind to current state. 73 EnsureAssigned(); 74 75 CHECK(!thread_ref_.is_null()); 76 77 // Return true if called from the bound sequence. 78 if (sequence_token_ == internal::SequenceToken::GetForCurrentThread()) { 79 return true; 80 } 81 82 // Return true if called from the bound thread after TLS destruction. 83 // 84 // TODO(pbos): This preserves existing behavior that `sequence_token_` is 85 // ignored after TLS shutdown. It should either be documented here why that is 86 // necessary (shouldn't this destroy on sequence?) or 87 // SequenceCheckerTest.FromThreadDestruction should be updated to reflect the 88 // expected behavior. 89 // 90 // crrev.com/682023 added this TLS-check to solve an edge case but that edge 91 // case was probably only a problem before TLS-destruction order was fixed in 92 // crrev.com/1119244. crrev.com/1117059 further improved TLS-destruction order 93 // of tokens by using `thread_local` and making it deterministic. 94 // 95 // See https://timsong-cpp.github.io/cppwp/n4140/basic.start.term: "If the 96 // completion of the constructor or dynamic initialization of an object with 97 // thread storage duration is sequenced before that of another, the completion 98 // of the destructor of the second is sequenced before the initiation of the 99 // destructor of the first." 100 if (ThreadLocalStorage::HasBeenDestroyed() && 101 thread_ref_ == PlatformThread::CurrentRef()) { 102 return true; 103 } 104 105 // On failure, set the `out_bound_at` argument. 106 if (out_bound_at && bound_at_) { 107 *out_bound_at = std::make_unique<debug::StackTrace>(*bound_at_); 108 } 109 return false; 110 } 111 DetachFromSequence()112void SequenceCheckerImpl::DetachFromSequence() { 113 AutoLock auto_lock(lock_); 114 bound_at_.reset(); 115 sequence_token_ = internal::SequenceToken(); 116 thread_ref_ = PlatformThreadRef(); 117 } 118 EnsureAssigned() const119void SequenceCheckerImpl::EnsureAssigned() const { 120 if (sequence_token_.IsValid()) { 121 return; 122 } 123 124 if (g_log_stack) { 125 bound_at_ = std::make_unique<debug::StackTrace>(size_t{10}); 126 } 127 128 sequence_token_ = internal::SequenceToken::GetForCurrentThread(); 129 DCHECK(sequence_token_.IsValid()); 130 thread_ref_ = PlatformThread::CurrentRef(); 131 DCHECK(!thread_ref_.is_null()); 132 } 133 134 } // namespace base 135