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()22void ThreadCheckerImpl::EnableStackLogging() { 23 g_log_stack = true; 24 } 25 ThreadCheckerImpl()26ThreadCheckerImpl::ThreadCheckerImpl() { 27 AutoLock auto_lock(lock_); 28 EnsureAssigned(); 29 } 30 31 ThreadCheckerImpl::~ThreadCheckerImpl() = default; 32 ThreadCheckerImpl(ThreadCheckerImpl && other)33ThreadCheckerImpl::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)50ThreadCheckerImpl& 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) const69bool 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()126void 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() const134void 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