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
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223

content / child / child_process.cc [blame]

// 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.

#include "content/child/child_process.h"

#include <string.h>

#include "base/clang_profiling_buildflags.h"
#include "base/functional/bind.h"
#include "base/message_loop/message_pump_type.h"
#include "base/process/process_handle.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/hang_watcher.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "build/config/compiler/compiler_buildflags.h"
#include "content/child/child_thread_impl.h"
#include "content/common/process_visibility_tracker.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "sandbox/policy/sandbox_type.h"
#include "services/tracing/public/cpp/trace_startup.h"
#include "third_party/blink/public/common/features.h"

#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
#include "base/test/clang_profiling.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include "content/common/android/cpu_time_metrics.h"
#endif

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "content/child/sandboxed_process_thread_type_handler.h"
#endif

namespace content {

namespace {

constinit thread_local ChildProcess* child_process = nullptr;

class ChildIOThread : public base::Thread {
 public:
  ChildIOThread() : base::Thread("Chrome_ChildIOThread") {}
  ChildIOThread(const ChildIOThread&) = delete;
  ChildIOThread(ChildIOThread&&) = delete;
  ChildIOThread& operator=(const ChildIOThread&) = delete;
  ChildIOThread& operator=(ChildIOThread&&) = delete;

  void Run(base::RunLoop* run_loop) override {
    mojo::InterfaceEndpointClient::SetThreadNameSuffixForMetrics(
        "ChildIOThread");
    base::ScopedClosureRunner unregister_thread_closure;
    if (base::HangWatcher::IsIOThreadHangWatchingEnabled()) {
      unregister_thread_closure = base::HangWatcher::RegisterThread(
          base::HangWatcher::ThreadType::kIOThread);
    }
    base::Thread::Run(run_loop);
  }
};

}  // namespace

ChildProcess::ChildProcess(base::ThreadType io_thread_type,
                           std::unique_ptr<base::ThreadPoolInstance::InitParams>
                               thread_pool_init_params)
    : resetter_(&child_process, this, nullptr),
      io_thread_(std::make_unique<ChildIOThread>()) {
  // Start ThreadPoolInstance if not already done. A ThreadPoolInstance
  // should already exist, and may already be running when ChildProcess is
  // instantiated in the browser process or in a test process.
  //
  // There are 3 possibilities:
  //
  // 1. ChildProcess is actually being constructed on a thread in the browser
  //    process (eg. for single-process mode). The ThreadPool was already
  //    started on the main thread, but this happened before the ChildProcess
  //    thread was created, which creates a happens-before relationship. So
  //    it's safe to check WasStartedUnsafe().
  // 2. ChildProcess is being constructed in a test. The ThreadPool was
  //    already started by TaskEnvironment on the main thread. Depending on
  //    the test, ChildProcess might be constructed on the main thread or
  //    another thread that was created after the test start. Either way, it's
  //    safe to check WasStartedUnsafe().
  // 3. ChildProcess is being constructed in a subprocess from ContentMain, on
  //    the main thread. This is the same thread that created the ThreadPool
  //    so it's safe to check WasStartedUnsafe().
  //
  // Note that the only case we expect WasStartedUnsafe() to return true
  // should be running on the main thread. So if there's a logic error and a
  // stale read causes WasStartedUnsafe() to return false after the
  // ThreadPool was started, Start() will correctly DCHECK as it's called on the
  // wrong thread. (The result never flips from true to false so a stale read
  // should never return true.)
  auto* thread_pool = base::ThreadPoolInstance::Get();
  DCHECK(thread_pool);
  if (!thread_pool->WasStartedUnsafe()) {
    if (thread_pool_init_params)
      thread_pool->Start(*thread_pool_init_params.get());
    else
      thread_pool->StartWithDefaultParams();
    initialized_thread_pool_ = true;
  }

  tracing::InitTracingPostThreadPoolStartAndFeatureList(
      /* enable_consumer */ false);

  // Ensure the visibility tracker is created on the main thread.
  ProcessVisibilityTracker::GetInstance();

#if BUILDFLAG(IS_ANDROID)
  SetupCpuTimeMetrics();
#endif

  // We can't recover from failing to start the IO thread.
  base::Thread::Options thread_options(base::MessagePumpType::IO, 0);
  thread_options.thread_type = io_thread_type;
// TODO(crbug.com/40226692): Figure out whether IS_ANDROID can be lifted here.
#if BUILDFLAG(IS_ANDROID)
  // TODO(reveman): Remove this in favor of setting it explicitly for each type
  // of process.
  thread_options.thread_type = base::ThreadType::kDisplayCritical;
#endif
  CHECK(io_thread_->StartWithOptions(std::move(thread_options)));
  io_thread_runner_ = io_thread_->task_runner();
}

ChildProcess::ChildProcess(
    scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner)
    : resetter_(&child_process, this, nullptr),
      io_thread_runner_(std::move(io_thread_runner)) {}

ChildProcess::~ChildProcess() {
  DCHECK_EQ(child_process, this);

  // Signal this event before destroying the child process.  That way all
  // background threads can cleanup.
  // For example, in the renderer the RenderThread instances will be able to
  // notice shutdown before the render process begins waiting for them to exit.
  shutdown_event_.Signal();

  if (main_thread_) {  // null in unittests.
    main_thread_->Shutdown();
    if (main_thread_->ShouldBeDestroyed()) {
      main_thread_.reset();
    } else {
      // Leak the main_thread_. See a comment in
      // RenderThreadImpl::ShouldBeDestroyed.
      main_thread_.release();
    }
  }

  if (io_thread_) {
    io_thread_->Stop();
    io_thread_.reset();
  }

  if (initialized_thread_pool_) {
    DCHECK(base::ThreadPoolInstance::Get());
    base::ThreadPoolInstance::Get()->Shutdown();
  }

#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) && BUILDFLAG(CLANG_PGO)
  // Flush the profiling data to disk. Doing this manually (vs relying on this
  // being done automatically when the process exits) will ensure that this data
  // doesn't get lost if the process is fast killed.
  base::WriteClangProfilingProfile();
#endif
}

ChildThreadImpl* ChildProcess::main_thread() {
  return main_thread_.get();
}

void ChildProcess::set_main_thread(ChildThreadImpl* thread) {
  main_thread_.reset(thread);
}

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
void ChildProcess::SetIOThreadType(base::ThreadType thread_type) {
  if (!io_thread_) {
    return;
  }

  // The SandboxedProcessThreadTypeHandler isn't created in
  // in --single-process mode or if certain base::Features are disabled. See
  // instances of SandboxedProcessThreadTypeHandler::Create() for more details.
  if (SandboxedProcessThreadTypeHandler* sandboxed_process_thread_type_handler =
          SandboxedProcessThreadTypeHandler::Get()) {
    sandboxed_process_thread_type_handler->HandleThreadTypeChange(
        io_thread_->GetThreadId(), base::ThreadType::kDisplayCritical);
  }
}
#endif

void ChildProcess::AddRefProcess() {
  DCHECK(!main_thread_.get() ||  // null in unittests.
         main_thread_->main_thread_runner()->BelongsToCurrentThread());
  ref_count_++;
}

void ChildProcess::ReleaseProcess() {
  DCHECK(!main_thread_.get() ||  // null in unittests.
         main_thread_->main_thread_runner()->BelongsToCurrentThread());
  DCHECK(ref_count_);
  if (--ref_count_)
    return;

  if (main_thread_)  // null in unittests.
    main_thread_->OnProcessFinalRelease();
}

ChildProcess* ChildProcess::current() {
  return child_process;
}

base::WaitableEvent* ChildProcess::GetShutDownEvent() {
  return &shutdown_event_;
}

}  // namespace content