nsProxyRelease.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #ifndef nsProxyRelease_h__
  6. #define nsProxyRelease_h__
  7. #include "nsIEventTarget.h"
  8. #include "nsIThread.h"
  9. #include "nsCOMPtr.h"
  10. #include "nsAutoPtr.h"
  11. #include "MainThreadUtils.h"
  12. #include "nsThreadUtils.h"
  13. #include "mozilla/Likely.h"
  14. #include "mozilla/Move.h"
  15. #include "mozilla/TypeTraits.h"
  16. #include "mozilla/Unused.h"
  17. #ifdef XPCOM_GLUE_AVOID_NSPR
  18. #error NS_ProxyRelease implementation depends on NSPR.
  19. #endif
  20. namespace detail {
  21. template<typename T>
  22. class ProxyReleaseEvent : public mozilla::Runnable
  23. {
  24. public:
  25. explicit ProxyReleaseEvent(already_AddRefed<T> aDoomed)
  26. : mDoomed(aDoomed.take()) {}
  27. NS_IMETHOD Run() override
  28. {
  29. NS_IF_RELEASE(mDoomed);
  30. return NS_OK;
  31. }
  32. private:
  33. T* MOZ_OWNING_REF mDoomed;
  34. };
  35. template<typename T>
  36. void
  37. ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed, bool aAlwaysProxy)
  38. {
  39. // Auto-managing release of the pointer.
  40. RefPtr<T> doomed = aDoomed;
  41. nsresult rv;
  42. if (!doomed || !aTarget) {
  43. return;
  44. }
  45. if (!aAlwaysProxy) {
  46. bool onCurrentThread = false;
  47. rv = aTarget->IsOnCurrentThread(&onCurrentThread);
  48. if (NS_SUCCEEDED(rv) && onCurrentThread) {
  49. return;
  50. }
  51. }
  52. nsCOMPtr<nsIRunnable> ev = new ProxyReleaseEvent<T>(doomed.forget());
  53. rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
  54. if (NS_FAILED(rv)) {
  55. NS_WARNING("failed to post proxy release event, leaking!");
  56. // It is better to leak the aDoomed object than risk crashing as
  57. // a result of deleting it on the wrong thread.
  58. }
  59. }
  60. template<bool nsISupportsBased>
  61. struct ProxyReleaseChooser
  62. {
  63. template<typename T>
  64. static void ProxyRelease(nsIEventTarget* aTarget,
  65. already_AddRefed<T> aDoomed,
  66. bool aAlwaysProxy)
  67. {
  68. ::detail::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
  69. }
  70. };
  71. template<>
  72. struct ProxyReleaseChooser<true>
  73. {
  74. // We need an intermediate step for handling classes with ambiguous
  75. // inheritance to nsISupports.
  76. template<typename T>
  77. static void ProxyRelease(nsIEventTarget* aTarget,
  78. already_AddRefed<T> aDoomed,
  79. bool aAlwaysProxy)
  80. {
  81. ProxyReleaseISupports(aTarget, ToSupports(aDoomed.take()), aAlwaysProxy);
  82. }
  83. static void ProxyReleaseISupports(nsIEventTarget* aTarget,
  84. nsISupports* aDoomed,
  85. bool aAlwaysProxy);
  86. };
  87. } // namespace detail
  88. /**
  89. * Ensures that the delete of a smart pointer occurs on the target thread.
  90. *
  91. * @param aTarget
  92. * the target thread where the doomed object should be released.
  93. * @param aDoomed
  94. * the doomed object; the object to be released on the target thread.
  95. * @param aAlwaysProxy
  96. * normally, if NS_ProxyRelease is called on the target thread, then the
  97. * doomed object will be released directly. However, if this parameter is
  98. * true, then an event will always be posted to the target thread for
  99. * asynchronous release.
  100. */
  101. template<class T>
  102. inline NS_HIDDEN_(void)
  103. NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed,
  104. bool aAlwaysProxy = false)
  105. {
  106. ::detail::ProxyReleaseChooser<mozilla::IsBaseOf<nsISupports, T>::value>
  107. ::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
  108. }
  109. /**
  110. * Ensures that the delete of a smart pointer occurs on the main thread.
  111. *
  112. * @param aDoomed
  113. * the doomed object; the object to be released on the main thread.
  114. * @param aAlwaysProxy
  115. * normally, if NS_ReleaseOnMainThread is called on the main thread,
  116. * then the doomed object will be released directly. However, if this
  117. * parameter is true, then an event will always be posted to the main
  118. * thread for asynchronous release.
  119. */
  120. template<class T>
  121. inline NS_HIDDEN_(void)
  122. NS_ReleaseOnMainThread(already_AddRefed<T> aDoomed,
  123. bool aAlwaysProxy = false)
  124. {
  125. // NS_ProxyRelease treats a null event target as "the current thread". So a
  126. // handle on the main thread is only necessary when we're not already on the
  127. // main thread or the release must happen asynchronously.
  128. nsCOMPtr<nsIThread> mainThread;
  129. if (!NS_IsMainThread() || aAlwaysProxy) {
  130. nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
  131. if (NS_FAILED(rv)) {
  132. MOZ_ASSERT_UNREACHABLE("Could not get main thread; leaking an object!");
  133. mozilla::Unused << aDoomed.take();
  134. return;
  135. }
  136. }
  137. NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy);
  138. }
  139. /**
  140. * Class to safely handle main-thread-only pointers off the main thread.
  141. *
  142. * Classes like XPCWrappedJS are main-thread-only, which means that it is
  143. * forbidden to call methods on instances of these classes off the main thread.
  144. * For various reasons (see bug 771074), this restriction recently began to
  145. * apply to AddRef/Release as well.
  146. *
  147. * This presents a problem for consumers that wish to hold a callback alive
  148. * on non-main-thread code. A common example of this is the proxy callback
  149. * pattern, where non-main-thread code holds a strong-reference to the callback
  150. * object, and dispatches new Runnables (also with a strong reference) to the
  151. * main thread in order to execute the callback. This involves several AddRef
  152. * and Release calls on the other thread, which is (now) verboten.
  153. *
  154. * The basic idea of this class is to introduce a layer of indirection.
  155. * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
  156. * maintains one strong reference to the main-thread-only object. It must be
  157. * instantiated on the main thread (so that the AddRef of the underlying object
  158. * happens on the main thread), but consumers may subsequently pass references
  159. * to the holder anywhere they please. These references are meant to be opaque
  160. * when accessed off-main-thread (assertions enforce this).
  161. *
  162. * The semantics of RefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
  163. * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
  164. * to the above (though it includes various convenience methods). The basic
  165. * pattern is as follows.
  166. *
  167. * // On the main thread:
  168. * nsCOMPtr<nsIFooCallback> callback = ...;
  169. * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
  170. * new nsMainThreadPtrHolder<nsIFooCallback>(callback);
  171. * // Pass callbackHandle to structs/classes that might be accessed on other
  172. * // threads.
  173. *
  174. * All structs and classes that might be accessed on other threads should store
  175. * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
  176. */
  177. template<class T>
  178. class nsMainThreadPtrHolder final
  179. {
  180. public:
  181. // We can only acquire a pointer on the main thread. We to fail fast for
  182. // threading bugs, so by default we assert if our pointer is used or acquired
  183. // off-main-thread. But some consumers need to use the same pointer for
  184. // multiple classes, some of which are main-thread-only and some of which
  185. // aren't. So we allow them to explicitly disable this strict checking.
  186. explicit nsMainThreadPtrHolder(T* aPtr, bool aStrict = true)
  187. : mRawPtr(nullptr)
  188. , mStrict(aStrict)
  189. {
  190. // We can only AddRef our pointer on the main thread, which means that the
  191. // holder must be constructed on the main thread.
  192. MOZ_ASSERT(!mStrict || NS_IsMainThread());
  193. NS_IF_ADDREF(mRawPtr = aPtr);
  194. }
  195. explicit nsMainThreadPtrHolder(already_AddRefed<T> aPtr, bool aString = true)
  196. : mRawPtr(aPtr.take())
  197. , mStrict(aString)
  198. {
  199. // Since we don't need to AddRef the pointer, this constructor is safe to
  200. // call on any thread.
  201. }
  202. private:
  203. // We can be released on any thread.
  204. ~nsMainThreadPtrHolder()
  205. {
  206. if (NS_IsMainThread()) {
  207. NS_IF_RELEASE(mRawPtr);
  208. } else if (mRawPtr) {
  209. NS_ReleaseOnMainThread(dont_AddRef(mRawPtr));
  210. }
  211. }
  212. public:
  213. T* get()
  214. {
  215. // Nobody should be touching the raw pointer off-main-thread.
  216. if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
  217. NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
  218. MOZ_CRASH();
  219. }
  220. return mRawPtr;
  221. }
  222. bool operator==(const nsMainThreadPtrHolder<T>& aOther) const
  223. {
  224. return mRawPtr == aOther.mRawPtr;
  225. }
  226. bool operator!() const
  227. {
  228. return !mRawPtr;
  229. }
  230. NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
  231. private:
  232. // Our wrapped pointer.
  233. T* mRawPtr;
  234. // Whether to strictly enforce thread invariants in this class.
  235. bool mStrict;
  236. // Copy constructor and operator= not implemented. Once constructed, the
  237. // holder is immutable.
  238. T& operator=(nsMainThreadPtrHolder& aOther);
  239. nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther);
  240. };
  241. template<class T>
  242. class nsMainThreadPtrHandle
  243. {
  244. RefPtr<nsMainThreadPtrHolder<T>> mPtr;
  245. public:
  246. nsMainThreadPtrHandle() : mPtr(nullptr) {}
  247. MOZ_IMPLICIT nsMainThreadPtrHandle(decltype(nullptr)) : mPtr(nullptr) {}
  248. explicit nsMainThreadPtrHandle(nsMainThreadPtrHolder<T>* aHolder)
  249. : mPtr(aHolder)
  250. {
  251. }
  252. explicit nsMainThreadPtrHandle(
  253. already_AddRefed<nsMainThreadPtrHolder<T>> aHolder)
  254. : mPtr(aHolder)
  255. {
  256. }
  257. nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther)
  258. : mPtr(aOther.mPtr)
  259. {
  260. }
  261. nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther)
  262. {
  263. mPtr = aOther.mPtr;
  264. return *this;
  265. }
  266. nsMainThreadPtrHandle& operator=(nsMainThreadPtrHolder<T>* aHolder)
  267. {
  268. mPtr = aHolder;
  269. return *this;
  270. }
  271. // These all call through to nsMainThreadPtrHolder, and thus implicitly
  272. // assert that we're on the main thread. Off-main-thread consumers must treat
  273. // these handles as opaque.
  274. T* get()
  275. {
  276. if (mPtr) {
  277. return mPtr.get()->get();
  278. }
  279. return nullptr;
  280. }
  281. const T* get() const
  282. {
  283. if (mPtr) {
  284. return mPtr.get()->get();
  285. }
  286. return nullptr;
  287. }
  288. operator T*() { return get(); }
  289. T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return get(); }
  290. // These are safe to call on other threads with appropriate external locking.
  291. bool operator==(const nsMainThreadPtrHandle<T>& aOther) const
  292. {
  293. if (!mPtr || !aOther.mPtr) {
  294. return mPtr == aOther.mPtr;
  295. }
  296. return *mPtr == *aOther.mPtr;
  297. }
  298. bool operator!=(const nsMainThreadPtrHandle<T>& aOther) const
  299. {
  300. return !operator==(aOther);
  301. }
  302. bool operator==(decltype(nullptr)) const { return mPtr == nullptr; }
  303. bool operator!=(decltype(nullptr)) const { return mPtr != nullptr; }
  304. bool operator!() const {
  305. return !mPtr || !*mPtr;
  306. }
  307. };
  308. namespace mozilla {
  309. template<typename T>
  310. using PtrHolder = nsMainThreadPtrHolder<T>;
  311. template<typename T>
  312. using PtrHandle = nsMainThreadPtrHandle<T>;
  313. } // namespace mozilla
  314. #endif