910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_H_
#define CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_H_

#include <memory>
#include <string>
#include <utility>

#include "base/check.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/macros/uniquify.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/thread_annotations.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_task_traits.h"

#if defined(UNIT_TEST)
#include "base/logging.h"
#endif

namespace content {

// Use DCHECK_CURRENTLY_ON(BrowserThread::ID) to DCHECK that a function can only
// be called on the named BrowserThread.
#define DCHECK_CURRENTLY_ON(thread_identifier)                                \
  ::content::internal::ScopedValidateBrowserThreadDebugChecker BASE_UNIQUIFY( \
      scoped_validate_browser_thread_dchecker_)(thread_identifier)

// Use CHECK_CURRENTLY_ON(BrowserThread::ID) to CHECK that a function can only
// be called on the named BrowserThread.
#define CHECK_CURRENTLY_ON(thread_identifier, ...)                       \
  ::content::internal::ScopedValidateBrowserThreadChecker BASE_UNIQUIFY( \
      scoped_validate_browser_thread_checker_)(                          \
      thread_identifier __VA_OPT__(, ) __VA_ARGS__)

// GUARDED_BY_BROWSER_THREAD() enforces that a member variable is only accessed
// from a scope that invokes DCHECK_CURRENTLY_ON() or CHECK_CURRENTLY_ON() or
// from a function annotated with VALID_BROWSER_THREAD_REQUIRED(). The code will
// not compile if the member variable is accessed and these conditions are not
// met.
#define GUARDED_BY_BROWSER_THREAD(thread_identifier) \
  GUARDED_BY(::content::internal::GetBrowserThreadChecker(thread_identifier))

// VALID_CONTEXT_REQUIRED() enforces that a member function is only accessed
// from a scope that invokes DCHECK_CURRENTLY_ON() or CHECK_CURRENTLY_ON() or
// from another function annotated with VALID_BROWSER_THREAD_REQUIRED(). The
// code will not compile if the member function is accessed and these conditions
// are not met.
#define VALID_BROWSER_THREAD_REQUIRED(thread_identifier) \
  EXCLUSIVE_LOCKS_REQUIRED(                              \
      ::content::internal::GetBrowserThreadChecker(thread_identifier))

// The main entry point to post tasks to the UI thread. Tasks posted with the
// same |traits| will run in posting order (i.e. according to the
// SequencedTaskRunner contract). Tasks posted with different |traits| can be
// re-ordered. You may keep a reference to this task runner, it's always
// thread-safe to post to it though it may start returning false at some point
// during shutdown when it definitely is no longer accepting tasks.
//
// In unit tests, there must be a content::BrowserTaskEnvironment in scope for
// this API to be available.
CONTENT_EXPORT scoped_refptr<base::SingleThreadTaskRunner>
GetUIThreadTaskRunner(const BrowserTaskTraits& traits = {});

// The BrowserThread::IO counterpart to GetUIThreadTaskRunner().
CONTENT_EXPORT scoped_refptr<base::SingleThreadTaskRunner>
GetIOThreadTaskRunner(const BrowserTaskTraits& traits = {});

///////////////////////////////////////////////////////////////////////////////
// BrowserThread
//
// Utility functions for threads that are known by a browser-wide name.
class CONTENT_EXPORT BrowserThread {
 public:
  // An enumeration of the well-known threads.
  enum ID {
    // The main thread in the browser. It stops running tasks during shutdown
    // and is never joined.
    UI,

    // This is the thread that processes non-blocking I/O, i.e. IPC and network.
    // Blocking I/O should happen in base::ThreadPool. It is joined on shutdown
    // (and thus any task posted to it may block shutdown).
    //
    // The name is admittedly confusing, as the IO thread is not for blocking
    // I/O like calling base::File::Read. "The highly responsive, non-blocking
    // I/O thread for IPC" is more accurate but too long for an enum name. See
    // docs/transcripts/wuwt-e08-processes.md at 44:20 for more history.
    IO,

    // NOTE: do not add new threads here. Instead you should just use
    // base::ThreadPool::Create*TaskRunner to run tasks on the base::ThreadPool.

    // This identifier does not represent a thread.  Instead it counts the
    // number of well-known threads.  Insert new well-known threads before this
    // identifier.
    ID_COUNT
  };

  BrowserThread(const BrowserThread&) = delete;
  BrowserThread& operator=(const BrowserThread&) = delete;

  // Posts a |task| to run at BEST_EFFORT priority using an arbitrary
  // |task_runner| for which we do not control the priority.
  //
  // This is useful when a task needs to run on |task_runner| (for thread-safety
  // reasons) but should be delayed until after critical phases (e.g. startup).
  // TODO(crbug.com/40553790): Add support for sequence-funneling and remove
  // this method.
  static void PostBestEffortTask(const base::Location& from_here,
                                 scoped_refptr<base::TaskRunner> task_runner,
                                 base::OnceClosure task);

  // Callable on any thread.  Returns whether the given well-known thread is
  // initialized.
  [[nodiscard]] static bool IsThreadInitialized(ID identifier);

  // Callable on any thread.  Returns whether you're currently on a particular
  // thread.  To DCHECK this, use the DCHECK_CURRENTLY_ON() macro above.
  [[nodiscard]] static bool CurrentlyOn(ID identifier);

  // If the current message loop is one of the known threads, returns true and
  // sets identifier to its ID.  Otherwise returns false.
  [[nodiscard]] static bool GetCurrentThreadIdentifier(ID* identifier);

  // Use these templates in conjunction with RefCountedThreadSafe or scoped_ptr
  // when you want to ensure that an object is deleted on a specific thread.
  // This is needed when an object can hop between threads (i.e. UI -> IO ->
  // UI), and thread switching delays can mean that the final UI tasks executes
  // before the IO task's stack unwinds. This would lead to the object
  // destructing on the IO thread, which often is not what you want (i.e. to
  // notify other objects on the creating thread etc). Note: see
  // base::OnTaskRunnerDeleter and base::RefCountedDeleteOnSequence to bind to
  // SequencedTaskRunner instead of specific BrowserThreads.
  template <ID thread>
  struct DeleteOnThread {
    template <typename T>
    static void Destruct(const T* x) {
      if (CurrentlyOn(thread)) {
        delete x;
      } else {
        if (!GetTaskRunnerForThread(thread)->DeleteSoon(FROM_HERE, x)) {
#if defined(UNIT_TEST)
          // Only logged under unit testing because leaks at shutdown
          // are acceptable under normal circumstances.
          LOG(ERROR) << "DeleteSoon failed on thread " << thread;
#endif  // UNIT_TEST
        }
      }
    }
    template <typename T>
    inline void operator()(T* ptr) const {
      enum { type_must_be_complete = sizeof(T) };
      Destruct(ptr);
    }
  };

  // Sample usage with RefCountedThreadSafe:
  // class Foo
  //     : public base::RefCountedThreadSafe<
  //           Foo, BrowserThread::DeleteOnIOThread> {
  //
  // ...
  //  private:
  //   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
  //   friend class base::DeleteHelper<Foo>;
  //
  //   ~Foo();
  //
  // Sample usage with scoped_ptr:
  // std::unique_ptr<Foo, BrowserThread::DeleteOnIOThread> ptr;
  //
  // Note: see base::OnTaskRunnerDeleter and base::RefCountedDeleteOnSequence to
  // bind to SequencedTaskRunner instead of specific BrowserThreads.
  struct DeleteOnUIThread : public DeleteOnThread<UI> {};
  struct DeleteOnIOThread : public DeleteOnThread<IO> {};

  // Returns an appropriate error message for when DCHECK_CURRENTLY_ON() fails.
  static std::string GetCurrentlyOnErrorMessage(ID expected);

  // Runs all pending tasks for the given thread. Tasks posted after this method
  // is called (in particular any task posted from within any of the pending
  // tasks) will be queued but not run. Conceptually this call will disable all
  // queues, run any pending tasks, and re-enable all the queues.
  //
  // If any of the pending tasks posted a task, these could be run by calling
  // this method again or running a regular RunLoop. But if that were the case
  // you should probably rewrite you tests to wait for a specific event instead.
  //
  // NOTE: Can only be called from the UI thread.
  static void RunAllPendingTasksOnThreadForTesting(ID identifier);

  // Helper that returns GetUIThreadTaskRunner({}) or GetIOThreadTaskRunner({})
  // based on |identifier|. Requires that the BrowserThread with the provided
  // |identifier| was started.
  static scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForThread(
      ID identifier);

 private:
  friend class BrowserThreadImpl;
  BrowserThread() = default;
};

namespace internal {

class THREAD_ANNOTATION_ATTRIBUTE__(capability("BrowserThread checker"))
    CONTENT_EXPORT BrowserThreadChecker {
 public:
  [[nodiscard]] bool CalledOnValidBrowserThread(
      BrowserThread::ID thread_identifier) const;
};

// Returns the global BrowserThreadChecker associated with `thread_identifier`.
CONTENT_EXPORT const BrowserThreadChecker& GetBrowserThreadChecker(
    BrowserThread::ID thread_identifier);

// CHECK version.
class CONTENT_EXPORT SCOPED_LOCKABLE ScopedValidateBrowserThreadChecker {
 public:
  explicit ScopedValidateBrowserThreadChecker(
      BrowserThread::ID thread_identifier,
      base::NotFatalUntil fatal_milestone =
          base::NotFatalUntil::NoSpecifiedMilestoneInternal)
      EXCLUSIVE_LOCK_FUNCTION(GetBrowserThreadChecker(thread_identifier));
  ~ScopedValidateBrowserThreadChecker() UNLOCK_FUNCTION();
};

// DCHECK version.
// Note: When DCHECKs are disabled, this class needs to be completely optimized
// out in order to not regress binary size. This is achieved by inlining the
// constructor and the destructor. When DCHECKs are enabled, the constructor
// is not unnecessarily inlined.
class CONTENT_EXPORT SCOPED_LOCKABLE ScopedValidateBrowserThreadDebugChecker {
 public:
  explicit ScopedValidateBrowserThreadDebugChecker(
      BrowserThread::ID thread_identifier)
      EXCLUSIVE_LOCK_FUNCTION(GetBrowserThreadChecker(thread_identifier))
// Only inlined when DCHECKs are turned off.
#if DCHECK_IS_ON()
          ;
#else
  {
  }
#endif

  // Note: Can't use = default as it does not work well with UNLOCK_FUNCTION().
  // Clang will discard the UNLOCK_FUNCTION() attribute.
  // See https://github.com/llvm/llvm-project/issues/101199.
  ~ScopedValidateBrowserThreadDebugChecker() UNLOCK_FUNCTION() {}
};

}  // namespace internal

}  // namespace content

#endif  // CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_H_