forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththread_checker_impl.cc
143 lines (119 loc) · 5.1 KB
/
thread_checker_impl.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/threading/thread_checker_impl.h"
#include "base/check.h"
#include "base/debug/stack_trace.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_task_runner_handle.h"
namespace {
bool g_log_thread_and_sequence_checker_binding = false;
}
namespace base {
// static
void ThreadCheckerImpl::EnableStackLogging() {
g_log_thread_and_sequence_checker_binding = true;
}
ThreadCheckerImpl::ThreadCheckerImpl() {
AutoLock auto_lock(lock_);
EnsureAssignedLockRequired();
}
ThreadCheckerImpl::~ThreadCheckerImpl() = default;
ThreadCheckerImpl::ThreadCheckerImpl(ThreadCheckerImpl&& other) {
// Verify that |other| is called on its associated thread and bind it now if
// it is currently detached (even if this isn't a DCHECK build).
const bool other_called_on_valid_thread = other.CalledOnValidThread();
DCHECK(other_called_on_valid_thread);
// Intentionally not using |other.lock_| to let TSAN catch racy construct from
// |other|.
bound_at_ = std::move(other.bound_at_);
thread_id_ = other.thread_id_;
task_token_ = other.task_token_;
sequence_token_ = other.sequence_token_;
// other.bound_at_ was moved from so it's null.
other.thread_id_ = PlatformThreadRef();
other.task_token_ = TaskToken();
other.sequence_token_ = SequenceToken();
}
ThreadCheckerImpl& ThreadCheckerImpl::operator=(ThreadCheckerImpl&& other) {
DCHECK(CalledOnValidThread());
// Verify that |other| is called on its associated thread and bind it now if
// it is currently detached (even if this isn't a DCHECK build).
const bool other_called_on_valid_thread = other.CalledOnValidThread();
DCHECK(other_called_on_valid_thread);
// Intentionally not using either |lock_| to let TSAN catch racy assign.
TS_UNCHECKED_READ(thread_id_) = TS_UNCHECKED_READ(other.thread_id_);
TS_UNCHECKED_READ(task_token_) = TS_UNCHECKED_READ(other.task_token_);
TS_UNCHECKED_READ(sequence_token_) = TS_UNCHECKED_READ(other.sequence_token_);
TS_UNCHECKED_READ(other.thread_id_) = PlatformThreadRef();
TS_UNCHECKED_READ(other.task_token_) = TaskToken();
TS_UNCHECKED_READ(other.sequence_token_) = SequenceToken();
return *this;
}
bool ThreadCheckerImpl::CalledOnValidThread(
std::unique_ptr<debug::StackTrace>* out_bound_at) const {
const bool has_thread_been_destroyed = ThreadLocalStorage::HasBeenDestroyed();
AutoLock auto_lock(lock_);
// TaskToken/SequenceToken access thread-local storage. During destruction
// the state of thread-local storage is not guaranteed to be in a consistent
// state. Further, task-runner only installs the tokens when running a task.
if (!has_thread_been_destroyed) {
EnsureAssignedLockRequired();
// Always return true when called from the task from which this
// ThreadCheckerImpl was assigned to a thread.
if (task_token_ == TaskToken::GetForCurrentThread())
return true;
// If this ThreadCheckerImpl is bound to a valid SequenceToken, it must be
// equal to the current SequenceToken and there must be a registered
// ThreadTaskRunnerHandle. Otherwise, the fact that the current task runs on
// the thread to which this ThreadCheckerImpl is bound is fortuitous.
if (sequence_token_.IsValid() &&
(sequence_token_ != SequenceToken::GetForCurrentThread() ||
!ThreadTaskRunnerHandle::IsSet())) {
if (out_bound_at && bound_at_) {
*out_bound_at = std::make_unique<debug::StackTrace>(*bound_at_);
}
return false;
}
} else if (thread_id_.is_null()) {
// We're in tls destruction but the |thread_id_| hasn't been assigned yet.
// Assign it now. This doesn't call EnsureAssigned() as to do so while in
// tls destruction may result in the wrong TaskToken/SequenceToken.
if (g_log_thread_and_sequence_checker_binding)
bound_at_ = std::make_unique<debug::StackTrace>(size_t{10});
thread_id_ = PlatformThread::CurrentRef();
return true;
}
if (thread_id_ != PlatformThread::CurrentRef()) {
if (out_bound_at && bound_at_) {
*out_bound_at = std::make_unique<debug::StackTrace>(*bound_at_);
}
return false;
}
return true;
}
void ThreadCheckerImpl::DetachFromThread() {
AutoLock auto_lock(lock_);
bound_at_ = nullptr;
thread_id_ = PlatformThreadRef();
task_token_ = TaskToken();
sequence_token_ = SequenceToken();
}
std::unique_ptr<debug::StackTrace> ThreadCheckerImpl::GetBoundAt() const {
AutoLock auto_lock(lock_);
if (!bound_at_)
return nullptr;
return std::make_unique<debug::StackTrace>(*bound_at_);
}
void ThreadCheckerImpl::EnsureAssignedLockRequired() const {
if (!thread_id_.is_null())
return;
if (g_log_thread_and_sequence_checker_binding)
bound_at_ = std::make_unique<debug::StackTrace>(size_t{10});
thread_id_ = PlatformThread::CurrentRef();
task_token_ = TaskToken::GetForCurrentThread();
sequence_token_ = SequenceToken::GetForCurrentThread();
}
} // namespace base