xref: /aosp_15_r20/external/cronet/base/threading/thread_checker_impl.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2011 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/threading/thread_checker_impl.h"
6 
7 #include "base/check.h"
8 #include "base/debug/stack_trace.h"
9 #include "base/sequence_token.h"
10 #include "base/task/single_thread_task_runner.h"
11 #include "base/threading/platform_thread.h"
12 #include "base/threading/thread_checker.h"
13 #include "base/threading/thread_local.h"
14 
15 namespace {
16 bool g_log_stack = false;
17 }
18 
19 namespace base {
20 
21 // static
EnableStackLogging()22 void ThreadCheckerImpl::EnableStackLogging() {
23   g_log_stack = true;
24 }
25 
ThreadCheckerImpl()26 ThreadCheckerImpl::ThreadCheckerImpl() {
27   AutoLock auto_lock(lock_);
28   EnsureAssigned();
29 }
30 
31 ThreadCheckerImpl::~ThreadCheckerImpl() = default;
32 
ThreadCheckerImpl(ThreadCheckerImpl && other)33 ThreadCheckerImpl::ThreadCheckerImpl(ThreadCheckerImpl&& other) {
34   // Verify that `other` is called on the correct thread.
35   // Note: This binds `other` if not already bound.
36   CHECK(other.CalledOnValidThread());
37 
38   //  Not using `other.lock_` to let TSAN catch racy construct from `other`.
39   bound_at_ = std::move(other.bound_at_);
40   thread_ref_ = other.thread_ref_;
41   task_token_ = other.task_token_;
42   sequence_token_ = other.sequence_token_;
43 
44   // `other.bound_at_` was moved from so it's null.
45   other.thread_ref_ = PlatformThreadRef();
46   other.task_token_ = internal::TaskToken();
47   other.sequence_token_ = internal::SequenceToken();
48 }
49 
operator =(ThreadCheckerImpl && other)50 ThreadCheckerImpl& ThreadCheckerImpl::operator=(ThreadCheckerImpl&& other) {
51   CHECK(CalledOnValidThread());
52 
53   // Verify that `other` is called on the correct thread.
54   // Note: This binds `other` if not already bound.
55   CHECK(other.CalledOnValidThread());
56 
57   // Intentionally not using either |lock_| to let TSAN catch racy assign.
58   TS_UNCHECKED_READ(thread_ref_) = TS_UNCHECKED_READ(other.thread_ref_);
59   TS_UNCHECKED_READ(task_token_) = TS_UNCHECKED_READ(other.task_token_);
60   TS_UNCHECKED_READ(sequence_token_) = TS_UNCHECKED_READ(other.sequence_token_);
61 
62   TS_UNCHECKED_READ(other.thread_ref_) = PlatformThreadRef();
63   TS_UNCHECKED_READ(other.task_token_) = internal::TaskToken();
64   TS_UNCHECKED_READ(other.sequence_token_) = internal::SequenceToken();
65 
66   return *this;
67 }
68 
CalledOnValidThread(std::unique_ptr<debug::StackTrace> * out_bound_at) const69 bool ThreadCheckerImpl::CalledOnValidThread(
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   DCHECK(sequence_token_.IsValid());
75 
76   // Cases to handle:
77   //
78   // 1. Bound outside a task and used on the same thread: return true.
79   // 2. Used on the same thread, TLS destroyed: return true.
80   //         Note: This case exists for historical reasons and should be
81   //         removed. See details in `SequenceCheckerImpl`.
82   // 3. Same sequence as when this was bound:
83   //   3a. Sequence is associated with a thread: return true.
84   //   3b. Sequence may run on any thread: return false.
85   //         Note: Return false even if this happens on the same thread as when
86   //         this was bound, because that would be fortuitous.
87   // 4. Different sequence than when this was bound: return false.
88 
89   if (thread_ref_ == PlatformThread::CurrentRef()) {
90     // If this runs on the bound thread:
91 
92     // Return true if the checker was bound outside of a `TaskScope`.
93     if (!task_token_.IsValid()) {
94       return true;
95     }
96 
97     // Return true if the checker was bound in the same `TaskScope`.
98     if (task_token_ == internal::TaskToken::GetForCurrentThread()) {
99       return true;
100     }
101 
102     // Return true if TLS has been destroyed.
103     //
104     // This exists for historical reasons and can probably be removed. See
105     // details in `SequenceCheckerImpl::CalledOnValidSequence()`.
106     if (ThreadLocalStorage::HasBeenDestroyed()) {
107       return true;
108     }
109 
110     // Return true if the checker was bound in the same thread-bound sequence.
111     // `CurrentTaskIsThreadBound()` avoids returning true when non-thread-bound
112     // tasks from the same sequence run on the same thread by chance.
113     if (sequence_token_ == internal::SequenceToken::GetForCurrentThread() &&
114         internal::CurrentTaskIsThreadBound()) {
115       return true;
116     }
117   }
118 
119   // On failure, set the `out_bound_at` argument.
120   if (out_bound_at && bound_at_) {
121     *out_bound_at = std::make_unique<debug::StackTrace>(*bound_at_);
122   }
123   return false;
124 }
125 
DetachFromThread()126 void ThreadCheckerImpl::DetachFromThread() {
127   AutoLock auto_lock(lock_);
128   bound_at_ = nullptr;
129   thread_ref_ = PlatformThreadRef();
130   task_token_ = internal::TaskToken();
131   sequence_token_ = internal::SequenceToken();
132 }
133 
EnsureAssigned() const134 void ThreadCheckerImpl::EnsureAssigned() const {
135   if (!thread_ref_.is_null()) {
136     return;
137   }
138   if (g_log_stack) {
139     bound_at_ = std::make_unique<debug::StackTrace>(size_t{10});
140   }
141   thread_ref_ = PlatformThread::CurrentRef();
142   task_token_ = internal::TaskToken::GetForCurrentThread();
143   sequence_token_ = internal::SequenceToken::GetForCurrentThread();
144 }
145 
146 }  // namespace base
147