123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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/. */
- #ifndef nsProxyRelease_h__
- #define nsProxyRelease_h__
- #include "nsIEventTarget.h"
- #include "nsIThread.h"
- #include "nsCOMPtr.h"
- #include "nsAutoPtr.h"
- #include "MainThreadUtils.h"
- #include "nsThreadUtils.h"
- #include "mozilla/Likely.h"
- #include "mozilla/Move.h"
- #include "mozilla/TypeTraits.h"
- #include "mozilla/Unused.h"
- #ifdef XPCOM_GLUE_AVOID_NSPR
- #error NS_ProxyRelease implementation depends on NSPR.
- #endif
- namespace detail {
- template<typename T>
- class ProxyReleaseEvent : public mozilla::Runnable
- {
- public:
- explicit ProxyReleaseEvent(already_AddRefed<T> aDoomed)
- : mDoomed(aDoomed.take()) {}
- NS_IMETHOD Run() override
- {
- NS_IF_RELEASE(mDoomed);
- return NS_OK;
- }
- private:
- T* MOZ_OWNING_REF mDoomed;
- };
- template<typename T>
- void
- ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed, bool aAlwaysProxy)
- {
- // Auto-managing release of the pointer.
- RefPtr<T> doomed = aDoomed;
- nsresult rv;
- if (!doomed || !aTarget) {
- return;
- }
- if (!aAlwaysProxy) {
- bool onCurrentThread = false;
- rv = aTarget->IsOnCurrentThread(&onCurrentThread);
- if (NS_SUCCEEDED(rv) && onCurrentThread) {
- return;
- }
- }
- nsCOMPtr<nsIRunnable> ev = new ProxyReleaseEvent<T>(doomed.forget());
- rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
- if (NS_FAILED(rv)) {
- NS_WARNING("failed to post proxy release event, leaking!");
- // It is better to leak the aDoomed object than risk crashing as
- // a result of deleting it on the wrong thread.
- }
- }
- template<bool nsISupportsBased>
- struct ProxyReleaseChooser
- {
- template<typename T>
- static void ProxyRelease(nsIEventTarget* aTarget,
- already_AddRefed<T> aDoomed,
- bool aAlwaysProxy)
- {
- ::detail::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
- }
- };
- template<>
- struct ProxyReleaseChooser<true>
- {
- // We need an intermediate step for handling classes with ambiguous
- // inheritance to nsISupports.
- template<typename T>
- static void ProxyRelease(nsIEventTarget* aTarget,
- already_AddRefed<T> aDoomed,
- bool aAlwaysProxy)
- {
- ProxyReleaseISupports(aTarget, ToSupports(aDoomed.take()), aAlwaysProxy);
- }
- static void ProxyReleaseISupports(nsIEventTarget* aTarget,
- nsISupports* aDoomed,
- bool aAlwaysProxy);
- };
- } // namespace detail
- /**
- * Ensures that the delete of a smart pointer occurs on the target thread.
- *
- * @param aTarget
- * the target thread where the doomed object should be released.
- * @param aDoomed
- * the doomed object; the object to be released on the target thread.
- * @param aAlwaysProxy
- * normally, if NS_ProxyRelease is called on the target thread, then the
- * doomed object will be released directly. However, if this parameter is
- * true, then an event will always be posted to the target thread for
- * asynchronous release.
- */
- template<class T>
- inline NS_HIDDEN_(void)
- NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed,
- bool aAlwaysProxy = false)
- {
- ::detail::ProxyReleaseChooser<mozilla::IsBaseOf<nsISupports, T>::value>
- ::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
- }
- /**
- * Ensures that the delete of a smart pointer occurs on the main thread.
- *
- * @param aDoomed
- * the doomed object; the object to be released on the main thread.
- * @param aAlwaysProxy
- * normally, if NS_ReleaseOnMainThread is called on the main thread,
- * then the doomed object will be released directly. However, if this
- * parameter is true, then an event will always be posted to the main
- * thread for asynchronous release.
- */
- template<class T>
- inline NS_HIDDEN_(void)
- NS_ReleaseOnMainThread(already_AddRefed<T> aDoomed,
- bool aAlwaysProxy = false)
- {
- // NS_ProxyRelease treats a null event target as "the current thread". So a
- // handle on the main thread is only necessary when we're not already on the
- // main thread or the release must happen asynchronously.
- nsCOMPtr<nsIThread> mainThread;
- if (!NS_IsMainThread() || aAlwaysProxy) {
- nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
- if (NS_FAILED(rv)) {
- MOZ_ASSERT_UNREACHABLE("Could not get main thread; leaking an object!");
- mozilla::Unused << aDoomed.take();
- return;
- }
- }
- NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy);
- }
- /**
- * Class to safely handle main-thread-only pointers off the main thread.
- *
- * Classes like XPCWrappedJS are main-thread-only, which means that it is
- * forbidden to call methods on instances of these classes off the main thread.
- * For various reasons (see bug 771074), this restriction recently began to
- * apply to AddRef/Release as well.
- *
- * This presents a problem for consumers that wish to hold a callback alive
- * on non-main-thread code. A common example of this is the proxy callback
- * pattern, where non-main-thread code holds a strong-reference to the callback
- * object, and dispatches new Runnables (also with a strong reference) to the
- * main thread in order to execute the callback. This involves several AddRef
- * and Release calls on the other thread, which is (now) verboten.
- *
- * The basic idea of this class is to introduce a layer of indirection.
- * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
- * maintains one strong reference to the main-thread-only object. It must be
- * instantiated on the main thread (so that the AddRef of the underlying object
- * happens on the main thread), but consumers may subsequently pass references
- * to the holder anywhere they please. These references are meant to be opaque
- * when accessed off-main-thread (assertions enforce this).
- *
- * The semantics of RefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
- * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
- * to the above (though it includes various convenience methods). The basic
- * pattern is as follows.
- *
- * // On the main thread:
- * nsCOMPtr<nsIFooCallback> callback = ...;
- * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
- * new nsMainThreadPtrHolder<nsIFooCallback>(callback);
- * // Pass callbackHandle to structs/classes that might be accessed on other
- * // threads.
- *
- * All structs and classes that might be accessed on other threads should store
- * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
- */
- template<class T>
- class nsMainThreadPtrHolder final
- {
- public:
- // We can only acquire a pointer on the main thread. We to fail fast for
- // threading bugs, so by default we assert if our pointer is used or acquired
- // off-main-thread. But some consumers need to use the same pointer for
- // multiple classes, some of which are main-thread-only and some of which
- // aren't. So we allow them to explicitly disable this strict checking.
- explicit nsMainThreadPtrHolder(T* aPtr, bool aStrict = true)
- : mRawPtr(nullptr)
- , mStrict(aStrict)
- {
- // We can only AddRef our pointer on the main thread, which means that the
- // holder must be constructed on the main thread.
- MOZ_ASSERT(!mStrict || NS_IsMainThread());
- NS_IF_ADDREF(mRawPtr = aPtr);
- }
- explicit nsMainThreadPtrHolder(already_AddRefed<T> aPtr, bool aString = true)
- : mRawPtr(aPtr.take())
- , mStrict(aString)
- {
- // Since we don't need to AddRef the pointer, this constructor is safe to
- // call on any thread.
- }
- private:
- // We can be released on any thread.
- ~nsMainThreadPtrHolder()
- {
- if (NS_IsMainThread()) {
- NS_IF_RELEASE(mRawPtr);
- } else if (mRawPtr) {
- NS_ReleaseOnMainThread(dont_AddRef(mRawPtr));
- }
- }
- public:
- T* get()
- {
- // Nobody should be touching the raw pointer off-main-thread.
- if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
- NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
- MOZ_CRASH();
- }
- return mRawPtr;
- }
- bool operator==(const nsMainThreadPtrHolder<T>& aOther) const
- {
- return mRawPtr == aOther.mRawPtr;
- }
- bool operator!() const
- {
- return !mRawPtr;
- }
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
- private:
- // Our wrapped pointer.
- T* mRawPtr;
- // Whether to strictly enforce thread invariants in this class.
- bool mStrict;
- // Copy constructor and operator= not implemented. Once constructed, the
- // holder is immutable.
- T& operator=(nsMainThreadPtrHolder& aOther);
- nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther);
- };
- template<class T>
- class nsMainThreadPtrHandle
- {
- RefPtr<nsMainThreadPtrHolder<T>> mPtr;
- public:
- nsMainThreadPtrHandle() : mPtr(nullptr) {}
- MOZ_IMPLICIT nsMainThreadPtrHandle(decltype(nullptr)) : mPtr(nullptr) {}
- explicit nsMainThreadPtrHandle(nsMainThreadPtrHolder<T>* aHolder)
- : mPtr(aHolder)
- {
- }
- explicit nsMainThreadPtrHandle(
- already_AddRefed<nsMainThreadPtrHolder<T>> aHolder)
- : mPtr(aHolder)
- {
- }
- nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther)
- : mPtr(aOther.mPtr)
- {
- }
- nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther)
- {
- mPtr = aOther.mPtr;
- return *this;
- }
- nsMainThreadPtrHandle& operator=(nsMainThreadPtrHolder<T>* aHolder)
- {
- mPtr = aHolder;
- return *this;
- }
- // These all call through to nsMainThreadPtrHolder, and thus implicitly
- // assert that we're on the main thread. Off-main-thread consumers must treat
- // these handles as opaque.
- T* get()
- {
- if (mPtr) {
- return mPtr.get()->get();
- }
- return nullptr;
- }
- const T* get() const
- {
- if (mPtr) {
- return mPtr.get()->get();
- }
- return nullptr;
- }
- operator T*() { return get(); }
- T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return get(); }
- // These are safe to call on other threads with appropriate external locking.
- bool operator==(const nsMainThreadPtrHandle<T>& aOther) const
- {
- if (!mPtr || !aOther.mPtr) {
- return mPtr == aOther.mPtr;
- }
- return *mPtr == *aOther.mPtr;
- }
- bool operator!=(const nsMainThreadPtrHandle<T>& aOther) const
- {
- return !operator==(aOther);
- }
- bool operator==(decltype(nullptr)) const { return mPtr == nullptr; }
- bool operator!=(decltype(nullptr)) const { return mPtr != nullptr; }
- bool operator!() const {
- return !mPtr || !*mPtr;
- }
- };
- namespace mozilla {
- template<typename T>
- using PtrHolder = nsMainThreadPtrHolder<T>;
- template<typename T>
- using PtrHandle = nsMainThreadPtrHandle<T>;
- } // namespace mozilla
- #endif
|