xpcom/glue/nsProxyRelease.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/glue/nsProxyRelease.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,206 @@
     1.4 +/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifndef nsProxyRelease_h__
    1.10 +#define nsProxyRelease_h__
    1.11 +
    1.12 +#include "nsIEventTarget.h"
    1.13 +#include "nsIThread.h"
    1.14 +#include "nsCOMPtr.h"
    1.15 +#include "nsAutoPtr.h"
    1.16 +#include "MainThreadUtils.h"
    1.17 +#include "mozilla/Likely.h"
    1.18 +
    1.19 +#ifdef XPCOM_GLUE_AVOID_NSPR
    1.20 +#error NS_ProxyRelease implementation depends on NSPR.
    1.21 +#endif
    1.22 +
    1.23 +/**
    1.24 + * Ensure that a nsCOMPtr is released on the target thread.
    1.25 + *
    1.26 + * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool)
    1.27 + */
    1.28 +template <class T>
    1.29 +inline NS_HIDDEN_(nsresult)
    1.30 +NS_ProxyRelease
    1.31 +    (nsIEventTarget *target, nsCOMPtr<T> &doomed, bool alwaysProxy=false)
    1.32 +{
    1.33 +   T* raw = nullptr;
    1.34 +   doomed.swap(raw);
    1.35 +   return NS_ProxyRelease(target, raw, alwaysProxy);
    1.36 +}
    1.37 +
    1.38 +/**
    1.39 + * Ensure that a nsRefPtr is released on the target thread.
    1.40 + *
    1.41 + * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool)
    1.42 + */
    1.43 +template <class T>
    1.44 +inline NS_HIDDEN_(nsresult)
    1.45 +NS_ProxyRelease
    1.46 +    (nsIEventTarget *target, nsRefPtr<T> &doomed, bool alwaysProxy=false)
    1.47 +{
    1.48 +   T* raw = nullptr;
    1.49 +   doomed.swap(raw);
    1.50 +   return NS_ProxyRelease(target, raw, alwaysProxy);
    1.51 +}
    1.52 +
    1.53 +/**
    1.54 + * Ensures that the delete of a nsISupports object occurs on the target thread.
    1.55 + *
    1.56 + * @param target
    1.57 + *        the target thread where the doomed object should be released.
    1.58 + * @param doomed
    1.59 + *        the doomed object; the object to be released on the target thread.
    1.60 + * @param alwaysProxy
    1.61 + *        normally, if NS_ProxyRelease is called on the target thread, then the
    1.62 + *        doomed object will released directly.  however, if this parameter is
    1.63 + *        true, then an event will always be posted to the target thread for
    1.64 + *        asynchronous release.
    1.65 + */
    1.66 +NS_COM_GLUE nsresult
    1.67 +NS_ProxyRelease
    1.68 +    (nsIEventTarget *target, nsISupports *doomed, bool alwaysProxy=false);
    1.69 +
    1.70 +/**
    1.71 + * Class to safely handle main-thread-only pointers off the main thread.
    1.72 + *
    1.73 + * Classes like XPCWrappedJS are main-thread-only, which means that it is
    1.74 + * forbidden to call methods on instances of these classes off the main thread.
    1.75 + * For various reasons (see bug 771074), this restriction recently began to
    1.76 + * apply to AddRef/Release as well.
    1.77 + *
    1.78 + * This presents a problem for consumers that wish to hold a callback alive
    1.79 + * on non-main-thread code. A common example of this is the proxy callback
    1.80 + * pattern, where non-main-thread code holds a strong-reference to the callback
    1.81 + * object, and dispatches new Runnables (also with a strong reference) to the
    1.82 + * main thread in order to execute the callback. This involves several AddRef
    1.83 + * and Release calls on the other thread, which is (now) verboten.
    1.84 + *
    1.85 + * The basic idea of this class is to introduce a layer of indirection.
    1.86 + * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
    1.87 + * maintains one strong reference to the main-thread-only object. It must be
    1.88 + * instantiated on the main thread (so that the AddRef of the underlying object
    1.89 + * happens on the main thread), but consumers may subsequently pass references
    1.90 + * to the holder anywhere they please. These references are meant to be opaque
    1.91 + * when accessed off-main-thread (assertions enforce this).
    1.92 + *
    1.93 + * The semantics of nsRefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
    1.94 + * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
    1.95 + * to the above (though it includes various convenience methods). The basic
    1.96 + * pattern is as follows.
    1.97 + *
    1.98 + * // On the main thread:
    1.99 + * nsCOMPtr<nsIFooCallback> callback = ...;
   1.100 + * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
   1.101 + *   new nsMainThreadPtrHolder<nsIFooCallback>(callback);
   1.102 + * // Pass callbackHandle to structs/classes that might be accessed on other
   1.103 + * // threads.
   1.104 + *
   1.105 + * All structs and classes that might be accessed on other threads should store
   1.106 + * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
   1.107 + */
   1.108 +template<class T>
   1.109 +class nsMainThreadPtrHolder MOZ_FINAL
   1.110 +{
   1.111 +public:
   1.112 +  // We can only acquire a pointer on the main thread. We to fail fast for
   1.113 +  // threading bugs, so by default we assert if our pointer is used or acquired
   1.114 +  // off-main-thread. But some consumers need to use the same pointer for
   1.115 +  // multiple classes, some of which are main-thread-only and some of which
   1.116 +  // aren't. So we allow them to explicitly disable this strict checking.
   1.117 +  nsMainThreadPtrHolder(T* ptr, bool strict = true) : mRawPtr(nullptr), mStrict(strict) {
   1.118 +    // We can only AddRef our pointer on the main thread, which means that the
   1.119 +    // holder must be constructed on the main thread.
   1.120 +    MOZ_ASSERT(!mStrict || NS_IsMainThread());
   1.121 +    NS_IF_ADDREF(mRawPtr = ptr);
   1.122 +  }
   1.123 +
   1.124 +  // We can be released on any thread.
   1.125 +  ~nsMainThreadPtrHolder() {
   1.126 +    if (NS_IsMainThread()) {
   1.127 +      NS_IF_RELEASE(mRawPtr);
   1.128 +    } else if (mRawPtr) {
   1.129 +      nsCOMPtr<nsIThread> mainThread;
   1.130 +      NS_GetMainThread(getter_AddRefs(mainThread));
   1.131 +      if (!mainThread) {
   1.132 +        NS_WARNING("Couldn't get main thread! Leaking pointer.");
   1.133 +        return;
   1.134 +      }
   1.135 +      NS_ProxyRelease(mainThread, mRawPtr);
   1.136 +    }
   1.137 +  }
   1.138 +
   1.139 +  T* get() {
   1.140 +    // Nobody should be touching the raw pointer off-main-thread.
   1.141 +    if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
   1.142 +      NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
   1.143 +      MOZ_CRASH();
   1.144 +    }
   1.145 +    return mRawPtr;
   1.146 +  }
   1.147 +
   1.148 +  bool operator==(const nsMainThreadPtrHolder<T>& aOther) const { return mRawPtr == aOther.mRawPtr; }
   1.149 +
   1.150 +  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
   1.151 +
   1.152 +private:
   1.153 +  // Our wrapped pointer.
   1.154 +  T* mRawPtr;
   1.155 +
   1.156 +  // Whether to strictly enforce thread invariants in this class.
   1.157 +  bool mStrict;
   1.158 +
   1.159 +  // Copy constructor and operator= not implemented. Once constructed, the
   1.160 +  // holder is immutable.
   1.161 +  T& operator=(nsMainThreadPtrHolder& other);
   1.162 +  nsMainThreadPtrHolder(const nsMainThreadPtrHolder& other);
   1.163 +};
   1.164 +
   1.165 +template<class T>
   1.166 +class nsMainThreadPtrHandle
   1.167 +{
   1.168 +  nsRefPtr<nsMainThreadPtrHolder<T> > mPtr;
   1.169 +
   1.170 +  public:
   1.171 +  nsMainThreadPtrHandle() : mPtr(nullptr) {}
   1.172 +  nsMainThreadPtrHandle(nsMainThreadPtrHolder<T> *aHolder) : mPtr(aHolder) {}
   1.173 +  nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther) : mPtr(aOther.mPtr) {}
   1.174 +  nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther) {
   1.175 +    mPtr = aOther.mPtr;
   1.176 +    return *this;
   1.177 +  }
   1.178 +
   1.179 +  // These all call through to nsMainThreadPtrHolder, and thus implicitly
   1.180 +  // assert that we're on the main thread. Off-main-thread consumers must treat
   1.181 +  // these handles as opaque.
   1.182 +  T* get()
   1.183 +  {
   1.184 +    if (mPtr) {
   1.185 +      return mPtr.get()->get();
   1.186 +    }
   1.187 +    return nullptr;
   1.188 +  }
   1.189 +  const T* get() const
   1.190 +  {
   1.191 +    if (mPtr) {
   1.192 +      return mPtr.get()->get();
   1.193 +    }
   1.194 +    return nullptr;
   1.195 +  }
   1.196 +
   1.197 +  operator T*() { return get(); }
   1.198 +  T* operator->() { return get(); }
   1.199 +
   1.200 +  // These are safe to call on other threads with appropriate external locking.
   1.201 +  bool operator==(const nsMainThreadPtrHandle<T>& aOther) const {
   1.202 +    if (!mPtr || !aOther.mPtr)
   1.203 +      return mPtr == aOther.mPtr;
   1.204 +    return *mPtr == *aOther.mPtr;
   1.205 +  }
   1.206 +  bool operator!() { return !mPtr; }
   1.207 +};
   1.208 +
   1.209 +#endif

mercurial