xref: /aosp_15_r20/external/cronet/base/sequence_checker_impl.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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()26 void SequenceCheckerImpl::EnableStackLogging() {
27   g_log_stack = true;
28   ThreadChecker::EnableStackLogging();
29 }
30 
SequenceCheckerImpl()31 SequenceCheckerImpl::SequenceCheckerImpl() {
32   AutoLock auto_lock(lock_);
33   EnsureAssigned();
34 }
35 
36 SequenceCheckerImpl::~SequenceCheckerImpl() = default;
37 
SequenceCheckerImpl(SequenceCheckerImpl && other)38 SequenceCheckerImpl::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)52 SequenceCheckerImpl& 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) const69 bool 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()112 void SequenceCheckerImpl::DetachFromSequence() {
113   AutoLock auto_lock(lock_);
114   bound_at_.reset();
115   sequence_token_ = internal::SequenceToken();
116   thread_ref_ = PlatformThreadRef();
117 }
118 
EnsureAssigned() const119 void 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