Bug 1506291 - Move the AV1 decoders to a sandbox-friendly CPU counting wrapper. r=gcp,mjf
authorJed Davis <jld@mozilla.com>
Mon, 25 Feb 2019 16:20:50 +0000
changeset 519404 bf58d8320f5a1de358b930d996615c73ff22cce9
parent 519403 94cb1fe9db5eb0f0aa0634541afb08af17cf5c05
child 519405 493b443954fe15f7b542ba14671f25e5f8531dff
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp, mjf
bugs1506291
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1506291 - Move the AV1 decoders to a sandbox-friendly CPU counting wrapper. r=gcp,mjf Counting CPUs accesses the filesystem (sysfs or procfs), which we'd like to disallow when sandboxed if possible, and fails silently if access is denied. Because the CPU count rarely changes, this patch handles that problem for the RDD process by caching a copy before starting sandboxing. Tested with a local patch to have the sandbox file broker client crash if accessing the sysfs node for the CPU count, to verify that it's not accessed. Depends on D14524 Differential Revision: https://phabricator.services.mozilla.com/D20895
dom/media/platforms/agnostic/AOMDecoder.cpp
dom/media/platforms/agnostic/DAV1DDecoder.cpp
toolkit/xre/nsEmbedFunctions.cpp
xpcom/threads/nsThreadUtils.cpp
xpcom/threads/nsThreadUtils.h
--- a/dom/media/platforms/agnostic/AOMDecoder.cpp
+++ b/dom/media/platforms/agnostic/AOMDecoder.cpp
@@ -10,16 +10,17 @@
 #include "aom/aomdx.h"
 #include "aom/aom_image.h"
 #include "gfx2DGlue.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/SyncRunnable.h"
 #include "nsError.h"
 #include "prsystem.h"
 #include "ImageContainer.h"
+#include "nsThreadUtils.h"
 
 #include <algorithm>
 
 #undef LOG
 #define LOG(arg, ...)                                                  \
   DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, __func__, \
             ##__VA_ARGS__)
 #define LOG_RESULT(code, message, ...)                                        \
@@ -42,27 +43,27 @@ using namespace layers;
 static MediaResult InitContext(AOMDecoder& aAOMDecoder, aom_codec_ctx_t* aCtx,
                                const VideoInfo& aInfo) {
   aom_codec_iface_t* dx = aom_codec_av1_dx();
   if (!dx) {
     return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                        RESULT_DETAIL("Couldn't get AV1 decoder interface."));
   }
 
-  int decode_threads = 2;
+  size_t decode_threads = 2;
   if (aInfo.mDisplay.width >= 2048) {
     decode_threads = 8;
   } else if (aInfo.mDisplay.width >= 1024) {
     decode_threads = 4;
   }
-  decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors());
+  decode_threads = std::min(decode_threads, GetNumberOfProcessors());
 
   aom_codec_dec_cfg_t config;
   PodZero(&config);
-  config.threads = decode_threads;
+  config.threads = static_cast<unsigned int>(decode_threads);
   config.w = config.h = 0;  // set after decode
   config.allow_lowbitdepth = true;
 
   aom_codec_flags_t flags = 0;
 
   auto res = aom_codec_dec_init(aCtx, dx, &config, flags);
   if (res != AOM_CODEC_OK) {
     LOGEX_RESULT(&aAOMDecoder, res, "Codec initialization failed, res=%d",
--- a/dom/media/platforms/agnostic/DAV1DDecoder.cpp
+++ b/dom/media/platforms/agnostic/DAV1DDecoder.cpp
@@ -1,39 +1,41 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DAV1DDecoder.h"
 
+#include "nsThreadUtils.h"
+
 #undef LOG
 #define LOG(arg, ...)                                                  \
   DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, __func__, \
             ##__VA_ARGS__)
 
 namespace mozilla {
 
 DAV1DDecoder::DAV1DDecoder(const CreateDecoderParams& aParams)
     : mInfo(aParams.VideoConfig()),
       mTaskQueue(aParams.mTaskQueue),
       mImageContainer(aParams.mImageContainer) {}
 
 RefPtr<MediaDataDecoder::InitPromise> DAV1DDecoder::Init() {
   Dav1dSettings settings;
   dav1d_default_settings(&settings);
-  int decoder_threads = 2;
+  size_t decoder_threads = 2;
   if (mInfo.mDisplay.width >= 2048) {
     decoder_threads = 8;
   } else if (mInfo.mDisplay.width >= 1024) {
     decoder_threads = 4;
   }
   settings.n_frame_threads =
-      std::min(decoder_threads, PR_GetNumberOfProcessors());
+      static_cast<int>(std::min(decoder_threads, GetNumberOfProcessors()));
 
   int res = dav1d_open(&mContext, &settings);
   if (res < 0) {
     return DAV1DDecoder::InitPromise::CreateAndReject(
         MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                     RESULT_DETAIL("Couldn't get dAV1d decoder interface.")),
         __func__);
   }
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -353,16 +353,19 @@ nsresult XRE_InitChildProcess(int aArgc,
     }
     setASanReporterPath(asanReporterPath);
   }
 #endif
 
 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
   // This has to happen before glib thread pools are started.
   mozilla::SandboxEarlyInit();
+  // This just needs to happen before sandboxing, to initialize the
+  // cached value, but libmozsandbox can't see this symbol.
+  mozilla::GetNumberOfProcessors();
 #endif
 
 #ifdef MOZ_JPROF
   // Call the code to install our handler
   setupProfilingStuff();
 #endif
 
 #if defined(XP_WIN)
--- a/xpcom/threads/nsThreadUtils.cpp
+++ b/xpcom/threads/nsThreadUtils.cpp
@@ -7,16 +7,17 @@
 #include "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 #include "mozilla/TimeStamp.h"
 #include "LeakRefPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsExceptionHandler.h"
 #include "nsITimer.h"
+#include "prsystem.h"
 
 #ifdef MOZILLA_INTERNAL_API
 #  include "nsThreadManager.h"
 #else
 #  include "nsXPCOMCIDInternal.h"
 #  include "nsIThreadManager.h"
 #  include "nsServiceManagerUtils.h"
 #endif
@@ -565,16 +566,26 @@ nsISerialEventTarget* GetMainThreadSeria
   nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
   if (NS_FAILED(rv)) {
     return nullptr;
   }
 
   return thread->SerialEventTarget();
 }
 
+size_t GetNumberOfProcessors() {
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+  static const PRInt32 procs = PR_GetNumberOfProcessors();
+#else
+  PRInt32 procs = PR_GetNumberOfProcessors();
+#endif
+  MOZ_ASSERT(procs > 0);
+  return static_cast<size_t>(procs);
+}
+
 }  // namespace mozilla
 
 bool nsIEventTarget::IsOnCurrentThread() {
   if (mVirtualThread) {
     return mVirtualThread == GetCurrentVirtualThread();
   }
   return IsOnCurrentThreadInfallible();
 }
--- a/xpcom/threads/nsThreadUtils.h
+++ b/xpcom/threads/nsThreadUtils.h
@@ -1747,11 +1747,18 @@ nsIEventTarget* GetMainThreadEventTarget
 // These variants of the above functions assert that the given thread has a
 // serial event target (i.e., that it's not part of a thread pool) and returns
 // that.
 
 nsISerialEventTarget* GetCurrentThreadSerialEventTarget();
 
 nsISerialEventTarget* GetMainThreadSerialEventTarget();
 
+// Returns the number of CPUs, like PR_GetNumberOfProcessors, except
+// that it can return a cached value on platforms where sandboxing
+// would prevent reading the current value (currently Linux).  CPU
+// hotplugging is uncommon, so this is unlikely to make a difference
+// in practice.
+size_t GetNumberOfProcessors();
+
 }  // namespace mozilla
 
 #endif  // nsThreadUtils_h__