tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CompositorThread.cpp (7568B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 #include "CompositorThread.h"
      7 
      8 #include "CompositorBridgeParent.h"
      9 #include "gfxGradientCache.h"
     10 #include "MainThreadUtils.h"
     11 #include "VRManagerParent.h"
     12 #include "mozilla/BackgroundHangMonitor.h"
     13 #include "mozilla/SpinEventLoopUntil.h"
     14 #include "mozilla/gfx/gfxVars.h"
     15 #include "mozilla/layers/CompositorManagerParent.h"
     16 #include "mozilla/layers/ImageBridgeParent.h"
     17 #include "mozilla/layers/VideoBridgeParent.h"
     18 #include "mozilla/media/MediaSystemResourceService.h"
     19 #include "nsThread.h"
     20 #include "nsThreadUtils.h"
     21 
     22 namespace mozilla {
     23 namespace layers {
     24 
     25 static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
     26 static Atomic<bool> sFinishedCompositorShutDown(false);
     27 static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor;
     28 static ProfilerThreadId sProfilerThreadId;
     29 
     30 nsIThread* CompositorThread() {
     31  return sCompositorThreadHolder
     32             ? sCompositorThreadHolder->GetCompositorThread()
     33             : nullptr;
     34 }
     35 
     36 CompositorThreadHolder* CompositorThreadHolder::GetSingleton() {
     37  return sCompositorThreadHolder;
     38 }
     39 
     40 CompositorThreadHolder::CompositorThreadHolder()
     41    : mCompositorThread(CreateCompositorThread()) {
     42  MOZ_ASSERT(NS_IsMainThread());
     43 }
     44 
     45 CompositorThreadHolder::~CompositorThreadHolder() {
     46  sFinishedCompositorShutDown = true;
     47 }
     48 
     49 /* static */ already_AddRefed<nsIThread>
     50 CompositorThreadHolder::CreateCompositorThread() {
     51  MOZ_ASSERT(NS_IsMainThread());
     52 
     53  MOZ_ASSERT(!sCompositorThreadHolder,
     54             "The compositor thread has already been started!");
     55 
     56  // When the CanvasRenderer thread is disabled, WebGL may be handled on this
     57  // thread, requiring a bigger stack size. See: CanvasManagerParent::Init
     58  //
     59  // This is 4M, which is higher than the default 256K.
     60  // Increased with bug 1753349 to accommodate the `chromium/5359` branch of
     61  // ANGLE, which has large peak stack usage for some pathological shader
     62  // compilations.
     63  //
     64  // Previously increased to 512K to accommodate Mesa in bug 1753340.
     65  //
     66  // Previously increased to 320K to avoid a stack overflow in the
     67  // Intel Vulkan driver initialization in bug 1716120.
     68  //
     69  // Note: we only override it if it's limited already.
     70  uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE;
     71  if (stackSize) {
     72    stackSize =
     73        std::max(stackSize, gfx::gfxVars::SupportsThreadsafeGL() &&
     74                                    !gfx::gfxVars::UseCanvasRenderThread()
     75                                ? 4096U << 10
     76                                : 512U << 10);
     77  }
     78 
     79  nsCOMPtr<nsIThread> compositorThread;
     80  nsresult rv = NS_NewNamedThread(
     81      "Compositor", getter_AddRefs(compositorThread),
     82      NS_NewRunnableFunction(
     83          "CompositorThreadHolder::CompositorThreadHolderSetup",
     84          []() {
     85            sProfilerThreadId = profiler_current_thread_id();
     86            sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor(
     87                "Compositor",
     88                /* Timeout values are powers-of-two to enable us get better
     89                   data. 128ms is chosen for transient hangs because 8Hz should
     90                   be the minimally acceptable goal for Compositor
     91                   responsiveness (normal goal is 60Hz). */
     92                128,
     93                /* 2048ms is chosen for permanent hangs because it's longer than
     94                 * most Compositor hangs seen in the wild, but is short enough
     95                 * to not miss getting native hang stacks. */
     96                2048);
     97            nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
     98            static_cast<nsThread*>(thread.get())->SetUseHangMonitor(true);
     99          }),
    100      {.stackSize = stackSize});
    101 
    102  if (NS_FAILED(rv)) {
    103    return nullptr;
    104  }
    105 
    106  ImageBridgeParent::Setup();
    107 
    108  return compositorThread.forget();
    109 }
    110 
    111 void CompositorThreadHolder::Start() {
    112  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
    113  MOZ_ASSERT(!sCompositorThreadHolder,
    114             "The compositor thread has already been started!");
    115 
    116  // We unset the holder instead of asserting because failing to start the
    117  // compositor thread may not be a fatal error. As long as this succeeds in
    118  // either the GPU process or the UI process, the user will have a usable
    119  // browser. If we get neither, it will crash as soon as we try to post to the
    120  // compositor thread for the first time.
    121  sProfilerThreadId = ProfilerThreadId();
    122  sCompositorThreadHolder = new CompositorThreadHolder();
    123  if (!sCompositorThreadHolder->GetCompositorThread()) {
    124    gfxCriticalNote << "Compositor thread not started ("
    125                    << XRE_IsParentProcess() << ")";
    126    sCompositorThreadHolder = nullptr;
    127  }
    128 }
    129 
    130 void CompositorThreadHolder::Shutdown() {
    131  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
    132  if (!sCompositorThreadHolder) {
    133    // We've already shutdown or never started.
    134    return;
    135  }
    136 
    137  ImageBridgeParent::Shutdown();
    138  gfx::VRManagerParent::Shutdown();
    139  MediaSystemResourceService::Shutdown();
    140  CompositorManagerParent::Shutdown();
    141  gfx::gfxGradientCache::Shutdown();
    142 
    143  // Ensure there are no pending tasks that would cause an access to the
    144  // thread's HangMonitor. APZ and Canvas can keep a reference to the compositor
    145  // thread and may continue to dispatch tasks on it as the system shuts down.
    146  CompositorThread()->Dispatch(NS_NewRunnableFunction(
    147      "CompositorThreadHolder::Shutdown",
    148      [compositorThreadHolder =
    149           RefPtr<CompositorThreadHolder>(sCompositorThreadHolder),
    150       backgroundHangMonitor = UniquePtr<mozilla::BackgroundHangMonitor>(
    151           sBackgroundHangMonitor)]() {
    152        VideoBridgeParent::UnregisterExternalImages();
    153        nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
    154        static_cast<nsThread*>(thread.get())->SetUseHangMonitor(false);
    155      }));
    156 
    157  sCompositorThreadHolder = nullptr;
    158  sBackgroundHangMonitor = nullptr;
    159 
    160  SpinEventLoopUntil("CompositorThreadHolder::Shutdown"_ns, [&]() {
    161    bool finished = sFinishedCompositorShutDown;
    162    return finished;
    163  });
    164 
    165  // At this point, the CompositorThreadHolder instance will have been
    166  // destroyed, but the compositor thread itself may still be running due to
    167  // APZ/Canvas code holding a reference to the underlying
    168  // nsIThread/nsISerialEventTarget. Any tasks scheduled to run on the
    169  // compositor thread earlier in this function will have been run to
    170  // completion.
    171  CompositorBridgeParent::FinishShutdown();
    172 }
    173 
    174 /* static */
    175 bool CompositorThreadHolder::IsInCompositorThread() {
    176  if (!CompositorThread()) {
    177    // We no longer have a pointer to the compositor thread, but we might
    178    // still be running in the compositor thread. In we ever had a
    179    // compositor thread, we logged the thread id in sProfilerThreadId,
    180    // so we compare that value against the current thread id.
    181    return (sProfilerThreadId == profiler_current_thread_id());
    182  }
    183  bool in = false;
    184  MOZ_ALWAYS_SUCCEEDS(CompositorThread()->IsOnCurrentThread(&in));
    185  return in;
    186 }
    187 
    188 /* static */
    189 ProfilerThreadId CompositorThreadHolder::GetThreadId() {
    190  return sCompositorThreadHolder ? sProfilerThreadId : ProfilerThreadId();
    191 }
    192 
    193 }  // namespace layers
    194 }  // namespace mozilla
    195 
    196 bool NS_IsInCompositorThread() {
    197  return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
    198 }