// 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/logging.h" #include "base/threading/thread_local.h" #include "base/threading/thread_task_runner_handle.h" namespace base { 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|. thread_id_ = other.thread_id_; task_token_ = other.task_token_; sequence_token_ = other.sequence_token_; 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() 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())) { 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. thread_id_ = PlatformThread::CurrentRef(); } return thread_id_ == PlatformThread::CurrentRef(); } void ThreadCheckerImpl::DetachFromThread() { AutoLock auto_lock(lock_); thread_id_ = PlatformThreadRef(); task_token_ = TaskToken(); sequence_token_ = SequenceToken(); } void ThreadCheckerImpl::EnsureAssignedLockRequired() const { if (!thread_id_.is_null()) return; thread_id_ = PlatformThread::CurrentRef(); task_token_ = TaskToken::GetForCurrentThread(); sequence_token_ = SequenceToken::GetForCurrentThread(); } } // namespace base