michael@0: /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsProxyRelease_h__ michael@0: #define nsProxyRelease_h__ michael@0: michael@0: #include "nsIEventTarget.h" michael@0: #include "nsIThread.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "MainThreadUtils.h" michael@0: #include "mozilla/Likely.h" michael@0: michael@0: #ifdef XPCOM_GLUE_AVOID_NSPR michael@0: #error NS_ProxyRelease implementation depends on NSPR. michael@0: #endif michael@0: michael@0: /** michael@0: * Ensure that a nsCOMPtr is released on the target thread. michael@0: * michael@0: * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) michael@0: */ michael@0: template michael@0: inline NS_HIDDEN_(nsresult) michael@0: NS_ProxyRelease michael@0: (nsIEventTarget *target, nsCOMPtr &doomed, bool alwaysProxy=false) michael@0: { michael@0: T* raw = nullptr; michael@0: doomed.swap(raw); michael@0: return NS_ProxyRelease(target, raw, alwaysProxy); michael@0: } michael@0: michael@0: /** michael@0: * Ensure that a nsRefPtr is released on the target thread. michael@0: * michael@0: * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) michael@0: */ michael@0: template michael@0: inline NS_HIDDEN_(nsresult) michael@0: NS_ProxyRelease michael@0: (nsIEventTarget *target, nsRefPtr &doomed, bool alwaysProxy=false) michael@0: { michael@0: T* raw = nullptr; michael@0: doomed.swap(raw); michael@0: return NS_ProxyRelease(target, raw, alwaysProxy); michael@0: } michael@0: michael@0: /** michael@0: * Ensures that the delete of a nsISupports object occurs on the target thread. michael@0: * michael@0: * @param target michael@0: * the target thread where the doomed object should be released. michael@0: * @param doomed michael@0: * the doomed object; the object to be released on the target thread. michael@0: * @param alwaysProxy michael@0: * normally, if NS_ProxyRelease is called on the target thread, then the michael@0: * doomed object will released directly. however, if this parameter is michael@0: * true, then an event will always be posted to the target thread for michael@0: * asynchronous release. michael@0: */ michael@0: NS_COM_GLUE nsresult michael@0: NS_ProxyRelease michael@0: (nsIEventTarget *target, nsISupports *doomed, bool alwaysProxy=false); michael@0: michael@0: /** michael@0: * Class to safely handle main-thread-only pointers off the main thread. michael@0: * michael@0: * Classes like XPCWrappedJS are main-thread-only, which means that it is michael@0: * forbidden to call methods on instances of these classes off the main thread. michael@0: * For various reasons (see bug 771074), this restriction recently began to michael@0: * apply to AddRef/Release as well. michael@0: * michael@0: * This presents a problem for consumers that wish to hold a callback alive michael@0: * on non-main-thread code. A common example of this is the proxy callback michael@0: * pattern, where non-main-thread code holds a strong-reference to the callback michael@0: * object, and dispatches new Runnables (also with a strong reference) to the michael@0: * main thread in order to execute the callback. This involves several AddRef michael@0: * and Release calls on the other thread, which is (now) verboten. michael@0: * michael@0: * The basic idea of this class is to introduce a layer of indirection. michael@0: * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally michael@0: * maintains one strong reference to the main-thread-only object. It must be michael@0: * instantiated on the main thread (so that the AddRef of the underlying object michael@0: * happens on the main thread), but consumers may subsequently pass references michael@0: * to the holder anywhere they please. These references are meant to be opaque michael@0: * when accessed off-main-thread (assertions enforce this). michael@0: * michael@0: * The semantics of nsRefPtr > would be cumbersome, so michael@0: * we also introduce nsMainThreadPtrHandle, which is conceptually identical michael@0: * to the above (though it includes various convenience methods). The basic michael@0: * pattern is as follows. michael@0: * michael@0: * // On the main thread: michael@0: * nsCOMPtr callback = ...; michael@0: * nsMainThreadPtrHandle callbackHandle = michael@0: * new nsMainThreadPtrHolder(callback); michael@0: * // Pass callbackHandle to structs/classes that might be accessed on other michael@0: * // threads. michael@0: * michael@0: * All structs and classes that might be accessed on other threads should store michael@0: * an nsMainThreadPtrHandle rather than an nsCOMPtr. michael@0: */ michael@0: template michael@0: class nsMainThreadPtrHolder MOZ_FINAL michael@0: { michael@0: public: michael@0: // We can only acquire a pointer on the main thread. We to fail fast for michael@0: // threading bugs, so by default we assert if our pointer is used or acquired michael@0: // off-main-thread. But some consumers need to use the same pointer for michael@0: // multiple classes, some of which are main-thread-only and some of which michael@0: // aren't. So we allow them to explicitly disable this strict checking. michael@0: nsMainThreadPtrHolder(T* ptr, bool strict = true) : mRawPtr(nullptr), mStrict(strict) { michael@0: // We can only AddRef our pointer on the main thread, which means that the michael@0: // holder must be constructed on the main thread. michael@0: MOZ_ASSERT(!mStrict || NS_IsMainThread()); michael@0: NS_IF_ADDREF(mRawPtr = ptr); michael@0: } michael@0: michael@0: // We can be released on any thread. michael@0: ~nsMainThreadPtrHolder() { michael@0: if (NS_IsMainThread()) { michael@0: NS_IF_RELEASE(mRawPtr); michael@0: } else if (mRawPtr) { michael@0: nsCOMPtr mainThread; michael@0: NS_GetMainThread(getter_AddRefs(mainThread)); michael@0: if (!mainThread) { michael@0: NS_WARNING("Couldn't get main thread! Leaking pointer."); michael@0: return; michael@0: } michael@0: NS_ProxyRelease(mainThread, mRawPtr); michael@0: } michael@0: } michael@0: michael@0: T* get() { michael@0: // Nobody should be touching the raw pointer off-main-thread. michael@0: if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) { michael@0: NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread"); michael@0: MOZ_CRASH(); michael@0: } michael@0: return mRawPtr; michael@0: } michael@0: michael@0: bool operator==(const nsMainThreadPtrHolder& aOther) const { return mRawPtr == aOther.mRawPtr; } michael@0: michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder) michael@0: michael@0: private: michael@0: // Our wrapped pointer. michael@0: T* mRawPtr; michael@0: michael@0: // Whether to strictly enforce thread invariants in this class. michael@0: bool mStrict; michael@0: michael@0: // Copy constructor and operator= not implemented. Once constructed, the michael@0: // holder is immutable. michael@0: T& operator=(nsMainThreadPtrHolder& other); michael@0: nsMainThreadPtrHolder(const nsMainThreadPtrHolder& other); michael@0: }; michael@0: michael@0: template michael@0: class nsMainThreadPtrHandle michael@0: { michael@0: nsRefPtr > mPtr; michael@0: michael@0: public: michael@0: nsMainThreadPtrHandle() : mPtr(nullptr) {} michael@0: nsMainThreadPtrHandle(nsMainThreadPtrHolder *aHolder) : mPtr(aHolder) {} michael@0: nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther) : mPtr(aOther.mPtr) {} michael@0: nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther) { michael@0: mPtr = aOther.mPtr; michael@0: return *this; michael@0: } michael@0: michael@0: // These all call through to nsMainThreadPtrHolder, and thus implicitly michael@0: // assert that we're on the main thread. Off-main-thread consumers must treat michael@0: // these handles as opaque. michael@0: T* get() michael@0: { michael@0: if (mPtr) { michael@0: return mPtr.get()->get(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: const T* get() const michael@0: { michael@0: if (mPtr) { michael@0: return mPtr.get()->get(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: operator T*() { return get(); } michael@0: T* operator->() { return get(); } michael@0: michael@0: // These are safe to call on other threads with appropriate external locking. michael@0: bool operator==(const nsMainThreadPtrHandle& aOther) const { michael@0: if (!mPtr || !aOther.mPtr) michael@0: return mPtr == aOther.mPtr; michael@0: return *mPtr == *aOther.mPtr; michael@0: } michael@0: bool operator!() { return !mPtr; } michael@0: }; michael@0: michael@0: #endif