xpcom/glue/nsProxyRelease.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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

mercurial