LCOV - code coverage report
Current view: top level - xpcom/threads - AbstractThread.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 95 153 62.1 %
Date: 2017-07-14 16:53:18 Functions: 24 40 60.0 %
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/AbstractThread.h"
       8             : 
       9             : #include "mozilla/ClearOnShutdown.h"
      10             : #include "mozilla/Maybe.h"
      11             : #include "mozilla/MozPromise.h" // We initialize the MozPromise logging in this file.
      12             : #include "mozilla/StaticPtr.h"
      13             : #include "mozilla/StateWatching.h" // We initialize the StateWatching logging in this file.
      14             : #include "mozilla/TaskQueue.h"
      15             : #include "mozilla/TaskDispatcher.h"
      16             : #include "mozilla/Unused.h"
      17             : 
      18             : #include "nsThreadUtils.h"
      19             : #include "nsContentUtils.h"
      20             : #include "nsServiceManagerUtils.h"
      21             : 
      22             : 
      23             : namespace mozilla {
      24             : 
      25             : LazyLogModule gMozPromiseLog("MozPromise");
      26             : LazyLogModule gStateWatchingLog("StateWatching");
      27             : 
      28           3 : StaticRefPtr<AbstractThread> sMainThread;
      29             : MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS;
      30             : 
      31           0 : class EventTargetWrapper : public AbstractThread
      32             : {
      33             : public:
      34           8 :   explicit EventTargetWrapper(nsIEventTarget* aTarget, bool aRequireTailDispatch)
      35           8 :     : AbstractThread(aRequireTailDispatch)
      36           8 :     , mTarget(aTarget)
      37             :   {
      38             :     // Our current mechanism of implementing tail dispatch is appshell-specific.
      39             :     // This is because a very similar mechanism already exists on the main
      40             :     // thread, and we want to avoid making event dispatch on the main thread
      41             :     // more complicated than it already is.
      42             :     //
      43             :     // If you need to use tail dispatch on other XPCOM threads, you'll need to
      44             :     // implement an nsIThreadObserver to fire the tail dispatcher at the
      45             :     // appropriate times.
      46          16 :     nsCOMPtr<nsIThread> thread(do_QueryInterface(aTarget));
      47           8 :     bool isOnCurrentThread = false;
      48           8 :     aTarget->IsOnCurrentThread(&isOnCurrentThread);
      49             : 
      50           8 :     MOZ_ASSERT_IF(aRequireTailDispatch,
      51             :       (thread && NS_IsMainThread() && NS_GetCurrentThread() == thread) ||
      52             :       (!thread && NS_IsMainThread() && isOnCurrentThread));
      53             : 
      54             :     // XXX Bug 1323742:
      55             :     // We hold mRunningThread for IsCurrentThreadIn() for now.
      56             :     // This shall be replaced by this == GetCurrent() in the future in
      57             :     // AbstractThread perspective instead of PR_Thread perspective.
      58           8 :     mRunningThread = thread ? thread.get() : NS_GetCurrentThread();
      59           8 :     MOZ_ASSERT(mRunningThread);
      60           8 :   }
      61             : 
      62           6 :   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
      63             :                         DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
      64             :                         DispatchReason aReason = NormalDispatch) override
      65             :   {
      66             :     AbstractThread* currentThread;
      67           6 :     if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
      68           0 :       currentThread->TailDispatcher().AddTask(this, Move(aRunnable), aFailureHandling);
      69           0 :       return;
      70             :     }
      71             : 
      72          18 :     RefPtr<nsIRunnable> runner(new Runner(this, Move(aRunnable), false /* already drained by TaskGroupRunnable  */));
      73           6 :     nsresult rv = mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
      74           6 :     MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
      75             :     Unused << rv;
      76             :   }
      77             : 
      78             :   // Prevent a GCC warning about the other overload of Dispatch being hidden.
      79             :   using AbstractThread::Dispatch;
      80             : 
      81          15 :   virtual bool IsCurrentThreadIn() override
      82             :   {
      83             :     // Compare NSPR threads so that this works after shutdown when
      84             :     // NS_GetCurrentThread starts returning null.
      85          15 :     PRThread* thread = nullptr;
      86          15 :     mRunningThread->GetPRThread(&thread);
      87          15 :     bool in = PR_GetCurrentThread() == thread;
      88          15 :     return in;
      89             :   }
      90             : 
      91           0 :   void FireTailDispatcher()
      92             :   {
      93           0 :     MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
      94           0 :     mTailDispatcher.ref().DrainDirectTasks();
      95           0 :     mTailDispatcher.reset();
      96           0 :   }
      97             : 
      98           0 :   virtual TaskDispatcher& TailDispatcher() override
      99             :   {
     100             :     // See the comment in the constructor.
     101           0 :     MOZ_ASSERT(mRunningThread ==
     102             :       static_cast<EventTargetWrapper*>(sMainThread.get())->mRunningThread);
     103           0 :     MOZ_ASSERT(IsCurrentThreadIn());
     104           0 :     if (!mTailDispatcher.isSome()) {
     105           0 :       mTailDispatcher.emplace(/* aIsTailDispatcher = */ true);
     106             : 
     107             :       nsCOMPtr<nsIRunnable> event =
     108           0 :         NewRunnableMethod("EventTargetWrapper::FireTailDispatcher",
     109             :                           this,
     110           0 :                           &EventTargetWrapper::FireTailDispatcher);
     111           0 :       nsContentUtils::RunInStableState(event.forget());
     112             :     }
     113             : 
     114           0 :     return mTailDispatcher.ref();
     115             :   }
     116             : 
     117           0 :   virtual bool MightHaveTailTasks() override
     118             :   {
     119           0 :     return mTailDispatcher.isSome();
     120             :   }
     121             : 
     122           0 :   virtual nsIEventTarget* AsEventTarget() override { return mTarget; }
     123             : 
     124             : private:
     125             :   nsCOMPtr<nsIThread> mRunningThread;
     126             :   RefPtr<nsIEventTarget> mTarget;
     127             :   Maybe<AutoTaskDispatcher> mTailDispatcher;
     128             : 
     129             :   virtual already_AddRefed<nsIRunnable>
     130           0 :   CreateDirectTaskDrainer(already_AddRefed<nsIRunnable> aRunnable) override
     131             :   {
     132             :     RefPtr<Runner> runner =
     133           0 :       new Runner(this, Move(aRunnable), /* aDrainDirectTasks */ true);
     134           0 :     return runner.forget();
     135             :   }
     136             : 
     137          18 :   class Runner : public CancelableRunnable {
     138             :     class MOZ_STACK_CLASS AutoTaskGuard final {
     139             :     public:
     140           6 :       explicit AutoTaskGuard(EventTargetWrapper* aThread)
     141           6 :         : mLastCurrentThread(nullptr)
     142             :       {
     143           6 :         MOZ_ASSERT(aThread);
     144           6 :         mLastCurrentThread = sCurrentThreadTLS.get();
     145           6 :         sCurrentThreadTLS.set(aThread);
     146           6 :       }
     147             : 
     148           6 :       ~AutoTaskGuard()
     149           6 :       {
     150           6 :         sCurrentThreadTLS.set(mLastCurrentThread);
     151           6 :       }
     152             :     private:
     153             :       AbstractThread* mLastCurrentThread;
     154             :     };
     155             : 
     156             :   public:
     157           6 :     explicit Runner(EventTargetWrapper* aThread,
     158             :                     already_AddRefed<nsIRunnable> aRunnable,
     159             :                     bool aDrainDirectTasks)
     160           6 :       : CancelableRunnable("EventTargetWrapper::Runner")
     161             :       , mThread(aThread)
     162             :       , mRunnable(aRunnable)
     163           6 :       , mDrainDirectTasks(aDrainDirectTasks)
     164             :     {
     165           6 :     }
     166             : 
     167           6 :     NS_IMETHOD Run() override
     168             :     {
     169          12 :       AutoTaskGuard taskGuard(mThread);
     170             : 
     171           6 :       MOZ_ASSERT(mThread == AbstractThread::GetCurrent());
     172           6 :       MOZ_ASSERT(mThread->IsCurrentThreadIn());
     173           6 :       nsresult rv = mRunnable->Run();
     174             : 
     175           6 :       if (mDrainDirectTasks) {
     176           0 :         mThread->TailDispatcher().DrainDirectTasks();
     177             :       }
     178             : 
     179          12 :       return rv;
     180             :     }
     181             : 
     182           0 :     nsresult Cancel() override
     183             :     {
     184             :       // Set the TLS during Cancel() just in case it calls Run().
     185           0 :       AutoTaskGuard taskGuard(mThread);
     186             : 
     187           0 :       nsresult rv = NS_OK;
     188             : 
     189             :       // Try to cancel the runnable if it implements the right interface.
     190             :       // Otherwise just skip the runnable.
     191           0 :       nsCOMPtr<nsICancelableRunnable> cr = do_QueryInterface(mRunnable);
     192           0 :       if (cr) {
     193           0 :         rv = cr->Cancel();
     194             :       }
     195             : 
     196           0 :       return rv;
     197             :     }
     198             : 
     199           2 :     NS_IMETHOD GetName(nsACString& aName) override
     200             :     {
     201           2 :       aName.AssignLiteral("AbstractThread::Runner");
     202           4 :       if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) {
     203           4 :         nsAutoCString name;
     204           2 :         named->GetName(name);
     205           2 :         if (!name.IsEmpty()) {
     206           2 :           aName.AppendLiteral(" for ");
     207           2 :           aName.Append(name);
     208             :         }
     209             :       }
     210           2 :       return NS_OK;
     211             :     }
     212             : 
     213             :   private:
     214             :     RefPtr<EventTargetWrapper> mThread;
     215             :     RefPtr<nsIRunnable> mRunnable;
     216             :     bool mDrainDirectTasks;
     217             :   };
     218             : };
     219             : 
     220          52 : NS_IMPL_ISUPPORTS(AbstractThread, nsIEventTarget, nsISerialEventTarget)
     221             : 
     222             : NS_IMETHODIMP_(bool)
     223           3 : AbstractThread::IsOnCurrentThreadInfallible()
     224             : {
     225           3 :   return IsCurrentThreadIn();
     226             : }
     227             : 
     228             : NS_IMETHODIMP
     229           0 : AbstractThread::IsOnCurrentThread(bool* aResult)
     230             : {
     231           0 :   *aResult = IsCurrentThreadIn();
     232           0 :   return NS_OK;
     233             : }
     234             : 
     235             : NS_IMETHODIMP
     236           0 : AbstractThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
     237             : {
     238           0 :   nsCOMPtr<nsIRunnable> event(aEvent);
     239           0 :   return Dispatch(event.forget(), aFlags);
     240             : }
     241             : 
     242             : NS_IMETHODIMP
     243           6 : AbstractThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
     244             : {
     245           6 :   Dispatch(Move(aEvent), DontAssertDispatchSuccess, NormalDispatch);
     246           6 :   return NS_OK;
     247             : }
     248             : 
     249             : NS_IMETHODIMP
     250           0 : AbstractThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
     251             :                                  uint32_t aDelayMs)
     252             : {
     253           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     254             : }
     255             : 
     256             : void
     257           0 : AbstractThread::TailDispatchTasksFor(AbstractThread* aThread)
     258             : {
     259           0 :   if (MightHaveTailTasks()) {
     260           0 :     TailDispatcher().DispatchTasksFor(aThread);
     261             :   }
     262           0 : }
     263             : 
     264             : bool
     265           0 : AbstractThread::HasTailTasksFor(AbstractThread* aThread)
     266             : {
     267           0 :   if (!MightHaveTailTasks()) {
     268           0 :     return false;
     269             :   }
     270           0 :   return TailDispatcher().HasTasksFor(aThread);
     271             : }
     272             : 
     273             : bool
     274           6 : AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const
     275             : {
     276           6 :   MOZ_ASSERT(aThread);
     277             :   // We require tail dispatch if both the source and destination
     278             :   // threads support it.
     279           6 :   return SupportsTailDispatch() && aThread->SupportsTailDispatch();
     280             : }
     281             : 
     282             : bool
     283           0 : AbstractThread::RequiresTailDispatchFromCurrentThread() const
     284             : {
     285           0 :   AbstractThread* current = GetCurrent();
     286           0 :   return current && RequiresTailDispatch(current);
     287             : }
     288             : 
     289             : AbstractThread*
     290           1 : AbstractThread::MainThread()
     291             : {
     292           1 :   MOZ_ASSERT(sMainThread);
     293           1 :   return sMainThread;
     294             : }
     295             : 
     296             : void
     297           5 : AbstractThread::InitTLS()
     298             : {
     299           5 :   if (!sCurrentThreadTLS.init()) {
     300           0 :     MOZ_CRASH();
     301             :   }
     302           5 : }
     303             : 
     304             : void
     305           3 : AbstractThread::InitMainThread()
     306             : {
     307           3 :   MOZ_ASSERT(NS_IsMainThread());
     308           3 :   MOZ_ASSERT(!sMainThread);
     309           6 :   nsCOMPtr<nsIThread> mainThread;
     310           3 :   NS_GetMainThread(getter_AddRefs(mainThread));
     311           3 :   MOZ_DIAGNOSTIC_ASSERT(mainThread);
     312           6 :   sMainThread = new EventTargetWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
     313           3 :   ClearOnShutdown(&sMainThread);
     314             : 
     315           3 :   if (!sCurrentThreadTLS.init()) {
     316           0 :     MOZ_CRASH();
     317             :   }
     318           3 :   sCurrentThreadTLS.set(sMainThread);
     319           3 : }
     320             : 
     321             : void
     322           0 : AbstractThread::DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable)
     323             : {
     324           0 :   GetCurrent()->TailDispatcher().AddStateChangeTask(this, Move(aRunnable));
     325           0 : }
     326             : 
     327             : /* static */ void
     328           0 : AbstractThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable)
     329             : {
     330           0 :   GetCurrent()->TailDispatcher().AddDirectTask(Move(aRunnable));
     331           0 : }
     332             : 
     333             : /* static */
     334             : already_AddRefed<AbstractThread>
     335           4 : AbstractThread::CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch)
     336             : {
     337           8 :   RefPtr<EventTargetWrapper> wrapper = new EventTargetWrapper(aThread, aRequireTailDispatch);
     338             : 
     339           4 :   bool onCurrentThread = false;
     340           4 :   Unused << aThread->IsOnCurrentThread(&onCurrentThread);
     341             : 
     342           4 :   if (onCurrentThread) {
     343           1 :     sCurrentThreadTLS.set(wrapper);
     344           1 :     return wrapper.forget();
     345             :   }
     346             : 
     347             :   // Set the thread-local sCurrentThreadTLS to point to the wrapper on the
     348             :   // target thread. This ensures that sCurrentThreadTLS is as expected by
     349             :   // AbstractThread::GetCurrent() on the target thread.
     350             :   nsCOMPtr<nsIRunnable> r =
     351           9 :     NS_NewRunnableFunction("AbstractThread::CreateXPCOMThreadWrapper",
     352          21 :                            [wrapper]() { sCurrentThreadTLS.set(wrapper); });
     353           3 :   aThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
     354           3 :   return wrapper.forget();
     355             : }
     356             : 
     357             : /* static  */
     358             : already_AddRefed<AbstractThread>
     359           1 : AbstractThread::CreateEventTargetWrapper(nsIEventTarget* aEventTarget,
     360             :                                          bool aRequireTailDispatch)
     361             : {
     362           1 :   MOZ_ASSERT(aEventTarget);
     363           2 :   nsCOMPtr<nsIThread> thread(do_QueryInterface(aEventTarget));
     364             :   Unused << thread; // simpler than DebugOnly<nsCOMPtr<nsIThread>>
     365           1 :   MOZ_ASSERT(!thread, "nsIThread should be wrapped by CreateXPCOMThreadWrapper!");
     366             : 
     367             :   RefPtr<EventTargetWrapper> wrapper =
     368           2 :     new EventTargetWrapper(aEventTarget, aRequireTailDispatch);
     369             : 
     370           2 :   return wrapper.forget();
     371             : }
     372             : 
     373             : } // namespace mozilla

Generated by: LCOV version 1.13