// 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/threading/thread_task_runner_handle.h"

namespace base {

ThreadCheckerImpl::ThreadCheckerImpl() {
  AutoLock auto_lock(lock_);
  EnsureAssigned();
}

ThreadCheckerImpl::~ThreadCheckerImpl() = default;

bool ThreadCheckerImpl::CalledOnValidThread() const {
  AutoLock auto_lock(lock_);
  EnsureAssigned();

  // 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;
  }

  return thread_id_ == PlatformThread::CurrentRef();
}

void ThreadCheckerImpl::DetachFromThread() {
  AutoLock auto_lock(lock_);
  thread_id_ = PlatformThreadRef();
  task_token_ = TaskToken();
  sequence_token_ = SequenceToken();
}

void ThreadCheckerImpl::EnsureAssigned() const {
  lock_.AssertAcquired();
  if (!thread_id_.is_null())
    return;

  thread_id_ = PlatformThread::CurrentRef();
  task_token_ = TaskToken::GetForCurrentThread();
  sequence_token_ = SequenceToken::GetForCurrentThread();
}

}  // namespace base