Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #ifndef nsProxyRelease_h__ |
michael@0 | 7 | #define nsProxyRelease_h__ |
michael@0 | 8 | |
michael@0 | 9 | #include "nsIEventTarget.h" |
michael@0 | 10 | #include "nsIThread.h" |
michael@0 | 11 | #include "nsCOMPtr.h" |
michael@0 | 12 | #include "nsAutoPtr.h" |
michael@0 | 13 | #include "MainThreadUtils.h" |
michael@0 | 14 | #include "mozilla/Likely.h" |
michael@0 | 15 | |
michael@0 | 16 | #ifdef XPCOM_GLUE_AVOID_NSPR |
michael@0 | 17 | #error NS_ProxyRelease implementation depends on NSPR. |
michael@0 | 18 | #endif |
michael@0 | 19 | |
michael@0 | 20 | /** |
michael@0 | 21 | * Ensure that a nsCOMPtr is released on the target thread. |
michael@0 | 22 | * |
michael@0 | 23 | * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) |
michael@0 | 24 | */ |
michael@0 | 25 | template <class T> |
michael@0 | 26 | inline NS_HIDDEN_(nsresult) |
michael@0 | 27 | NS_ProxyRelease |
michael@0 | 28 | (nsIEventTarget *target, nsCOMPtr<T> &doomed, bool alwaysProxy=false) |
michael@0 | 29 | { |
michael@0 | 30 | T* raw = nullptr; |
michael@0 | 31 | doomed.swap(raw); |
michael@0 | 32 | return NS_ProxyRelease(target, raw, alwaysProxy); |
michael@0 | 33 | } |
michael@0 | 34 | |
michael@0 | 35 | /** |
michael@0 | 36 | * Ensure that a nsRefPtr is released on the target thread. |
michael@0 | 37 | * |
michael@0 | 38 | * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) |
michael@0 | 39 | */ |
michael@0 | 40 | template <class T> |
michael@0 | 41 | inline NS_HIDDEN_(nsresult) |
michael@0 | 42 | NS_ProxyRelease |
michael@0 | 43 | (nsIEventTarget *target, nsRefPtr<T> &doomed, bool alwaysProxy=false) |
michael@0 | 44 | { |
michael@0 | 45 | T* raw = nullptr; |
michael@0 | 46 | doomed.swap(raw); |
michael@0 | 47 | return NS_ProxyRelease(target, raw, alwaysProxy); |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | /** |
michael@0 | 51 | * Ensures that the delete of a nsISupports object occurs on the target thread. |
michael@0 | 52 | * |
michael@0 | 53 | * @param target |
michael@0 | 54 | * the target thread where the doomed object should be released. |
michael@0 | 55 | * @param doomed |
michael@0 | 56 | * the doomed object; the object to be released on the target thread. |
michael@0 | 57 | * @param alwaysProxy |
michael@0 | 58 | * normally, if NS_ProxyRelease is called on the target thread, then the |
michael@0 | 59 | * doomed object will released directly. however, if this parameter is |
michael@0 | 60 | * true, then an event will always be posted to the target thread for |
michael@0 | 61 | * asynchronous release. |
michael@0 | 62 | */ |
michael@0 | 63 | NS_COM_GLUE nsresult |
michael@0 | 64 | NS_ProxyRelease |
michael@0 | 65 | (nsIEventTarget *target, nsISupports *doomed, bool alwaysProxy=false); |
michael@0 | 66 | |
michael@0 | 67 | /** |
michael@0 | 68 | * Class to safely handle main-thread-only pointers off the main thread. |
michael@0 | 69 | * |
michael@0 | 70 | * Classes like XPCWrappedJS are main-thread-only, which means that it is |
michael@0 | 71 | * forbidden to call methods on instances of these classes off the main thread. |
michael@0 | 72 | * For various reasons (see bug 771074), this restriction recently began to |
michael@0 | 73 | * apply to AddRef/Release as well. |
michael@0 | 74 | * |
michael@0 | 75 | * This presents a problem for consumers that wish to hold a callback alive |
michael@0 | 76 | * on non-main-thread code. A common example of this is the proxy callback |
michael@0 | 77 | * pattern, where non-main-thread code holds a strong-reference to the callback |
michael@0 | 78 | * object, and dispatches new Runnables (also with a strong reference) to the |
michael@0 | 79 | * main thread in order to execute the callback. This involves several AddRef |
michael@0 | 80 | * and Release calls on the other thread, which is (now) verboten. |
michael@0 | 81 | * |
michael@0 | 82 | * The basic idea of this class is to introduce a layer of indirection. |
michael@0 | 83 | * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally |
michael@0 | 84 | * maintains one strong reference to the main-thread-only object. It must be |
michael@0 | 85 | * instantiated on the main thread (so that the AddRef of the underlying object |
michael@0 | 86 | * happens on the main thread), but consumers may subsequently pass references |
michael@0 | 87 | * to the holder anywhere they please. These references are meant to be opaque |
michael@0 | 88 | * when accessed off-main-thread (assertions enforce this). |
michael@0 | 89 | * |
michael@0 | 90 | * The semantics of nsRefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so |
michael@0 | 91 | * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical |
michael@0 | 92 | * to the above (though it includes various convenience methods). The basic |
michael@0 | 93 | * pattern is as follows. |
michael@0 | 94 | * |
michael@0 | 95 | * // On the main thread: |
michael@0 | 96 | * nsCOMPtr<nsIFooCallback> callback = ...; |
michael@0 | 97 | * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle = |
michael@0 | 98 | * new nsMainThreadPtrHolder<nsIFooCallback>(callback); |
michael@0 | 99 | * // Pass callbackHandle to structs/classes that might be accessed on other |
michael@0 | 100 | * // threads. |
michael@0 | 101 | * |
michael@0 | 102 | * All structs and classes that might be accessed on other threads should store |
michael@0 | 103 | * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>. |
michael@0 | 104 | */ |
michael@0 | 105 | template<class T> |
michael@0 | 106 | class nsMainThreadPtrHolder MOZ_FINAL |
michael@0 | 107 | { |
michael@0 | 108 | public: |
michael@0 | 109 | // We can only acquire a pointer on the main thread. We to fail fast for |
michael@0 | 110 | // threading bugs, so by default we assert if our pointer is used or acquired |
michael@0 | 111 | // off-main-thread. But some consumers need to use the same pointer for |
michael@0 | 112 | // multiple classes, some of which are main-thread-only and some of which |
michael@0 | 113 | // aren't. So we allow them to explicitly disable this strict checking. |
michael@0 | 114 | nsMainThreadPtrHolder(T* ptr, bool strict = true) : mRawPtr(nullptr), mStrict(strict) { |
michael@0 | 115 | // We can only AddRef our pointer on the main thread, which means that the |
michael@0 | 116 | // holder must be constructed on the main thread. |
michael@0 | 117 | MOZ_ASSERT(!mStrict || NS_IsMainThread()); |
michael@0 | 118 | NS_IF_ADDREF(mRawPtr = ptr); |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | // We can be released on any thread. |
michael@0 | 122 | ~nsMainThreadPtrHolder() { |
michael@0 | 123 | if (NS_IsMainThread()) { |
michael@0 | 124 | NS_IF_RELEASE(mRawPtr); |
michael@0 | 125 | } else if (mRawPtr) { |
michael@0 | 126 | nsCOMPtr<nsIThread> mainThread; |
michael@0 | 127 | NS_GetMainThread(getter_AddRefs(mainThread)); |
michael@0 | 128 | if (!mainThread) { |
michael@0 | 129 | NS_WARNING("Couldn't get main thread! Leaking pointer."); |
michael@0 | 130 | return; |
michael@0 | 131 | } |
michael@0 | 132 | NS_ProxyRelease(mainThread, mRawPtr); |
michael@0 | 133 | } |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | T* get() { |
michael@0 | 137 | // Nobody should be touching the raw pointer off-main-thread. |
michael@0 | 138 | if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) { |
michael@0 | 139 | NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread"); |
michael@0 | 140 | MOZ_CRASH(); |
michael@0 | 141 | } |
michael@0 | 142 | return mRawPtr; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | bool operator==(const nsMainThreadPtrHolder<T>& aOther) const { return mRawPtr == aOther.mRawPtr; } |
michael@0 | 146 | |
michael@0 | 147 | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>) |
michael@0 | 148 | |
michael@0 | 149 | private: |
michael@0 | 150 | // Our wrapped pointer. |
michael@0 | 151 | T* mRawPtr; |
michael@0 | 152 | |
michael@0 | 153 | // Whether to strictly enforce thread invariants in this class. |
michael@0 | 154 | bool mStrict; |
michael@0 | 155 | |
michael@0 | 156 | // Copy constructor and operator= not implemented. Once constructed, the |
michael@0 | 157 | // holder is immutable. |
michael@0 | 158 | T& operator=(nsMainThreadPtrHolder& other); |
michael@0 | 159 | nsMainThreadPtrHolder(const nsMainThreadPtrHolder& other); |
michael@0 | 160 | }; |
michael@0 | 161 | |
michael@0 | 162 | template<class T> |
michael@0 | 163 | class nsMainThreadPtrHandle |
michael@0 | 164 | { |
michael@0 | 165 | nsRefPtr<nsMainThreadPtrHolder<T> > mPtr; |
michael@0 | 166 | |
michael@0 | 167 | public: |
michael@0 | 168 | nsMainThreadPtrHandle() : mPtr(nullptr) {} |
michael@0 | 169 | nsMainThreadPtrHandle(nsMainThreadPtrHolder<T> *aHolder) : mPtr(aHolder) {} |
michael@0 | 170 | nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther) : mPtr(aOther.mPtr) {} |
michael@0 | 171 | nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther) { |
michael@0 | 172 | mPtr = aOther.mPtr; |
michael@0 | 173 | return *this; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | // These all call through to nsMainThreadPtrHolder, and thus implicitly |
michael@0 | 177 | // assert that we're on the main thread. Off-main-thread consumers must treat |
michael@0 | 178 | // these handles as opaque. |
michael@0 | 179 | T* get() |
michael@0 | 180 | { |
michael@0 | 181 | if (mPtr) { |
michael@0 | 182 | return mPtr.get()->get(); |
michael@0 | 183 | } |
michael@0 | 184 | return nullptr; |
michael@0 | 185 | } |
michael@0 | 186 | const T* get() const |
michael@0 | 187 | { |
michael@0 | 188 | if (mPtr) { |
michael@0 | 189 | return mPtr.get()->get(); |
michael@0 | 190 | } |
michael@0 | 191 | return nullptr; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | operator T*() { return get(); } |
michael@0 | 195 | T* operator->() { return get(); } |
michael@0 | 196 | |
michael@0 | 197 | // These are safe to call on other threads with appropriate external locking. |
michael@0 | 198 | bool operator==(const nsMainThreadPtrHandle<T>& aOther) const { |
michael@0 | 199 | if (!mPtr || !aOther.mPtr) |
michael@0 | 200 | return mPtr == aOther.mPtr; |
michael@0 | 201 | return *mPtr == *aOther.mPtr; |
michael@0 | 202 | } |
michael@0 | 203 | bool operator!() { return !mPtr; } |
michael@0 | 204 | }; |
michael@0 | 205 | |
michael@0 | 206 | #endif |