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/task/single_thread_task_runner.h" 10 #include "base/threading/platform_thread.h" 11 #include "base/threading/thread_checker.h" 12 #include "base/threading/thread_local.h" 13 14 namespace { 15 bool g_log_thread_and_sequence_checker_binding = false; 16 } 17 18 namespace base { 19 20 // static EnableStackLogging()21void ThreadCheckerImpl::EnableStackLogging() { 22 g_log_thread_and_sequence_checker_binding = true; 23 } 24 ThreadCheckerImpl()25ThreadCheckerImpl::ThreadCheckerImpl() { 26 AutoLock auto_lock(lock_); 27 EnsureAssigned(); 28 } 29 30 ThreadCheckerImpl::~ThreadCheckerImpl() = default; 31 ThreadCheckerImpl(ThreadCheckerImpl && other)32ThreadCheckerImpl::ThreadCheckerImpl(ThreadCheckerImpl&& other) { 33 // Verify that |other| is called on its associated thread and bind it now if 34 // it is currently detached (even if this isn't a DCHECK build). 35 const bool other_called_on_valid_thread = other.CalledOnValidThread(); 36 DCHECK(other_called_on_valid_thread); 37 38 // Intentionally not using |other.lock_| to let TSAN catch racy construct from 39 // |other|. 40 bound_at_ = std::move(other.bound_at_); 41 thread_id_ = other.thread_id_; 42 task_token_ = other.task_token_; 43 sequence_token_ = other.sequence_token_; 44 45 // other.bound_at_ was moved from so it's null. 46 other.thread_id_ = PlatformThreadRef(); 47 other.task_token_ = TaskToken(); 48 other.sequence_token_ = SequenceToken(); 49 } 50 operator =(ThreadCheckerImpl && other)51ThreadCheckerImpl& ThreadCheckerImpl::operator=(ThreadCheckerImpl&& other) { 52 DCHECK(CalledOnValidThread()); 53 54 // Verify that |other| is called on its associated thread and bind it now if 55 // it is currently detached (even if this isn't a DCHECK build). 56 const bool other_called_on_valid_thread = other.CalledOnValidThread(); 57 DCHECK(other_called_on_valid_thread); 58 59 // Intentionally not using either |lock_| to let TSAN catch racy assign. 60 TS_UNCHECKED_READ(thread_id_) = TS_UNCHECKED_READ(other.thread_id_); 61 TS_UNCHECKED_READ(task_token_) = TS_UNCHECKED_READ(other.task_token_); 62 TS_UNCHECKED_READ(sequence_token_) = TS_UNCHECKED_READ(other.sequence_token_); 63 64 TS_UNCHECKED_READ(other.thread_id_) = PlatformThreadRef(); 65 TS_UNCHECKED_READ(other.task_token_) = TaskToken(); 66 TS_UNCHECKED_READ(other.sequence_token_) = SequenceToken(); 67 68 return *this; 69 } 70 CalledOnValidThread(std::unique_ptr<debug::StackTrace> * out_bound_at) const71bool ThreadCheckerImpl::CalledOnValidThread( 72 std::unique_ptr<debug::StackTrace>* out_bound_at) const { 73 AutoLock auto_lock(lock_); 74 return CalledOnValidThreadInternal(out_bound_at); 75 } 76 CalledOnValidThreadInternal(std::unique_ptr<debug::StackTrace> * out_bound_at) const77bool ThreadCheckerImpl::CalledOnValidThreadInternal( 78 std::unique_ptr<debug::StackTrace>* out_bound_at) const { 79 // If we're detached, bind to current state. 80 EnsureAssigned(); 81 82 // Always return true when called from the task from which this 83 // ThreadCheckerImpl was assigned to a thread. 84 if (task_token_.IsValid() && 85 task_token_ == TaskToken::GetForCurrentThread()) { 86 return true; 87 } 88 89 // If this ThreadCheckerImpl is bound to a valid SequenceToken, it must be 90 // equal to the current SequenceToken and there must be a registered 91 // SingleThreadTaskRunner::CurrentDefaultHandle. Otherwise, the fact that 92 // the current task runs on the thread to which this ThreadCheckerImpl is 93 // bound is fortuitous. 94 // 95 // TODO(pbos): This preserves existing behavior that `sequence_token_` is 96 // ignored after TLS shutdown. It should either be documented here why that is 97 // necessary (shouldn't this destroy on sequence?) or 98 // SequenceCheckerTest.CalledOnValidSequenceFromThreadDestruction should be 99 // updated to reflect the expected behavior. 100 // 101 // See SequenceCheckerImpl::CalledOnValidSequence for additional context. 102 const bool sequence_is_invalid = 103 sequence_token_.IsValid() && 104 (sequence_token_ != SequenceToken::GetForCurrentThread() || 105 !SingleThreadTaskRunner::HasCurrentDefault()) && 106 !ThreadLocalStorage::HasBeenDestroyed(); 107 108 if (sequence_is_invalid || thread_id_ != PlatformThread::CurrentRef()) { 109 if (out_bound_at && bound_at_) { 110 *out_bound_at = std::make_unique<debug::StackTrace>(*bound_at_); 111 } 112 return false; 113 } 114 return true; 115 } 116 DetachFromThread()117void ThreadCheckerImpl::DetachFromThread() { 118 AutoLock auto_lock(lock_); 119 bound_at_ = nullptr; 120 thread_id_ = PlatformThreadRef(); 121 task_token_ = TaskToken(); 122 sequence_token_ = SequenceToken(); 123 } 124 GetBoundAt() const125std::unique_ptr<debug::StackTrace> ThreadCheckerImpl::GetBoundAt() const { 126 if (!bound_at_) 127 return nullptr; 128 return std::make_unique<debug::StackTrace>(*bound_at_); 129 } 130 EnsureAssigned() const131void ThreadCheckerImpl::EnsureAssigned() const { 132 if (!thread_id_.is_null()) 133 return; 134 if (g_log_thread_and_sequence_checker_binding) 135 bound_at_ = std::make_unique<debug::StackTrace>(size_t{10}); 136 thread_id_ = PlatformThread::CurrentRef(); 137 task_token_ = TaskToken::GetForCurrentThread(); 138 sequence_token_ = SequenceToken::GetForCurrentThread(); 139 } 140 141 } // namespace base 142