tor-browser

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

RDDProcessManager.cpp (12516B)


      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 "RDDProcessManager.h"
      7 
      8 #include "RDDChild.h"
      9 #include "RDDProcessHost.h"
     10 #include "mozilla/MemoryReportingProcess.h"
     11 #include "mozilla/Preferences.h"
     12 #include "mozilla/RemoteMediaManagerChild.h"
     13 #include "mozilla/RemoteMediaManagerParent.h"
     14 #include "mozilla/StaticPrefs_media.h"
     15 #include "mozilla/SyncRunnable.h"  // for LaunchRDDProcess
     16 #include "mozilla/dom/ContentParent.h"
     17 #include "mozilla/gfx/GPUProcessManager.h"
     18 #include "mozilla/ipc/Endpoint.h"
     19 #include "mozilla/ipc/ProcessChild.h"
     20 #include "mozilla/layers/CompositorThread.h"
     21 #include "mozilla/layers/VideoBridgeParent.h"
     22 #include "nsAppRunner.h"
     23 #include "nsContentUtils.h"
     24 
     25 namespace mozilla {
     26 
     27 using namespace gfx;
     28 using namespace layers;
     29 
     30 static StaticAutoPtr<RDDProcessManager> sRDDSingleton;
     31 
     32 static bool sRDDProcessShutdown = false;
     33 
     34 bool RDDProcessManager::IsShutdown() const {
     35  MOZ_ASSERT(NS_IsMainThread());
     36  return sRDDProcessShutdown || !sRDDSingleton;
     37 }
     38 
     39 RDDProcessManager* RDDProcessManager::Get() { return sRDDSingleton; }
     40 
     41 void RDDProcessManager::Initialize() {
     42  MOZ_ASSERT(XRE_IsParentProcess());
     43  sRDDSingleton = new RDDProcessManager();
     44 }
     45 
     46 void RDDProcessManager::Shutdown() { sRDDSingleton = nullptr; }
     47 
     48 void RDDProcessManager::RDDProcessShutdown() {
     49  MOZ_ASSERT(NS_IsMainThread());
     50  sRDDProcessShutdown = true;
     51  if (sRDDSingleton) {
     52    sRDDSingleton->DestroyProcess();
     53  }
     54 }
     55 
     56 RDDProcessManager::RDDProcessManager()
     57    : mObserver(new Observer(this)), mTaskFactory(this) {
     58  MOZ_COUNT_CTOR(RDDProcessManager);
     59  // Start listening for pref changes so we can
     60  // forward them to the process once it is running.
     61  nsContentUtils::RegisterShutdownObserver(mObserver);
     62  Preferences::AddStrongObserver(mObserver, "");
     63 }
     64 
     65 RDDProcessManager::~RDDProcessManager() {
     66  MOZ_COUNT_DTOR(RDDProcessManager);
     67  MOZ_ASSERT(NS_IsMainThread());
     68 
     69  // The RDD process should have already been shut down.
     70  MOZ_ASSERT(!mProcess && !mRDDChild);
     71 }
     72 
     73 NS_IMPL_ISUPPORTS(RDDProcessManager::Observer, nsIObserver);
     74 
     75 RDDProcessManager::Observer::Observer(RDDProcessManager* aManager)
     76    : mManager(aManager) {}
     77 
     78 NS_IMETHODIMP
     79 RDDProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
     80                                     const char16_t* aData) {
     81  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     82    mManager->OnXPCOMShutdown();
     83  } else if (!strcmp(aTopic, "nsPref:changed")) {
     84    mManager->OnPreferenceChange(aData);
     85  }
     86  return NS_OK;
     87 }
     88 
     89 void RDDProcessManager::OnXPCOMShutdown() {
     90  MOZ_ASSERT(NS_IsMainThread());
     91  nsContentUtils::UnregisterShutdownObserver(mObserver);
     92  Preferences::RemoveObserver(mObserver, "");
     93 }
     94 
     95 void RDDProcessManager::OnPreferenceChange(const char16_t* aData) {
     96  MOZ_ASSERT(NS_IsMainThread());
     97  if (!mProcess) {
     98    // Process hasn't been launched yet
     99    return;
    100  }
    101 
    102  // We know prefs are ASCII here.
    103  NS_LossyConvertUTF16toASCII strData(aData);
    104 
    105  mozilla::dom::Pref pref(strData, /* isLocked */ false,
    106                          /* isSanitized */ false, Nothing(), Nothing());
    107 
    108  Preferences::GetPreference(&pref, GeckoProcessType_RDD,
    109                             /* remoteType */ ""_ns);
    110  if (!!mRDDChild) {
    111    MOZ_ASSERT(mQueuedPrefs.IsEmpty());
    112    mRDDChild->SendPreferenceUpdate(pref);
    113  } else if (IsRDDProcessLaunching()) {
    114    mQueuedPrefs.AppendElement(pref);
    115  }
    116 }
    117 
    118 RefPtr<GenericNonExclusivePromise> RDDProcessManager::LaunchRDDProcess() {
    119  MOZ_ASSERT(NS_IsMainThread());
    120 
    121  if (IsShutdown()) {
    122    return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
    123                                                       __func__);
    124  }
    125 
    126  if (mNumProcessAttempts && !StaticPrefs::media_rdd_retryonfailure_enabled()) {
    127    // We failed to start the RDD process earlier, abort now.
    128    return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
    129                                                       __func__);
    130  }
    131 
    132  if (mProcess) {
    133    MOZ_DIAGNOSTIC_ASSERT(mLaunchRDDPromise);
    134    return mLaunchRDDPromise;
    135  }
    136 
    137  geckoargs::ChildProcessArgs extraArgs;
    138  ipc::ProcessChild::AddPlatformBuildID(extraArgs);
    139 
    140  // The subprocess is launched asynchronously, so we
    141  // wait for the promise to be resolved to acquire the IPDL actor.
    142  mProcess = new RDDProcessHost(this);
    143  if (!mProcess->Launch(std::move(extraArgs))) {
    144    mNumProcessAttempts++;
    145    DestroyProcess();
    146    return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
    147                                                       __func__);
    148  }
    149 
    150  mLaunchRDDPromise = mProcess->LaunchPromise()->Then(
    151      GetMainThreadSerialEventTarget(), __func__,
    152      [this](bool) {
    153        if (IsShutdown()) {
    154          return GenericNonExclusivePromise::CreateAndReject(
    155              NS_ERROR_NOT_AVAILABLE, __func__);
    156        }
    157 
    158        if (NS_WARN_IF(!IsRDDProcessLaunching())) {
    159          return GenericNonExclusivePromise::CreateAndReject(
    160              NS_ERROR_NOT_AVAILABLE, __func__);
    161        }
    162 
    163        mRDDChild = mProcess->GetActor();
    164        mProcessToken = mProcess->GetProcessToken();
    165 
    166        // Flush any pref updates that happened during
    167        // launch and weren't included in the blobs set
    168        // up in LaunchRDDProcess.
    169        for (const mozilla::dom::Pref& pref : mQueuedPrefs) {
    170          (void)NS_WARN_IF(!mRDDChild->SendPreferenceUpdate(pref));
    171        }
    172        mQueuedPrefs.Clear();
    173 
    174        CrashReporter::RecordAnnotationCString(
    175            CrashReporter::Annotation::RDDProcessStatus, "Running");
    176 
    177        auto* gpm = GPUProcessManager::Get();
    178        if (NS_WARN_IF(!mRDDChild->CanSend()) || NS_WARN_IF(!gpm) ||
    179            NS_WARN_IF(NS_FAILED(gpm->EnsureGPUReady())) ||
    180            NS_WARN_IF(NS_FAILED(gpm->CreateRddVideoBridge(this, mRDDChild)))) {
    181          mNumProcessAttempts++;
    182          DestroyProcess();
    183          return GenericNonExclusivePromise::CreateAndReject(
    184              NS_ERROR_NOT_AVAILABLE, __func__);
    185        }
    186 
    187        gpm->AddListener(mRDDChild);
    188        return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
    189      },
    190      [this](nsresult aError) {
    191        if (Get()) {
    192          mNumProcessAttempts++;
    193          DestroyProcess();
    194        }
    195        return GenericNonExclusivePromise::CreateAndReject(aError, __func__);
    196      });
    197  return mLaunchRDDPromise;
    198 }
    199 
    200 auto RDDProcessManager::EnsureRDDProcessAndCreateBridge(
    201    ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aParentId)
    202    -> RefPtr<EnsureRDDPromise> {
    203  return InvokeAsync(
    204      GetMainThreadSerialEventTarget(), __func__,
    205      [aOtherProcess, aParentId, this]() -> RefPtr<EnsureRDDPromise> {
    206        return LaunchRDDProcess()->Then(
    207            GetMainThreadSerialEventTarget(), __func__,
    208            [aOtherProcess, aParentId, this]() {
    209              if (IsShutdown()) {
    210                return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
    211                                                         __func__);
    212              }
    213              ipc::Endpoint<PRemoteMediaManagerChild> endpoint;
    214              if (!CreateContentBridge(aOtherProcess, aParentId, &endpoint)) {
    215                return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
    216                                                         __func__);
    217              }
    218              mNumProcessAttempts = 0;
    219              return EnsureRDDPromise::CreateAndResolve(std::move(endpoint),
    220                                                        __func__);
    221            },
    222            [](nsresult aError) {
    223              return EnsureRDDPromise::CreateAndReject(aError, __func__);
    224            });
    225      });
    226 }
    227 
    228 bool RDDProcessManager::IsRDDProcessLaunching() const {
    229  MOZ_ASSERT(NS_IsMainThread());
    230  return !!mProcess && !mRDDChild;
    231 }
    232 
    233 bool RDDProcessManager::IsRDDProcessAlive() const {
    234  MOZ_ASSERT(NS_IsMainThread());
    235  return mRDDChild && mRDDChild->CanSend() && mProcess;
    236 }
    237 
    238 void RDDProcessManager::OnProcessUnexpectedShutdown(RDDProcessHost* aHost) {
    239  MOZ_ASSERT(NS_IsMainThread());
    240  MOZ_ASSERT(mProcess && mProcess == aHost);
    241 
    242  mNumUnexpectedCrashes++;
    243 
    244  DestroyProcess();
    245 }
    246 
    247 void RDDProcessManager::NotifyRemoteActorDestroyed(
    248    const uint64_t& aProcessToken) {
    249  if (!NS_IsMainThread()) {
    250    RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
    251        &RDDProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
    252    NS_DispatchToMainThread(task.forget());
    253    return;
    254  }
    255 
    256  if (mProcessToken != aProcessToken) {
    257    // This token is for an older process; we can safely ignore it.
    258    return;
    259  }
    260 
    261  // One of the bridged top-level actors for the RDD process has been
    262  // prematurely terminated, and we're receiving a notification. This
    263  // can happen if the ActorDestroy for a bridged protocol fires
    264  // before the ActorDestroy for PRDDChild.
    265  OnProcessUnexpectedShutdown(mProcess);
    266 }
    267 
    268 void RDDProcessManager::DestroyProcess() {
    269  MOZ_ASSERT(NS_IsMainThread());
    270 
    271  if (!mProcess) {
    272    return;
    273  }
    274 
    275  // Move onto the stack to ensure we don't re-enter from a chained promise
    276  // rejection on the process shutdown.
    277  RDDProcessHost* process = mProcess;
    278  mProcess = nullptr;
    279 
    280  process->Shutdown();
    281  mProcessToken = 0;
    282  mRDDChild = nullptr;
    283  mLaunchRDDPromise = nullptr;
    284  mQueuedPrefs.Clear();
    285 
    286  CrashReporter::RecordAnnotationCString(
    287      CrashReporter::Annotation::RDDProcessStatus, "Destroyed");
    288 }
    289 
    290 bool RDDProcessManager::CreateContentBridge(
    291    ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aParentId,
    292    ipc::Endpoint<PRemoteMediaManagerChild>* aOutRemoteMediaManager) {
    293  MOZ_ASSERT(NS_IsMainThread());
    294 
    295  if (NS_WARN_IF(!IsRDDProcessAlive())) {
    296    MOZ_LOG(sPDMLog, LogLevel::Debug,
    297            ("RDD shutdown before creating content bridge"));
    298    return false;
    299  }
    300 
    301  ipc::Endpoint<PRemoteMediaManagerParent> parentPipe;
    302  ipc::Endpoint<PRemoteMediaManagerChild> childPipe;
    303 
    304  nsresult rv = PRemoteMediaManager::CreateEndpoints(
    305      mRDDChild->OtherEndpointProcInfo(), aOtherProcess, &parentPipe,
    306      &childPipe);
    307  if (NS_FAILED(rv)) {
    308    MOZ_LOG(sPDMLog, LogLevel::Debug,
    309            ("Could not create content remote decoder: %d", int(rv)));
    310    return false;
    311  }
    312 
    313  mRDDChild->SendNewContentRemoteMediaManager(std::move(parentPipe), aParentId);
    314 
    315  *aOutRemoteMediaManager = std::move(childPipe);
    316  return true;
    317 }
    318 
    319 base::ProcessId RDDProcessManager::RDDProcessPid() {
    320  MOZ_ASSERT(NS_IsMainThread());
    321  base::ProcessId rddPid =
    322      mRDDChild ? mRDDChild->OtherPid() : base::kInvalidProcessId;
    323  return rddPid;
    324 }
    325 
    326 ipc::EndpointProcInfo RDDProcessManager::RDDEndpointProcInfo() {
    327  MOZ_ASSERT(NS_IsMainThread());
    328  return mRDDChild ? mRDDChild->OtherEndpointProcInfo()
    329                   : ipc::EndpointProcInfo::Invalid();
    330 }
    331 
    332 class RDDMemoryReporter : public MemoryReportingProcess {
    333 public:
    334  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RDDMemoryReporter, override)
    335 
    336  bool IsAlive() const override { return !!GetChild(); }
    337 
    338  bool SendRequestMemoryReport(
    339      const uint32_t& aGeneration, const bool& aAnonymize,
    340      const bool& aMinimizeMemoryUsage,
    341      const Maybe<ipc::FileDescriptor>& aDMDFile) override {
    342    RDDChild* child = GetChild();
    343    if (!child) {
    344      return false;
    345    }
    346 
    347    return child->SendRequestMemoryReport(aGeneration, aAnonymize,
    348                                          aMinimizeMemoryUsage, aDMDFile);
    349  }
    350 
    351  int32_t Pid() const override {
    352    if (RDDChild* child = GetChild()) {
    353      return (int32_t)child->OtherPid();
    354    }
    355    return 0;
    356  }
    357 
    358 private:
    359  RDDChild* GetChild() const {
    360    if (RDDProcessManager* rddpm = RDDProcessManager::Get()) {
    361      if (RDDChild* child = rddpm->GetRDDChild()) {
    362        return child;
    363      }
    364    }
    365    return nullptr;
    366  }
    367 
    368 protected:
    369  ~RDDMemoryReporter() = default;
    370 };
    371 
    372 RefPtr<MemoryReportingProcess> RDDProcessManager::GetProcessMemoryReporter() {
    373  if (!mProcess || !mProcess->IsConnected()) {
    374    return nullptr;
    375  }
    376  return new RDDMemoryReporter();
    377 }
    378 
    379 RefPtr<PRDDChild::TestTriggerMetricsPromise>
    380 RDDProcessManager::TestTriggerMetrics() {
    381  if (!NS_WARN_IF(!mRDDChild)) {
    382    return mRDDChild->SendTestTriggerMetrics();
    383  }
    384 
    385  return PRDDChild::TestTriggerMetricsPromise::CreateAndReject(
    386      ipc::ResponseRejectReason::SendError, __func__);
    387 }
    388 
    389 }  // namespace mozilla