• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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()21 void ThreadCheckerImpl::EnableStackLogging() {
22   g_log_thread_and_sequence_checker_binding = true;
23 }
24 
ThreadCheckerImpl()25 ThreadCheckerImpl::ThreadCheckerImpl() {
26   AutoLock auto_lock(lock_);
27   EnsureAssigned();
28 }
29 
30 ThreadCheckerImpl::~ThreadCheckerImpl() = default;
31 
ThreadCheckerImpl(ThreadCheckerImpl && other)32 ThreadCheckerImpl::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)51 ThreadCheckerImpl& 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) const71 bool 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) const77 bool 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()117 void 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() const125 std::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() const131 void 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