LCOV - code coverage report
Current view: top level - xpcom/threads - nsProxyRelease.h (source / functions) Hit Total Coverage
Test: output.info Lines: 89 113 78.8 %
Date: 2017-07-14 16:53:18 Functions: 166 882 18.8 %
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             : #ifndef nsProxyRelease_h__
       8             : #define nsProxyRelease_h__
       9             : 
      10             : #include "nsIEventTarget.h"
      11             : #include "nsIThread.h"
      12             : #include "nsCOMPtr.h"
      13             : #include "nsAutoPtr.h"
      14             : #include "MainThreadUtils.h"
      15             : #include "nsPrintfCString.h"
      16             : #include "nsThreadUtils.h"
      17             : #include "mozilla/Likely.h"
      18             : #include "mozilla/Move.h"
      19             : #include "mozilla/SystemGroup.h"
      20             : #include "mozilla/TypeTraits.h"
      21             : #include "mozilla/Unused.h"
      22             : 
      23             : #ifdef XPCOM_GLUE_AVOID_NSPR
      24             : #error NS_ProxyRelease implementation depends on NSPR.
      25             : #endif
      26             : 
      27             : namespace detail {
      28             : 
      29             : template<typename T>
      30         225 : class ProxyReleaseEvent : public mozilla::Runnable
      31             : {
      32             : public:
      33          75 :   ProxyReleaseEvent(const char* aName, already_AddRefed<T> aDoomed)
      34          75 :   : Runnable(aName), mDoomed(aDoomed.take()) {}
      35             : 
      36          75 :   NS_IMETHOD Run() override
      37             :   {
      38          75 :     NS_IF_RELEASE(mDoomed);
      39          75 :     return NS_OK;
      40             :   }
      41             : 
      42          75 :   NS_IMETHOD GetName(nsACString& aName) override
      43             :   {
      44             : #ifdef RELEASE_OR_BETA
      45             :     aName.Truncate();
      46             : #else
      47          75 :     if (mName) {
      48          75 :       aName.Append(nsPrintfCString("ProxyReleaseEvent for %s", mName));
      49             :     } else {
      50           0 :       aName.AssignLiteral("ProxyReleaseEvent");
      51             :     }
      52             : #endif
      53          75 :     return NS_OK;
      54             :   }
      55             : 
      56             : private:
      57             :   T* MOZ_OWNING_REF mDoomed;
      58             : };
      59             : 
      60             : template<typename T>
      61             : void
      62        1333 : ProxyRelease(const char* aName, nsIEventTarget* aTarget,
      63             :              already_AddRefed<T> aDoomed, bool aAlwaysProxy)
      64             : {
      65             :   // Auto-managing release of the pointer.
      66        1408 :   RefPtr<T> doomed = aDoomed;
      67             :   nsresult rv;
      68             : 
      69        1333 :   if (!doomed || !aTarget) {
      70        1204 :     return;
      71             :   }
      72             : 
      73         129 :   if (!aAlwaysProxy) {
      74         115 :     bool onCurrentThread = false;
      75         115 :     rv = aTarget->IsOnCurrentThread(&onCurrentThread);
      76         115 :     if (NS_SUCCEEDED(rv) && onCurrentThread) {
      77          54 :       return;
      78             :     }
      79             :   }
      80             : 
      81         225 :   nsCOMPtr<nsIRunnable> ev = new ProxyReleaseEvent<T>(aName, doomed.forget());
      82             : 
      83          75 :   rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
      84          75 :   if (NS_FAILED(rv)) {
      85           0 :     NS_WARNING("failed to post proxy release event, leaking!");
      86             :     // It is better to leak the aDoomed object than risk crashing as
      87             :     // a result of deleting it on the wrong thread.
      88             :   }
      89             : }
      90             : 
      91             : template<bool nsISupportsBased>
      92             : struct ProxyReleaseChooser
      93             : {
      94             :   template<typename T>
      95           0 :   static void ProxyRelease(const char* aName,
      96             :                            nsIEventTarget* aTarget,
      97             :                            already_AddRefed<T> aDoomed,
      98             :                            bool aAlwaysProxy)
      99             :   {
     100           0 :     ::detail::ProxyRelease(aName, aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
     101           0 :   }
     102             : };
     103             : 
     104             : template<>
     105             : struct ProxyReleaseChooser<true>
     106             : {
     107             :   // We need an intermediate step for handling classes with ambiguous
     108             :   // inheritance to nsISupports.
     109             :   template<typename T>
     110        1333 :   static void ProxyRelease(const char* aName,
     111             :                            nsIEventTarget* aTarget,
     112             :                            already_AddRefed<T> aDoomed,
     113             :                            bool aAlwaysProxy)
     114             :   {
     115        1333 :     ProxyReleaseISupports(aName, aTarget, ToSupports(aDoomed.take()), aAlwaysProxy);
     116        1333 :   }
     117             : 
     118             :   static void ProxyReleaseISupports(const char* aName,
     119             :                                     nsIEventTarget* aTarget,
     120             :                                     nsISupports* aDoomed,
     121             :                                     bool aAlwaysProxy);
     122             : };
     123             : 
     124             : } // namespace detail
     125             : 
     126             : /**
     127             :  * Ensures that the delete of a smart pointer occurs on the target thread.
     128             :  *
     129             :  * @param aTarget
     130             :  *        the target thread where the doomed object should be released.
     131             :  * @param aDoomed
     132             :  *        the doomed object; the object to be released on the target thread.
     133             :  * @param aAlwaysProxy
     134             :  *        normally, if NS_ProxyRelease is called on the target thread, then the
     135             :  *        doomed object will be released directly. However, if this parameter is
     136             :  *        true, then an event will always be posted to the target thread for
     137             :  *        asynchronous release.
     138             :  */
     139             : template<class T>
     140             : inline NS_HIDDEN_(void)
     141        1333 : NS_ProxyRelease(const char* aName, nsIEventTarget* aTarget,
     142             :                 already_AddRefed<T> aDoomed, bool aAlwaysProxy = false)
     143             : {
     144        1333 :   ::detail::ProxyReleaseChooser<mozilla::IsBaseOf<nsISupports, T>::value>
     145        2666 :     ::ProxyRelease(aName, aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
     146        1333 : }
     147             : 
     148             : /**
     149             :  * Ensures that the delete of a smart pointer occurs on the main thread.
     150             :  *
     151             :  * @param aDoomed
     152             :  *        the doomed object; the object to be released on the main thread.
     153             :  * @param aAlwaysProxy
     154             :  *        normally, if NS_ReleaseOnMainThread is called on the main thread,
     155             :  *        then the doomed object will be released directly. However, if this
     156             :  *        parameter is true, then an event will always be posted to the main
     157             :  *        thread for asynchronous release.
     158             :  */
     159             : template<class T>
     160             : inline NS_HIDDEN_(void)
     161        1186 : NS_ReleaseOnMainThread(const char* aName,
     162             :                        already_AddRefed<T> aDoomed,
     163             :                        bool aAlwaysProxy = false)
     164             : {
     165             :   // NS_ProxyRelease treats a null event target as "the current thread".  So a
     166             :   // handle on the main thread is only necessary when we're not already on the
     167             :   // main thread or the release must happen asynchronously.
     168        2372 :   nsCOMPtr<nsIThread> mainThread;
     169        1186 :   if (!NS_IsMainThread() || aAlwaysProxy) {
     170          15 :     nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
     171             : 
     172          15 :     if (NS_FAILED(rv)) {
     173           0 :       MOZ_ASSERT_UNREACHABLE("Could not get main thread; leaking an object!");
     174             :       mozilla::Unused << aDoomed.take();
     175             :       return;
     176             :     }
     177             :   }
     178             : 
     179        1186 :   NS_ProxyRelease(aName, mainThread, mozilla::Move(aDoomed), aAlwaysProxy);
     180             : }
     181             : 
     182             : /**
     183             :  * This is the same as NS_ReleaseOnMainThread, except that the
     184             :  * runnable for the deletion will be dispatched to the system group.
     185             :  */
     186             : template<class T>
     187             : inline NS_HIDDEN_(void)
     188          33 : NS_ReleaseOnMainThreadSystemGroup(already_AddRefed<T> aDoomed,
     189             :                                   bool aAlwaysProxy = false)
     190             : {
     191             :   // NS_ProxyRelease treats a null event target as "the current thread".  So a
     192             :   // handle on the main thread is only necessary when we're not already on the
     193             :   // main thread or the release must happen asynchronously.
     194          66 :   nsCOMPtr<nsIEventTarget> systemGroupEventTarget;
     195          33 :   if (!NS_IsMainThread() || aAlwaysProxy) {
     196          33 :     systemGroupEventTarget = mozilla::SystemGroup::EventTargetFor(mozilla::TaskCategory::Other);
     197             : 
     198          33 :     if (!systemGroupEventTarget) {
     199           0 :       MOZ_ASSERT_UNREACHABLE("Could not get main thread; leaking an object!");
     200             :       mozilla::Unused << aDoomed.take();
     201             :       return;
     202             :     }
     203             :   }
     204             : 
     205          66 :   NS_ProxyRelease("NS_ReleaseOnMainThreadSystemGroup", systemGroupEventTarget,
     206          33 :                   mozilla::Move(aDoomed), aAlwaysProxy);
     207             : }
     208             : 
     209             : /**
     210             :  * Class to safely handle main-thread-only pointers off the main thread.
     211             :  *
     212             :  * Classes like XPCWrappedJS are main-thread-only, which means that it is
     213             :  * forbidden to call methods on instances of these classes off the main thread.
     214             :  * For various reasons (see bug 771074), this restriction recently began to
     215             :  * apply to AddRef/Release as well.
     216             :  *
     217             :  * This presents a problem for consumers that wish to hold a callback alive
     218             :  * on non-main-thread code. A common example of this is the proxy callback
     219             :  * pattern, where non-main-thread code holds a strong-reference to the callback
     220             :  * object, and dispatches new Runnables (also with a strong reference) to the
     221             :  * main thread in order to execute the callback. This involves several AddRef
     222             :  * and Release calls on the other thread, which is (now) verboten.
     223             :  *
     224             :  * The basic idea of this class is to introduce a layer of indirection.
     225             :  * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
     226             :  * maintains one strong reference to the main-thread-only object. It must be
     227             :  * instantiated on the main thread (so that the AddRef of the underlying object
     228             :  * happens on the main thread), but consumers may subsequently pass references
     229             :  * to the holder anywhere they please. These references are meant to be opaque
     230             :  * when accessed off-main-thread (assertions enforce this).
     231             :  *
     232             :  * The semantics of RefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
     233             :  * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
     234             :  * to the above (though it includes various convenience methods). The basic
     235             :  * pattern is as follows.
     236             :  *
     237             :  * // On the main thread:
     238             :  * nsCOMPtr<nsIFooCallback> callback = ...;
     239             :  * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
     240             :  *   new nsMainThreadPtrHolder<nsIFooCallback>(callback);
     241             :  * // Pass callbackHandle to structs/classes that might be accessed on other
     242             :  * // threads.
     243             :  *
     244             :  * All structs and classes that might be accessed on other threads should store
     245             :  * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
     246             :  */
     247             : template<class T>
     248             : class nsMainThreadPtrHolder final
     249             : {
     250             : public:
     251             :   // We can only acquire a pointer on the main thread. We to fail fast for
     252             :   // threading bugs, so by default we assert if our pointer is used or acquired
     253             :   // off-main-thread. But some consumers need to use the same pointer for
     254             :   // multiple classes, some of which are main-thread-only and some of which
     255             :   // aren't. So we allow them to explicitly disable this strict checking.
     256         154 :   nsMainThreadPtrHolder(const char* aName, T* aPtr, bool aStrict = true,
     257             :                         nsIEventTarget* aMainThreadEventTarget = nullptr)
     258             :     : mRawPtr(nullptr)
     259             :     , mStrict(aStrict)
     260             :     , mMainThreadEventTarget(aMainThreadEventTarget)
     261             : #ifndef RELEASE_OR_BETA
     262         154 :     , mName(aName)
     263             : #endif
     264             :   {
     265             :     // We can only AddRef our pointer on the main thread, which means that the
     266             :     // holder must be constructed on the main thread.
     267         154 :     MOZ_ASSERT(!mStrict || NS_IsMainThread());
     268         154 :     NS_IF_ADDREF(mRawPtr = aPtr);
     269         154 :   }
     270         105 :   nsMainThreadPtrHolder(const char* aName, already_AddRefed<T> aPtr,
     271             :                         bool aStrict = true,
     272             :                         nsIEventTarget* aMainThreadEventTarget = nullptr)
     273             :     : mRawPtr(aPtr.take())
     274             :     , mStrict(aStrict)
     275             :     , mMainThreadEventTarget(aMainThreadEventTarget)
     276             : #ifndef RELEASE_OR_BETA
     277         105 :     , mName(aName)
     278             : #endif
     279             :   {
     280             :     // Since we don't need to AddRef the pointer, this constructor is safe to
     281             :     // call on any thread.
     282         105 :   }
     283             : 
     284             : private:
     285             :   // We can be released on any thread.
     286          64 :   ~nsMainThreadPtrHolder()
     287             :   {
     288          64 :     if (NS_IsMainThread()) {
     289          57 :       NS_IF_RELEASE(mRawPtr);
     290           7 :     } else if (mRawPtr) {
     291           6 :       if (!mMainThreadEventTarget) {
     292           6 :         mMainThreadEventTarget = do_GetMainThread();
     293             :       }
     294           6 :       MOZ_ASSERT(mMainThreadEventTarget);
     295           6 :       NS_ProxyRelease(
     296             : #ifdef RELEASE_OR_BETA
     297             :         nullptr,
     298             : #else
     299             :         mName,
     300             : #endif
     301             :         mMainThreadEventTarget, dont_AddRef(mRawPtr));
     302             :     }
     303          64 :   }
     304             : 
     305             : public:
     306        1825 :   T* get()
     307             :   {
     308             :     // Nobody should be touching the raw pointer off-main-thread.
     309        1825 :     if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
     310           0 :       NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
     311           0 :       MOZ_CRASH();
     312             :     }
     313        1825 :     return mRawPtr;
     314             :   }
     315             : 
     316           0 :   bool operator==(const nsMainThreadPtrHolder<T>& aOther) const
     317             :   {
     318           0 :     return mRawPtr == aOther.mRawPtr;
     319             :   }
     320          18 :   bool operator!() const
     321             :   {
     322          18 :     return !mRawPtr;
     323             :   }
     324             : 
     325         431 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
     326             : 
     327             : private:
     328             :   // Our wrapped pointer.
     329             :   T* mRawPtr;
     330             : 
     331             :   // Whether to strictly enforce thread invariants in this class.
     332             :   bool mStrict;
     333             : 
     334             :   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
     335             : 
     336             : #ifndef RELEASE_OR_BETA
     337             :   const char* mName = nullptr;
     338             : #endif
     339             : 
     340             :   // Copy constructor and operator= not implemented. Once constructed, the
     341             :   // holder is immutable.
     342             :   T& operator=(nsMainThreadPtrHolder& aOther);
     343             :   nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther);
     344             : };
     345             : 
     346             : template<class T>
     347         126 : class nsMainThreadPtrHandle
     348             : {
     349             :   RefPtr<nsMainThreadPtrHolder<T>> mPtr;
     350             : 
     351             : public:
     352         756 :   nsMainThreadPtrHandle() : mPtr(nullptr) {}
     353           0 :   MOZ_IMPLICIT nsMainThreadPtrHandle(decltype(nullptr)) : mPtr(nullptr) {}
     354         100 :   explicit nsMainThreadPtrHandle(nsMainThreadPtrHolder<T>* aHolder)
     355         100 :     : mPtr(aHolder)
     356             :   {
     357         100 :   }
     358          40 :   explicit nsMainThreadPtrHandle(
     359             :       already_AddRefed<nsMainThreadPtrHolder<T>> aHolder)
     360          40 :     : mPtr(aHolder)
     361             :   {
     362          40 :   }
     363          22 :   nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther)
     364          22 :     : mPtr(aOther.mPtr)
     365             :   {
     366          22 :   }
     367           0 :   nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther)
     368             :   {
     369           0 :     mPtr = aOther.mPtr;
     370           0 :     return *this;
     371             :   }
     372         172 :   nsMainThreadPtrHandle& operator=(nsMainThreadPtrHolder<T>* aHolder)
     373             :   {
     374         172 :     mPtr = aHolder;
     375         172 :     return *this;
     376             :   }
     377             : 
     378             :   // These all call through to nsMainThreadPtrHolder, and thus implicitly
     379             :   // assert that we're on the main thread. Off-main-thread consumers must treat
     380             :   // these handles as opaque.
     381        1824 :   T* get()
     382             :   {
     383        1824 :     if (mPtr) {
     384        1824 :       return mPtr.get()->get();
     385             :     }
     386           0 :     return nullptr;
     387             :   }
     388           1 :   const T* get() const
     389             :   {
     390           1 :     if (mPtr) {
     391           1 :       return mPtr.get()->get();
     392             :     }
     393           0 :     return nullptr;
     394             :   }
     395             : 
     396        1530 :   operator T*() { return get(); }
     397          22 :   T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return get(); }
     398             : 
     399             :   // These are safe to call on other threads with appropriate external locking.
     400           0 :   bool operator==(const nsMainThreadPtrHandle<T>& aOther) const
     401             :   {
     402           0 :     if (!mPtr || !aOther.mPtr) {
     403           0 :       return mPtr == aOther.mPtr;
     404             :     }
     405           0 :     return *mPtr == *aOther.mPtr;
     406             :   }
     407           0 :   bool operator!=(const nsMainThreadPtrHandle<T>& aOther) const
     408             :   {
     409           0 :     return !operator==(aOther);
     410             :   }
     411             :   bool operator==(decltype(nullptr)) const { return mPtr == nullptr; }
     412           0 :   bool operator!=(decltype(nullptr)) const { return mPtr != nullptr; }
     413         128 :   bool operator!() const {
     414         128 :     return !mPtr || !*mPtr;
     415             :   }
     416             : };
     417             : 
     418             : namespace mozilla {
     419             : 
     420             : template<typename T>
     421             : using PtrHolder = nsMainThreadPtrHolder<T>;
     422             : 
     423             : template<typename T>
     424             : using PtrHandle = nsMainThreadPtrHandle<T>;
     425             : 
     426             : } // namespace mozilla
     427             : 
     428             : #endif

Generated by: LCOV version 1.13