LCOV - code coverage report
Current view: top level - xpcom/threads - SchedulerGroup.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 141 162 87.0 %
Date: 2017-07-14 16:53:18 Functions: 31 36 86.1 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
       7             : #include "mozilla/SchedulerGroup.h"
       8             : 
       9             : #include "jsfriendapi.h"
      10             : #include "mozilla/AbstractThread.h"
      11             : #include "mozilla/Atomics.h"
      12             : #include "mozilla/Move.h"
      13             : #include "nsINamed.h"
      14             : #include "nsQueryObject.h"
      15             : #include "mozilla/dom/ScriptSettings.h"
      16             : #include "nsThreadUtils.h"
      17             : 
      18             : #include "mozilla/Telemetry.h"
      19             : 
      20             : using namespace mozilla;
      21             : 
      22             : /* SchedulerEventTarget */
      23             : 
      24             : namespace {
      25             : 
      26             : #define NS_DISPATCHEREVENTTARGET_IID \
      27             : { 0xbf4e36c8, 0x7d04, 0x4ef4, \
      28             :   { 0xbb, 0xd8, 0x11, 0x09, 0x0a, 0xdb, 0x4d, 0xf7 } }
      29             : 
      30             : class SchedulerEventTarget final : public nsISerialEventTarget
      31             : {
      32             :   RefPtr<SchedulerGroup> mDispatcher;
      33             :   TaskCategory mCategory;
      34             : 
      35             : public:
      36             :   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DISPATCHEREVENTTARGET_IID)
      37             : 
      38          40 :   SchedulerEventTarget(SchedulerGroup* aDispatcher, TaskCategory aCategory)
      39          40 :    : mDispatcher(aDispatcher)
      40          40 :    , mCategory(aCategory)
      41          40 :   {}
      42             : 
      43             :   NS_DECL_THREADSAFE_ISUPPORTS
      44             :   NS_DECL_NSIEVENTTARGET_FULL
      45             : 
      46           1 :   SchedulerGroup* Dispatcher() const { return mDispatcher; }
      47             : 
      48             : private:
      49           0 :   ~SchedulerEventTarget() {}
      50             : };
      51             : 
      52             : NS_DEFINE_STATIC_IID_ACCESSOR(SchedulerEventTarget, NS_DISPATCHEREVENTTARGET_IID)
      53             : 
      54             : static Atomic<uint64_t> gEarliestUnprocessedVsync(0);
      55             : 
      56             : class MOZ_RAII AutoCollectVsyncTelemetry final
      57             : {
      58             : public:
      59         129 :   explicit AutoCollectVsyncTelemetry(SchedulerGroup::Runnable* aRunnable
      60             :                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
      61         129 :     : mIsBackground(aRunnable->IsBackground())
      62             :   {
      63         129 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
      64             : #ifdef EARLY_BETA_OR_EARLIER
      65         129 :     aRunnable->GetName(mKey);
      66         129 :     mStart = TimeStamp::Now();
      67             : #endif
      68         129 :   }
      69         129 :   ~AutoCollectVsyncTelemetry()
      70         129 :   {
      71             : #ifdef EARLY_BETA_OR_EARLIER
      72         129 :     if (Telemetry::CanRecordBase()) {
      73         129 :       CollectTelemetry();
      74             :     }
      75             : #endif
      76         129 :   }
      77             : 
      78             : private:
      79             :   void CollectTelemetry();
      80             : 
      81             :   bool mIsBackground;
      82             :   nsCString mKey;
      83             :   TimeStamp mStart;
      84             :   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
      85             : };
      86             : 
      87             : void
      88         129 : AutoCollectVsyncTelemetry::CollectTelemetry()
      89             : {
      90         129 :   TimeStamp now = TimeStamp::Now();
      91             : 
      92             :   mozilla::Telemetry::HistogramID eventsId =
      93         129 :     mIsBackground ? Telemetry::CONTENT_JS_BACKGROUND_TICK_DELAY_EVENTS_MS
      94         129 :                   : Telemetry::CONTENT_JS_FOREGROUND_TICK_DELAY_EVENTS_MS;
      95             :   mozilla::Telemetry::HistogramID totalId =
      96         129 :     mIsBackground ? Telemetry::CONTENT_JS_BACKGROUND_TICK_DELAY_TOTAL_MS
      97         129 :                   : Telemetry::CONTENT_JS_FOREGROUND_TICK_DELAY_TOTAL_MS;
      98             : 
      99         129 :   uint64_t lastSeenVsync = gEarliestUnprocessedVsync;
     100         129 :   if (!lastSeenVsync) {
     101         127 :     return;
     102             :   }
     103             : 
     104           2 :   bool inconsistent = false;
     105           2 :   TimeStamp creation = TimeStamp::ProcessCreation(&inconsistent);
     106           2 :   if (inconsistent) {
     107           0 :     return;
     108             :   }
     109             : 
     110             :   TimeStamp pendingVsync =
     111           2 :     creation + TimeDuration::FromMicroseconds(lastSeenVsync);
     112             : 
     113           2 :   if (pendingVsync > now) {
     114           0 :     return;
     115             :   }
     116             : 
     117             :   uint32_t duration =
     118           2 :     static_cast<uint32_t>((now - pendingVsync).ToMilliseconds());
     119             : 
     120           2 :   Telemetry::Accumulate(eventsId, mKey, duration);
     121           2 :   Telemetry::Accumulate(totalId, duration);
     122             : 
     123           2 :   if (pendingVsync > mStart) {
     124           1 :     return;
     125             :   }
     126             : 
     127           1 :   Telemetry::Accumulate(Telemetry::CONTENT_JS_KNOWN_TICK_DELAY_MS, duration);
     128             : 
     129           1 :   return;
     130             : }
     131             : 
     132             : } // namespace
     133             : 
     134        1522 : NS_IMPL_ISUPPORTS(SchedulerEventTarget,
     135             :                   SchedulerEventTarget,
     136             :                   nsIEventTarget,
     137             :                   nsISerialEventTarget)
     138             : 
     139             : NS_IMETHODIMP
     140           0 : SchedulerEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
     141             : {
     142           0 :   return Dispatch(do_AddRef(aRunnable), aFlags);
     143             : }
     144             : 
     145             : NS_IMETHODIMP
     146         157 : SchedulerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
     147             : {
     148         157 :   if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
     149           0 :     return NS_ERROR_UNEXPECTED;
     150             :   }
     151         157 :   return mDispatcher->Dispatch(nullptr, mCategory, Move(aRunnable));
     152             : }
     153             : 
     154             : NS_IMETHODIMP
     155           0 : SchedulerEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
     156             : {
     157           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     158             : }
     159             : 
     160             : NS_IMETHODIMP
     161          91 : SchedulerEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
     162             : {
     163          91 :   *aIsOnCurrentThread = NS_IsMainThread();
     164          91 :   return NS_OK;
     165             : }
     166             : 
     167             : NS_IMETHODIMP_(bool)
     168           0 : SchedulerEventTarget::IsOnCurrentThreadInfallible()
     169             : {
     170           0 :   return NS_IsMainThread();
     171             : }
     172             : 
     173             : /* static */ nsresult
     174         430 : SchedulerGroup::UnlabeledDispatch(const char* aName,
     175             :                                   TaskCategory aCategory,
     176             :                                   already_AddRefed<nsIRunnable>&& aRunnable)
     177             : {
     178         860 :   nsCOMPtr<nsIRunnable> runnable(aRunnable);
     179         430 :   if (aName) {
     180         546 :     if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
     181         273 :       named->SetName(aName);
     182             :     }
     183             :   }
     184         430 :   if (NS_IsMainThread()) {
     185         263 :     return NS_DispatchToCurrentThread(runnable.forget());
     186             :   } else {
     187         167 :     return NS_DispatchToMainThread(runnable.forget());
     188             :   }
     189             : }
     190             : 
     191             : /* static */ void
     192          57 : SchedulerGroup::MarkVsyncReceived()
     193             : {
     194          57 :   if (gEarliestUnprocessedVsync) {
     195             :     // If we've seen a vsync already, but haven't handled it, keep the
     196             :     // older one.
     197         106 :     return;
     198             :   }
     199             : 
     200           4 :   MOZ_ASSERT(!NS_IsMainThread());
     201           4 :   bool inconsistent = false;
     202           4 :   TimeStamp creation = TimeStamp::ProcessCreation(&inconsistent);
     203           4 :   if (inconsistent) {
     204           0 :     return;
     205             :   }
     206             : 
     207           4 :   gEarliestUnprocessedVsync = (TimeStamp::Now() - creation).ToMicroseconds();
     208             : }
     209             : 
     210             : /* static */ void
     211           4 : SchedulerGroup::MarkVsyncRan()
     212             : {
     213           4 :   gEarliestUnprocessedVsync = 0;
     214           4 : }
     215             : 
     216             : SchedulerGroup* SchedulerGroup::sRunningDispatcher;
     217             : 
     218           6 : SchedulerGroup::SchedulerGroup()
     219           6 :  : mAccessValid(false)
     220             : {
     221           6 : }
     222             : 
     223             : nsresult
     224         300 : SchedulerGroup::Dispatch(const char* aName,
     225             :                          TaskCategory aCategory,
     226             :                          already_AddRefed<nsIRunnable>&& aRunnable)
     227             : {
     228         300 :   return LabeledDispatch(aName, aCategory, Move(aRunnable));
     229             : }
     230             : 
     231             : nsISerialEventTarget*
     232         262 : SchedulerGroup::EventTargetFor(TaskCategory aCategory) const
     233             : {
     234         262 :   MOZ_ASSERT(aCategory != TaskCategory::Count);
     235         262 :   MOZ_ASSERT(mEventTargets[size_t(aCategory)]);
     236         262 :   return mEventTargets[size_t(aCategory)];
     237             : }
     238             : 
     239             : AbstractThread*
     240           2 : SchedulerGroup::AbstractMainThreadFor(TaskCategory aCategory)
     241             : {
     242           2 :   MOZ_RELEASE_ASSERT(NS_IsMainThread());
     243           2 :   return AbstractMainThreadForImpl(aCategory);
     244             : }
     245             : 
     246             : AbstractThread*
     247           1 : SchedulerGroup::AbstractMainThreadForImpl(TaskCategory aCategory)
     248             : {
     249           1 :   MOZ_RELEASE_ASSERT(NS_IsMainThread());
     250           1 :   MOZ_ASSERT(aCategory != TaskCategory::Count);
     251           1 :   MOZ_ASSERT(mEventTargets[size_t(aCategory)]);
     252             : 
     253           1 :   if (!mAbstractThreads[size_t(aCategory)]) {
     254           1 :     mAbstractThreads[size_t(aCategory)] =
     255           3 :       AbstractThread::CreateEventTargetWrapper(mEventTargets[size_t(aCategory)],
     256           2 :                                                /* aDrainDirectTasks = */ true);
     257             :   }
     258             : 
     259           1 :   return mAbstractThreads[size_t(aCategory)];
     260             : }
     261             : 
     262             : void
     263           6 : SchedulerGroup::CreateEventTargets(bool aNeedValidation)
     264             : {
     265          54 :   for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
     266          48 :     TaskCategory category = static_cast<TaskCategory>(i);
     267          48 :     if (!aNeedValidation) {
     268             :       // The chrome TabGroup dispatches directly to the main thread. This means
     269             :       // that we don't have to worry about cyclical references when cleaning up
     270             :       // the chrome TabGroup.
     271           8 :       mEventTargets[i] = GetMainThreadSerialEventTarget();
     272             :     } else {
     273          40 :       mEventTargets[i] = CreateEventTargetFor(category);
     274             :     }
     275             :   }
     276           6 : }
     277             : 
     278             : void
     279           0 : SchedulerGroup::Shutdown(bool aXPCOMShutdown)
     280             : {
     281             :   // There is a RefPtr cycle TabGroup -> SchedulerEventTarget -> TabGroup. To
     282             :   // avoid leaks, we need to break the chain somewhere. We shouldn't be using
     283             :   // the ThrottledEventQueue for this TabGroup when no windows belong to it,
     284             :   // so it's safe to null out the queue here.
     285           0 :   for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
     286           0 :     mEventTargets[i] = aXPCOMShutdown ? nullptr : GetMainThreadSerialEventTarget();
     287           0 :     mAbstractThreads[i] = nullptr;
     288             :   }
     289           0 : }
     290             : 
     291             : already_AddRefed<nsISerialEventTarget>
     292          40 : SchedulerGroup::CreateEventTargetFor(TaskCategory aCategory)
     293             : {
     294             :   RefPtr<SchedulerEventTarget> target =
     295          80 :     new SchedulerEventTarget(this, aCategory);
     296          80 :   return target.forget();
     297             : }
     298             : 
     299             : /* static */ SchedulerGroup*
     300           1 : SchedulerGroup::FromEventTarget(nsIEventTarget* aEventTarget)
     301             : {
     302           2 :   RefPtr<SchedulerEventTarget> target = do_QueryObject(aEventTarget);
     303           1 :   if (!target) {
     304           0 :     return nullptr;
     305             :   }
     306           1 :   return target->Dispatcher();
     307             : }
     308             : 
     309             : nsresult
     310         300 : SchedulerGroup::LabeledDispatch(const char* aName,
     311             :                                 TaskCategory aCategory,
     312             :                                 already_AddRefed<nsIRunnable>&& aRunnable)
     313             : {
     314         600 :   nsCOMPtr<nsIRunnable> runnable(aRunnable);
     315         300 :   if (XRE_IsContentProcess()) {
     316         270 :     runnable = new Runnable(runnable.forget(), this);
     317             :   }
     318         600 :   return UnlabeledDispatch(aName, aCategory, runnable.forget());
     319             : }
     320             : 
     321             : void
     322         258 : SchedulerGroup::SetValidatingAccess(ValidationType aType)
     323             : {
     324         258 :   sRunningDispatcher = aType == StartValidation ? this : nullptr;
     325         258 :   mAccessValid = aType == StartValidation;
     326             : 
     327         516 :   dom::AutoJSAPI jsapi;
     328         258 :   jsapi.Init();
     329         258 :   js::EnableAccessValidation(jsapi.cx(), !!sRunningDispatcher);
     330         258 : }
     331             : 
     332         135 : SchedulerGroup::Runnable::Runnable(already_AddRefed<nsIRunnable>&& aRunnable,
     333         135 :                                    SchedulerGroup* aGroup)
     334             :   : mozilla::Runnable("SchedulerGroup::Runnable")
     335         135 :   , mRunnable(Move(aRunnable))
     336         270 :   , mGroup(aGroup)
     337             : {
     338         135 : }
     339             : 
     340             : NS_IMETHODIMP
     341         258 : SchedulerGroup::Runnable::GetName(nsACString& aName)
     342             : {
     343             :   // Try to get a name from the underlying runnable.
     344         516 :   nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable);
     345         258 :   if (named) {
     346         258 :     named->GetName(aName);
     347             :   }
     348         258 :   if (aName.IsEmpty()) {
     349           0 :     aName.AssignLiteral("anonymous");
     350             :   }
     351             : 
     352         258 :   aName.AppendASCII("(labeled)");
     353         516 :   return NS_OK;
     354             : }
     355             : 
     356             : NS_IMETHODIMP
     357         129 : SchedulerGroup::Runnable::Run()
     358             : {
     359         129 :   MOZ_RELEASE_ASSERT(NS_IsMainThread());
     360             : 
     361         129 :   mGroup->SetValidatingAccess(StartValidation);
     362             : 
     363             :   nsresult result;
     364             : 
     365             :   {
     366         258 :     AutoCollectVsyncTelemetry telemetry(this);
     367         129 :     result = mRunnable->Run();
     368             :   }
     369             : 
     370             :   // The runnable's destructor can have side effects, so try to execute it in
     371             :   // the scope of the TabGroup.
     372         129 :   mRunnable = nullptr;
     373             : 
     374         129 :   mGroup->SetValidatingAccess(EndValidation);
     375         129 :   return result;
     376             : }
     377             : 
     378        2175 : NS_IMPL_ISUPPORTS_INHERITED(SchedulerGroup::Runnable,
     379             :                             mozilla::Runnable,
     380             :                             SchedulerGroup::Runnable)
     381             : 
     382        1230 : SchedulerGroup::AutoProcessEvent::AutoProcessEvent()
     383        1230 :  : mPrevRunningDispatcher(SchedulerGroup::sRunningDispatcher)
     384             : {
     385        1230 :   SchedulerGroup* prev = sRunningDispatcher;
     386        1230 :   if (prev) {
     387           0 :     MOZ_ASSERT(prev->mAccessValid);
     388           0 :     prev->SetValidatingAccess(EndValidation);
     389             :   }
     390        1230 : }
     391             : 
     392        2454 : SchedulerGroup::AutoProcessEvent::~AutoProcessEvent()
     393             : {
     394        1227 :   MOZ_ASSERT(!sRunningDispatcher);
     395             : 
     396        1227 :   SchedulerGroup* prev = mPrevRunningDispatcher;
     397        1227 :   if (prev) {
     398           0 :     prev->SetValidatingAccess(StartValidation);
     399             :   }
     400        1227 : }

Generated by: LCOV version 1.13