thread_checker_impl.cc 5.1 KB

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