Bug 1131445 - variadic NS_NewRunnableMethodWithArgs with storage&passing argument type decorators. r=waldo
authorGerald Squelart <from_mozilla@squelart.com>
Sun, 01 Mar 2015 18:12:00 +0100
changeset 231513 974ca22a87c646cbc94e915a4536630bbd9d0f4c
parent 231512 1c64893b5fddfdc39cef039eed22877146b4a5fe
child 231514 826578768e85f970ed276ec7414a6313747c64f1
push id56281
push usercbook@mozilla.com
push dateTue, 03 Mar 2015 08:05:01 +0000
treeherdermozilla-inbound@974ca22a87c6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1131445
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1131445 - variadic NS_NewRunnableMethodWithArgs with storage&passing argument type decorators. r=waldo
mfbt/TypeTraits.h
mfbt/tests/TestTypeTraits.cpp
xpcom/glue/nsThreadUtils.h
xpcom/glue/tests/gtest/TestThreadUtils.cpp
xpcom/glue/tests/gtest/moz.build
--- a/mfbt/TypeTraits.h
+++ b/mfbt/TypeTraits.h
@@ -953,16 +953,52 @@ struct RemoveExtent<T[]>
 template<typename T, decltype(sizeof(1)) N>
 struct RemoveExtent<T[N]>
 {
   typedef T Type;
 };
 
 /* 20.9.7.5 Pointer modifications [meta.trans.ptr] */
 
+namespace detail {
+
+template<typename T, typename CVRemoved>
+struct RemovePointerHelper
+{
+  typedef T Type;
+};
+
+template<typename T, typename Pointee>
+struct RemovePointerHelper<T, Pointee*>
+{
+  typedef Pointee Type;
+};
+
+} // namespac detail
+
+/**
+ * Produces the pointed-to type if a pointer is provided, else returns the input
+ * type.  Note that this does not dereference pointer-to-member pointers.
+ *
+ * struct S { bool m; void f(); };
+ * mozilla::RemovePointer<int>::Type is int;
+ * mozilla::RemovePointer<int*>::Type is int;
+ * mozilla::RemovePointer<int* const>::Type is int;
+ * mozilla::RemovePointer<int* volatile>::Type is int;
+ * mozilla::RemovePointer<const long*>::Type is const long;
+ * mozilla::RemovePointer<void* const>::Type is void;
+ * mozilla::RemovePointer<void (S::*)()>::Type is void (S::*)();
+ * mozilla::RemovePointer<void (*)()>::Type is void();
+ * mozilla::RemovePointer<bool S::*>::Type is bool S::*.
+ */
+template<typename T>
+struct RemovePointer
+  : detail::RemovePointerHelper<T, typename RemoveCV<T>::Type>
+{};
+
 /* 20.9.7.6 Other transformations [meta.trans.other] */
 
 /**
  * EnableIf is a struct containing a typedef of T if and only if B is true.
  *
  * mozilla::EnableIf<true, int>::Type is int;
  * mozilla::EnableIf<false, int>::Type is a compile-time error.
  *
--- a/mfbt/tests/TestTypeTraits.cpp
+++ b/mfbt/tests/TestTypeTraits.cpp
@@ -18,16 +18,17 @@ using mozilla::IsPointer;
 using mozilla::IsReference;
 using mozilla::IsRvalueReference;
 using mozilla::IsSame;
 using mozilla::IsSigned;
 using mozilla::IsUnsigned;
 using mozilla::MakeSigned;
 using mozilla::MakeUnsigned;
 using mozilla::RemoveExtent;
+using mozilla::RemovePointer;
 
 static_assert(!IsArray<bool>::value,
               "bool not an array");
 static_assert(IsArray<bool[]>::value,
               "bool[] is an array");
 static_assert(IsArray<bool[5]>::value,
               "bool[5] is an array");
 
@@ -425,16 +426,38 @@ static_assert(IsSame<RemoveExtent<int>::
               "removing extent from non-array must return the non-array");
 static_assert(IsSame<RemoveExtent<const int[]>::Type, const int>::value,
               "removing extent from unknown-bound array must return element type");
 static_assert(IsSame<RemoveExtent<volatile int[5]>::Type, volatile int>::value,
               "removing extent from known-bound array must return element type");
 static_assert(IsSame<RemoveExtent<long[][17]>::Type, long[17]>::value,
               "removing extent from multidimensional array must return element type");
 
+struct TestRemovePointer { bool m; void f(); };
+static_assert(IsSame<RemovePointer<int>::Type, int>::value,
+              "removing pointer from int must return int");
+static_assert(IsSame<RemovePointer<int*>::Type, int>::value,
+              "removing pointer from int* must return int");
+static_assert(IsSame<RemovePointer<int* const>::Type, int>::value,
+              "removing pointer from int* const must return int");
+static_assert(IsSame<RemovePointer<int* volatile>::Type, int>::value,
+              "removing pointer from int* volatile must return int");
+static_assert(IsSame<RemovePointer<const long*>::Type, const long>::value,
+              "removing pointer from const long* must return const long");
+static_assert(IsSame<RemovePointer<void* const>::Type, void>::value,
+              "removing pointer from void* const must return void");
+static_assert(IsSame<RemovePointer<void (TestRemovePointer::*)()>::Type,
+                                   void (TestRemovePointer::*)()>::value,
+              "removing pointer from void (S::*)() must return void (S::*)()");
+static_assert(IsSame<RemovePointer<void (*)()>::Type, void()>::value,
+              "removing pointer from void (*)() must return void()");
+static_assert(IsSame<RemovePointer<bool TestRemovePointer::*>::Type,
+                                   bool TestRemovePointer::*>::value,
+              "removing pointer from bool S::* must return bool S::*");
+
 /*
  * Android's broken [u]intptr_t inttype macros are broken because its PRI*PTR
  * macros are defined as "ld", but sizeof(long) is 8 and sizeof(intptr_t)
  * is 4 on 32-bit Android. We redefine Android's PRI*PTR macros in
  * IntegerPrintfMacros.h and assert here that our new definitions match the
  * actual type sizes seen at compile time.
  */
 #if defined(ANDROID) && !defined(__LP64__)
--- a/xpcom/glue/nsThreadUtils.h
+++ b/xpcom/glue/nsThreadUtils.h
@@ -263,180 +263,476 @@ public:
   {
     // No ReturnTypeIsSafe makes this illegal!
   };
 
   // Make sure this return type is safe.
   typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check;
 };
 
-template<class ClassType, typename Arg, bool Owning>
+template<class ClassType, bool Owning>
 struct nsRunnableMethodReceiver
 {
   nsRefPtr<ClassType> mObj;
-  Arg mArg;
-  nsRunnableMethodReceiver(ClassType* aObj, Arg aArg)
-    : mObj(aObj)
-    , mArg(aArg)
-  {
-  }
+  explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
   ~nsRunnableMethodReceiver() { Revoke(); }
-  void Revoke() { mObj = nullptr; }
-};
-
-template<class ClassType, bool Owning>
-struct nsRunnableMethodReceiver<ClassType, void, Owning>
-{
-  nsRefPtr<ClassType> mObj;
-  explicit nsRunnableMethodReceiver(ClassType* aObj)
-    : mObj(aObj)
-  {
-  }
-  ~nsRunnableMethodReceiver() { Revoke(); }
+  ClassType* Get() const { return mObj.get(); }
   void Revoke() { mObj = nullptr; }
 };
 
 template<class ClassType>
-struct nsRunnableMethodReceiver<ClassType, void, false>
+struct nsRunnableMethodReceiver<ClassType, false>
 {
   ClassType* MOZ_NON_OWNING_REF mObj;
   explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
+  ClassType* Get() const { return mObj; }
   void Revoke() { mObj = nullptr; }
 };
 
 template<typename Method, bool Owning> struct nsRunnableMethodTraits;
 
-template<class C, typename R, typename A, bool Owning>
-struct nsRunnableMethodTraits<R(C::*)(A), Owning>
+template<class C, typename R, bool Owning, typename... As>
+struct nsRunnableMethodTraits<R(C::*)(As...), Owning>
 {
   typedef C class_type;
   typedef R return_type;
-  typedef A arg_type;
-  typedef nsRunnableMethod<C, R, Owning> base_type;
-};
-
-template<class C, typename R, bool Owning>
-struct nsRunnableMethodTraits<R(C::*)(), Owning>
-{
-  typedef C class_type;
-  typedef R return_type;
-  typedef void arg_type;
   typedef nsRunnableMethod<C, R, Owning> base_type;
 };
 
 #ifdef NS_HAVE_STDCALL
-template<class C, typename R, typename A, bool Owning>
-struct nsRunnableMethodTraits<R(__stdcall C::*)(A), Owning>
+template<class C, typename R, bool Owning, typename... As>
+struct nsRunnableMethodTraits<R(__stdcall C::*)(As...), Owning>
 {
   typedef C class_type;
   typedef R return_type;
-  typedef A arg_type;
   typedef nsRunnableMethod<C, R, Owning> base_type;
 };
 
 template<class C, typename R, bool Owning>
 struct nsRunnableMethodTraits<R(NS_STDCALL C::*)(), Owning>
 {
   typedef C class_type;
   typedef R return_type;
-  typedef void arg_type;
   typedef nsRunnableMethod<C, R, Owning> base_type;
 };
 #endif
 
-template<typename Method, typename Arg, bool Owning>
+
+// IsParameterStorageClass<T>::value is true if T is a parameter-storage class
+// that will be recognized by NS_New[NonOwning]RunnableMethodWithArg[s] to
+// force a specific storage&passing strategy (instead of inferring one,
+// see ParameterStorage).
+// When creating a new storage class, add a specialization for it to be
+// recognized.
+template<typename T>
+struct IsParameterStorageClass : public mozilla::FalseType {};
+
+// StoreXPassByY structs used to inform nsRunnableMethodArguments how to
+// store arguments, and how to pass them to the target method.
+
+template<typename T>
+struct StoreCopyPassByValue
+{
+  typedef T stored_type;
+  typedef T passed_type;
+  stored_type m;
+  template <typename A>
+  StoreCopyPassByValue(A&& a) : m(mozilla::Forward<A>(a)) {}
+  passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByValue<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByConstLRef
+{
+  typedef T stored_type;
+  typedef const T& passed_type;
+  stored_type m;
+  template <typename A>
+  StoreCopyPassByConstLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+  passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByConstLRef<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByLRef
+{
+  typedef T stored_type;
+  typedef T& passed_type;
+  stored_type m;
+  template <typename A>
+  StoreCopyPassByLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+  passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByLRef<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByRRef
+{
+  typedef T stored_type;
+  typedef T&& passed_type;
+  stored_type m;
+  template <typename A>
+  StoreCopyPassByRRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+  passed_type PassAsParameter() { return mozilla::Move(m); }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByRRef<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreRefPassByLRef
+{
+  typedef T& stored_type;
+  typedef T& passed_type;
+  stored_type m;
+  template <typename A>
+  StoreRefPassByLRef(A& a) : m(a) {}
+  passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreRefPassByLRef<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreConstRefPassByConstLRef
+{
+  typedef const T& stored_type;
+  typedef const T& passed_type;
+  stored_type m;
+  template <typename A>
+  StoreConstRefPassByConstLRef(const A& a) : m(a) {}
+  passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreConstRefPassByConstLRef<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StorensRefPtrPassByPtr
+{
+  typedef nsRefPtr<T> stored_type;
+  typedef T* passed_type;
+  stored_type m;
+  template <typename A>
+  StorensRefPtrPassByPtr(A a) : m(a) {}
+  passed_type PassAsParameter() { return m.get(); }
+};
+template<typename S>
+struct IsParameterStorageClass<StorensRefPtrPassByPtr<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StorePtrPassByPtr
+{
+  typedef T* stored_type;
+  typedef T* passed_type;
+  stored_type m;
+  template <typename A>
+  StorePtrPassByPtr(A a) : m(a) {}
+  passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StorePtrPassByPtr<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreConstPtrPassByConstPtr
+{
+  typedef const T* stored_type;
+  typedef const T* passed_type;
+  stored_type m;
+  template <typename A>
+  StoreConstPtrPassByConstPtr(A a) : m(a) {}
+  passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreConstPtrPassByConstPtr<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByConstPtr
+{
+  typedef T stored_type;
+  typedef const T* passed_type;
+  stored_type m;
+  template <typename A>
+  StoreCopyPassByConstPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+  passed_type PassAsParameter() { return &m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByConstPtr<S>>
+  : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByPtr
+{
+  typedef T stored_type;
+  typedef T* passed_type;
+  stored_type m;
+  template <typename A>
+  StoreCopyPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+  passed_type PassAsParameter() { return &m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByPtr<S>>
+  : public mozilla::TrueType {};
+
+namespace detail {
+
+template<typename TWithoutPointer>
+struct NonnsISupportsPointerStorageClass
+  : mozilla::Conditional<mozilla::IsConst<TWithoutPointer>::value,
+                         StoreConstPtrPassByConstPtr<
+                           typename mozilla::RemoveConst<TWithoutPointer>::Type>,
+                         StorePtrPassByPtr<TWithoutPointer>>
+{};
+
+template<typename TWithoutPointer>
+struct PointerStorageClass
+  : mozilla::Conditional<mozilla::IsBaseOf<nsISupports, TWithoutPointer>::value,
+                         StorensRefPtrPassByPtr<TWithoutPointer>,
+                         typename NonnsISupportsPointerStorageClass<
+                           TWithoutPointer
+                         >::Type>
+{};
+
+template<typename TWithoutRef>
+struct LValueReferenceStorageClass
+  : mozilla::Conditional<mozilla::IsConst<TWithoutRef>::value,
+                         StoreConstRefPassByConstLRef<
+                           typename mozilla::RemoveConst<TWithoutRef>::Type>,
+                         StoreRefPassByLRef<TWithoutRef>>
+{};
+
+template<typename T>
+struct NonLValueReferenceStorageClass
+  : mozilla::Conditional<mozilla::IsRvalueReference<T>::value,
+                         StoreCopyPassByRRef<
+                           typename mozilla::RemoveReference<T>::Type>,
+                         StoreCopyPassByValue<T>>
+{};
+
+template<typename T>
+struct NonPointerStorageClass
+  : mozilla::Conditional<mozilla::IsLvalueReference<T>::value,
+                         typename LValueReferenceStorageClass<
+                           typename mozilla::RemoveReference<T>::Type
+                         >::Type,
+                         typename NonLValueReferenceStorageClass<T>::Type>
+{};
+
+template<typename T>
+struct NonParameterStorageClass
+  : mozilla::Conditional<mozilla::IsPointer<T>::value,
+                         typename PointerStorageClass<
+                           typename mozilla::RemovePointer<T>::Type
+                         >::Type,
+                         typename NonPointerStorageClass<T>::Type>
+{};
+
+// Choose storage&passing strategy based on preferred storage type:
+// - If IsParameterStorageClass<T>::value is true, use as-is.
+// - nsISupports* -> StorensRefPtrPassByPtr<T>   : Store nsRefPtr<T>, pass T*
+// - const T*  -> StoreConstPtrPassByConstPtr<T> : Store const T*, pass const T*
+// - T*        -> StorePtrPassByPtr<T>           : Store T*, pass T*.
+// - const T&  -> StoreConstRefPassByConstLRef<T>: Store const T&, pass const T&.
+// - T&        -> StoreRefPassByLRef<T>          : Store T&, pass T&.
+// - T&&       -> StoreCopyPassByRRef<T>         : Store T, pass Move(T).
+// - Other T   -> StoreCopyPassByValue<T>        : Store T, pass T.
+// Other available explicit options:
+// -              StoreCopyPassByConstLRef<T>    : Store T, pass const T&.
+// -              StoreCopyPassByLRef<T>         : Store T, pass T& (of copy!)
+// -              StoreCopyPassByConstPtr<T>     : Store T, pass const T*
+// -              StoreCopyPassByPtr<T>          : Store T, pass T* (of copy!)
+// Or create your own class with PassAsParameter() method, optional
+// clean-up in destructor, and with associated IsParameterStorageClass<>.
+template<typename T>
+struct ParameterStorage
+  : mozilla::Conditional<IsParameterStorageClass<T>::value,
+                         T,
+                         typename NonParameterStorageClass<T>::Type>
+{};
+
+} /* namespace detail */
+
+// struct used to store arguments and later apply them to a method.
+template <typename... Ts> struct nsRunnableMethodArguments;
+
+// Specializations for 0-4 arguments, add more as required.
+// TODO Use tuple instead; And/or use lambdas.
+template <>
+struct nsRunnableMethodArguments<>
+{
+  template<class C, typename M> void apply(C* o, M m)
+  {
+    ((*o).*m)();
+  }
+};
+template <typename T0>
+struct nsRunnableMethodArguments<T0>
+{
+  typename ::detail::ParameterStorage<T0>::Type m0;
+  template<typename A0>
+  nsRunnableMethodArguments(A0&& a0)
+    : m0(mozilla::Forward<A0>(a0))
+  {}
+  template<class C, typename M> void apply(C* o, M m)
+  {
+    ((*o).*m)(m0.PassAsParameter());
+  }
+};
+template <typename T0, typename T1>
+struct nsRunnableMethodArguments<T0, T1>
+{
+  typename ::detail::ParameterStorage<T0>::Type m0;
+  typename ::detail::ParameterStorage<T1>::Type m1;
+  template<typename A0, typename A1>
+  nsRunnableMethodArguments(A0&& a0, A1&& a1)
+    : m0(mozilla::Forward<A0>(a0))
+    , m1(mozilla::Forward<A1>(a1))
+  {}
+  template<class C, typename M> void apply(C* o, M m)
+  {
+    ((*o).*m)(m0.PassAsParameter(), m1.PassAsParameter());
+  }
+};
+template <typename T0, typename T1, typename T2>
+struct nsRunnableMethodArguments<T0, T1, T2>
+{
+  typename ::detail::ParameterStorage<T0>::Type m0;
+  typename ::detail::ParameterStorage<T1>::Type m1;
+  typename ::detail::ParameterStorage<T2>::Type m2;
+  template<typename A0, typename A1, typename A2>
+  nsRunnableMethodArguments(A0&& a0, A1&& a1, A2&& a2)
+    : m0(mozilla::Forward<A0>(a0))
+    , m1(mozilla::Forward<A1>(a1))
+    , m2(mozilla::Forward<A2>(a2))
+  {}
+  template<class C, typename M> void apply(C* o, M m)
+  {
+    ((*o).*m)(m0.PassAsParameter(), m1.PassAsParameter(), m2.PassAsParameter());
+  }
+};
+template <typename T0, typename T1, typename T2, typename T3>
+struct nsRunnableMethodArguments<T0, T1, T2, T3>
+{
+  typename ::detail::ParameterStorage<T0>::Type m0;
+  typename ::detail::ParameterStorage<T1>::Type m1;
+  typename ::detail::ParameterStorage<T2>::Type m2;
+  typename ::detail::ParameterStorage<T3>::Type m3;
+  template<typename A0, typename A1, typename A2, typename A3>
+  nsRunnableMethodArguments(A0&& a0, A1&& a1, A2&& a2, A3&& a3)
+    : m0(mozilla::Forward<A0>(a0))
+    , m1(mozilla::Forward<A1>(a1))
+    , m2(mozilla::Forward<A2>(a2))
+    , m3(mozilla::Forward<A3>(a3))
+  {}
+  template<class C, typename M> void apply(C* o, M m)
+  {
+    ((*o).*m)(m0.PassAsParameter(), m1.PassAsParameter(),
+              m2.PassAsParameter(), m3.PassAsParameter());
+  }
+};
+
+template<typename Method, bool Owning, typename... Storages>
 class nsRunnableMethodImpl
   : public nsRunnableMethodTraits<Method, Owning>::base_type
 {
-  typedef typename nsRunnableMethodTraits<Method, Owning>::class_type ClassType;
-  nsRunnableMethodReceiver<ClassType, Arg, Owning> mReceiver;
+  typedef typename nsRunnableMethodTraits<Method, Owning>::class_type
+      ClassType;
+  nsRunnableMethodReceiver<ClassType, Owning> mReceiver;
   Method mMethod;
+  nsRunnableMethodArguments<Storages...> mArgs;
 public:
-  nsRunnableMethodImpl(ClassType* aObj, Method aMethod, Arg aArg)
-    : mReceiver(aObj, aArg)
+  virtual ~nsRunnableMethodImpl() { Revoke(); };
+  template<typename... Args>
+  explicit nsRunnableMethodImpl(ClassType* aObj, Method aMethod,
+                                Args&&... aArgs)
+    : mReceiver(aObj)
     , mMethod(aMethod)
+    , mArgs(mozilla::Forward<Args>(aArgs)...)
   {
+    static_assert(sizeof...(Storages) == sizeof...(Args), "Storages and Args should have equal sizes");
   }
   NS_IMETHOD Run()
   {
-    if (MOZ_LIKELY(mReceiver.mObj)) {
-      ((*mReceiver.mObj).*mMethod)(mReceiver.mArg);
+    if (MOZ_LIKELY(mReceiver.Get())) {
+      mArgs.apply(mReceiver.Get(), mMethod);
     }
     return NS_OK;
   }
   void Revoke() { mReceiver.Revoke(); }
 };
 
-template<typename Method, bool Owning>
-class nsRunnableMethodImpl<Method, void, Owning>
-  : public nsRunnableMethodTraits<Method, Owning>::base_type
-{
-  typedef typename nsRunnableMethodTraits<Method, Owning>::class_type ClassType;
-  nsRunnableMethodReceiver<ClassType, void, Owning> mReceiver;
-  Method mMethod;
-
-public:
-  nsRunnableMethodImpl(ClassType* aObj, Method aMethod)
-    : mReceiver(aObj)
-    , mMethod(aMethod)
-  {
-  }
-
-  NS_IMETHOD Run()
-  {
-    if (MOZ_LIKELY(mReceiver.mObj)) {
-      ((*mReceiver.mObj).*mMethod)();
-    }
-    return NS_OK;
-  }
-
-  void Revoke() { mReceiver.Revoke(); }
-};
-
 // Use this template function like so:
 //
 //   nsCOMPtr<nsIRunnable> event =
 //     NS_NewRunnableMethod(myObject, &MyClass::HandleEvent);
 //   NS_DispatchToCurrentThread(event);
 //
 // Statically enforced constraints:
 //  - myObject must be of (or implicitly convertible to) type MyClass
 //  - MyClass must defined AddRef and Release methods
 //
 template<typename PtrType, typename Method>
 typename nsRunnableMethodTraits<Method, true>::base_type*
 NS_NewRunnableMethod(PtrType aPtr, Method aMethod)
 {
-  return new nsRunnableMethodImpl<Method, void, true>(aPtr, aMethod);
-}
-
-template<typename T>
-struct dependent_type
-{
-  typedef T type;
-};
-
-
-// Similar to NS_NewRunnableMethod. Call like so:
-// Type myArg;
-// nsCOMPtr<nsIRunnable> event =
-//   NS_NewRunnableMethodWithArg<Type>(myObject, &MyClass::HandleEvent, myArg);
-template<typename Arg, typename Method, typename PtrType>
-typename nsRunnableMethodTraits<Method, true>::base_type*
-NS_NewRunnableMethodWithArg(PtrType&& aPtr, Method aMethod,
-                            typename dependent_type<Arg>::type aArg)
-{
-  return new nsRunnableMethodImpl<Method, Arg, true>(aPtr, aMethod, aArg);
+  return new nsRunnableMethodImpl<Method, true>(aPtr, aMethod);
 }
 
 template<typename PtrType, typename Method>
 typename nsRunnableMethodTraits<Method, false>::base_type*
 NS_NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod)
 {
-  return new nsRunnableMethodImpl<Method, void, false>(aPtr, aMethod);
+  return new nsRunnableMethodImpl<Method, false>(aPtr, aMethod);
+}
+
+// Similar to NS_NewRunnableMethod. Call like so:
+// nsCOMPtr<nsIRunnable> event =
+//   NS_NewRunnableMethodWithArg<Type>(myObject, &MyClass::HandleEvent, myArg);
+// 'Type' is the stored type for the argument, see ParameterStorage for details.
+template<typename Storage, typename Method, typename PtrType, typename Arg>
+typename nsRunnableMethodTraits<Method, true>::base_type*
+NS_NewRunnableMethodWithArg(PtrType&& aPtr, Method aMethod, Arg&& aArg)
+{
+  return new nsRunnableMethodImpl<Method, true, Storage>(
+      aPtr, aMethod, mozilla::Forward<Arg>(aArg));
+}
+
+// Similar to NS_NewRunnableMethod. Call like so:
+// nsCOMPtr<nsIRunnable> event =
+//   NS_NewRunnableMethodWithArg<Types,...>(myObject, &MyClass::HandleEvent, myArg1,...);
+// 'Types' are the stored type for each argument, see ParameterStorage for details.
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+typename nsRunnableMethodTraits<Method, true>::base_type*
+NS_NewRunnableMethodWithArgs(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+  static_assert(sizeof...(Storages) == sizeof...(Args),
+                "<Storages...> size should be equal to number of arguments");
+  return new nsRunnableMethodImpl<Method, true, Storages...>(
+      aPtr, aMethod, mozilla::Forward<Args>(aArgs)...);
+}
+
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+typename nsRunnableMethodTraits<Method, true>::base_type*
+NS_NewNonOwningRunnableMethodWithArgs(PtrType&& aPtr, Method aMethod,
+                                      Args&&... aArgs)
+{
+  static_assert(sizeof...(Storages) == sizeof...(Args),
+                "<Storages...> size should be equal to number of arguments");
+  return new nsRunnableMethodImpl<Method, false, Storages...>(
+      aPtr, aMethod, mozilla::Forward<Args>(aArgs)...);
 }
 
 #endif  // XPCOM_GLUE_AVOID_NSPR
 
 // This class is designed to be used when you have an event class E that has a
 // pointer back to resource class R.  If R goes away while E is still pending,
 // then it is important to "revoke" E so that it does not try use R after R has
 // been destroyed.  nsRevocableEventPtr makes it easy for R to manage such
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestThreadUtils.cpp
@@ -0,0 +1,880 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "nsThreadUtils.h"
+#include "gtest/gtest.h"
+
+// {9e70a320-be02-11d1-8031-006008159b5a}
+#define NS_IFOO_IID \
+  {0x9e70a320, 0xbe02, 0x11d1,    \
+    {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
+
+namespace TestThreadUtils {
+
+static bool gDebug = false;
+static int gAlive, gZombies;
+static int gAllConstructions, gConstructions, gCopyConstructions,
+           gMoveConstructions, gDestructions, gAssignments, gMoves;
+struct Spy
+{
+  static void ClearActions()
+  {
+    gAllConstructions = gConstructions = gCopyConstructions
+        = gMoveConstructions = gDestructions = gAssignments = gMoves = 0;
+  }
+  static void ClearAll()
+  {
+    ClearActions();
+    gAlive = 0;
+  }
+
+  explicit Spy(int aID) : mID(aID)
+  {
+    ++gAlive; ++gAllConstructions; ++gConstructions;
+    if (gDebug) { printf("Spy[%d@%p]()\n", mID, this); }
+  }
+  Spy(const Spy& o) : mID(o.mID)
+  {
+    ++gAlive; ++gAllConstructions; ++gCopyConstructions;
+    if (gDebug) { printf("Spy[%d@%p](&[%d@%p])\n", mID, this, o.mID, &o); }
+  }
+  Spy(Spy&& o) : mID(o.mID)
+  {
+    o.mID = -o.mID;
+    ++gZombies; ++gAllConstructions; ++gMoveConstructions;
+    if (gDebug) { printf("Spy[%d@%p](&&[%d->%d@%p])\n", mID, this, -o.mID, o.mID, &o); }
+  }
+  ~Spy()
+  {
+    if (mID >= 0) { --gAlive; } else { --gZombies; } ++gDestructions;
+    if (gDebug) { printf("~Spy[%d@%p]()\n", mID, this); }
+    mID = 0;
+  }
+  Spy& operator=(const Spy& o)
+  {
+    ++gAssignments;
+    if (gDebug) { printf("Spy[%d->%d@%p] = &[%d@%p]\n", mID, o.mID, this, o.mID, &o); }
+    mID = o.mID;
+    return *this;
+  };
+  Spy& operator=(Spy&& o)
+  {
+    --gAlive; ++gZombies;
+    ++gMoves;
+    if (gDebug) { printf("Spy[%d->%d@%p] = &&[%d->%d@%p]\n", mID, o.mID, this, o.mID, -o.mID, &o); }
+    mID = o.mID; o.mID = -o.mID;
+    return *this;
+  };
+
+  int mID; // ID given at construction, or negation if was moved from; 0 when destroyed.
+};
+
+struct ISpyWithISupports : public nsISupports
+{
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+  NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+  NS_IMETHOD_(int32_t) ID() = 0;
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(ISpyWithISupports, NS_IFOO_IID)
+struct SpyWithISupports : public ISpyWithISupports, public Spy
+{
+private:
+  virtual ~SpyWithISupports() = default;
+public:
+  explicit SpyWithISupports(int aID) : Spy(aID) {};
+  NS_DECL_ISUPPORTS
+  NS_IMETHOD_(nsrefcnt) RefCnt() MOZ_OVERRIDE { return mRefCnt; }
+  NS_IMETHOD_(int32_t) ID() MOZ_OVERRIDE { return mID; }
+};
+NS_IMPL_ISUPPORTS(SpyWithISupports, ISpyWithISupports)
+
+
+class IThreadUtilsObject : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+  NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+  NS_IMETHOD_(int32_t) ID() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IThreadUtilsObject, NS_IFOO_IID)
+
+struct ThreadUtilsObject : public IThreadUtilsObject
+{
+  // nsISupports implementation
+  NS_DECL_ISUPPORTS
+
+  // IThreadUtilsObject implementation
+  NS_IMETHOD_(nsrefcnt) RefCnt() MOZ_OVERRIDE { return mRefCnt; }
+  NS_IMETHOD_(int32_t) ID() MOZ_OVERRIDE { return 0; }
+
+  int mCount; // Number of calls + arguments processed.
+  int mA0, mA1, mA2, mA3;
+  Spy mSpy; const Spy* mSpyPtr;
+  ThreadUtilsObject()
+    : mCount(0)
+    , mA0(0), mA1(0), mA2(0), mA3(0)
+    , mSpy(1), mSpyPtr(nullptr)
+  {}
+private:
+  virtual ~ThreadUtilsObject() = default;
+public:
+  void Test0() { mCount += 1; }
+  void Test1i(int a0) { mCount += 2; mA0 = a0; }
+  void Test2i(int a0, int a1) { mCount += 3; mA0 = a0; mA1 = a1; }
+  void Test3i(int a0, int a1, int a2)
+  {
+    mCount += 4; mA0 = a0; mA1 = a1; mA2 = a2;
+  }
+  void Test4i(int a0, int a1, int a2, int a3)
+  {
+    mCount += 5; mA0 = a0; mA1 = a1; mA2 = a2; mA3 = a3;
+  }
+  void Test1pi(int* ap)
+  {
+    mCount += 2; mA0 = ap ? *ap : -1;
+  }
+  void Test1pci(const int* ap)
+  {
+    mCount += 2; mA0 = ap ? *ap : -1;
+  }
+  void Test1ri(int& ar)
+  {
+    mCount += 2; mA0 = ar;
+  }
+  void Test1rri(int&& arr)
+  {
+    mCount += 2; mA0 = arr;
+  }
+  void Test1upi(mozilla::UniquePtr<int> aup)
+  {
+    mCount += 2; mA0 = aup ? *aup : -1;
+  }
+  void Test1rupi(mozilla::UniquePtr<int>& aup)
+  {
+    mCount += 2; mA0 = aup ? *aup : -1;
+  }
+  void Test1rrupi(mozilla::UniquePtr<int>&& aup)
+  {
+    mCount += 2; mA0 = aup ? *aup : -1;
+  }
+
+  void Test1s(Spy) { mCount += 2; }
+  void Test1ps(Spy*) { mCount += 2; }
+  void Test1rs(Spy&) { mCount += 2; }
+  void Test1rrs(Spy&&) { mCount += 2; }
+  void Test1ups(mozilla::UniquePtr<Spy>) { mCount += 2; }
+  void Test1rups(mozilla::UniquePtr<Spy>&) { mCount += 2; }
+  void Test1rrups(mozilla::UniquePtr<Spy>&&) { mCount += 2; }
+
+  // Possible parameter passing styles:
+  void TestByValue(Spy s)
+  {
+    if (gDebug) { printf("TestByValue(Spy[%d@%p])\n", s.mID, &s); }
+    mSpy = s;
+  };
+  void TestByConstLRef(const Spy& s)
+  {
+    if (gDebug) { printf("TestByConstLRef(Spy[%d@%p]&)\n", s.mID, &s); }
+    mSpy = s;
+  };
+  void TestByRRef(Spy&& s)
+  {
+    if (gDebug) { printf("TestByRRef(Spy[%d@%p]&&)\n", s.mID, &s); }
+    mSpy = mozilla::Move(s);
+  };
+  void TestByLRef(Spy& s)
+  {
+    if (gDebug) { printf("TestByLRef(Spy[%d@%p]&)\n", s.mID, &s); }
+    mSpy = s;
+    mSpyPtr = &s;
+  };
+  void TestByPointer(Spy* p)
+  {
+    if (p) {
+      if (gDebug) { printf("TestByPointer(&Spy[%d@%p])\n", p->mID, p); }
+      mSpy = *p;
+    } else {
+      if (gDebug) { printf("TestByPointer(nullptr)\n"); }
+    }
+    mSpyPtr = p;
+  };
+  void TestByPointerToConst(const Spy* p)
+  {
+    if (p) {
+      if (gDebug) { printf("TestByPointerToConst(&Spy[%d@%p])\n", p->mID, p); }
+      mSpy = *p;
+    } else {
+      if (gDebug) { printf("TestByPointerToConst(nullptr)\n"); }
+    }
+    mSpyPtr = p;
+  };
+};
+
+NS_IMPL_ISUPPORTS(ThreadUtilsObject, IThreadUtilsObject)
+
+} // namespace TestThreadUtils;
+
+TEST(ThreadUtils, main)
+{
+#ifndef XPCOM_GLUE_AVOID_NSPR
+  using namespace TestThreadUtils;
+
+  static_assert(!IsParameterStorageClass<int>::value,
+                "'int' should not be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreCopyPassByValue<int>>::value,
+                "StoreCopyPassByValue<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreCopyPassByConstLRef<int>>::value,
+                "StoreCopyPassByConstLRef<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreCopyPassByLRef<int>>::value,
+                "StoreCopyPassByLRef<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreCopyPassByRRef<int>>::value,
+                "StoreCopyPassByRRef<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreRefPassByLRef<int>>::value,
+                "StoreRefPassByLRef<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreConstRefPassByConstLRef<int>>::value,
+                "StoreConstRefPassByConstLRef<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StorensRefPtrPassByPtr<int>>::value,
+                "StorensRefPtrPassByPtr<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StorePtrPassByPtr<int>>::value,
+                "StorePtrPassByPtr<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreConstPtrPassByConstPtr<int>>::value,
+                "StoreConstPtrPassByConstPtr<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreCopyPassByConstPtr<int>>::value,
+                "StoreCopyPassByConstPtr<int> should be recognized as Storage Class");
+  static_assert(IsParameterStorageClass<StoreCopyPassByPtr<int>>::value,
+                "StoreCopyPassByPtr<int> should be recognized as Storage Class");
+
+  nsRefPtr<ThreadUtilsObject> rpt(new ThreadUtilsObject);
+  int count = 0;
+
+  // Test legacy functions.
+
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableMethod(rpt, &ThreadUtilsObject::Test0);
+  r->Run();
+  EXPECT_EQ(count += 1, rpt->mCount);
+
+  r = NS_NewRunnableMethodWithArg<int>(rpt, &ThreadUtilsObject::Test1i, 11);
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(11, rpt->mA0);
+
+  // Test variadic function with simple POD arguments.
+
+  r = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test0);
+  r->Run();
+  EXPECT_EQ(count += 1, rpt->mCount);
+
+  static_assert(
+      mozilla::IsSame< ::detail::ParameterStorage<int>::Type,
+                      StoreCopyPassByValue<int>>::value,
+      "ns::detail::ParameterStorage<int>::Type should be StoreCopyPassByValue<int>");
+  static_assert(
+      mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByValue<int>>::Type,
+                      StoreCopyPassByValue<int>>::value,
+      "ns::detail::ParameterStorage<StoreCopyPassByValue<int>>::Type should be StoreCopyPassByValue<int>");
+
+  r = NS_NewRunnableMethodWithArgs<int>(rpt, &ThreadUtilsObject::Test1i, 12);
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(12, rpt->mA0);
+
+  r = NS_NewRunnableMethodWithArgs<int, int>(
+      rpt, &ThreadUtilsObject::Test2i, 21, 22);
+  r->Run();
+  EXPECT_EQ(count += 3, rpt->mCount);
+  EXPECT_EQ(21, rpt->mA0);
+  EXPECT_EQ(22, rpt->mA1);
+
+  r = NS_NewRunnableMethodWithArgs<int, int, int>(
+      rpt, &ThreadUtilsObject::Test3i, 31, 32, 33);
+  r->Run();
+  EXPECT_EQ(count += 4, rpt->mCount);
+  EXPECT_EQ(31, rpt->mA0);
+  EXPECT_EQ(32, rpt->mA1);
+  EXPECT_EQ(33, rpt->mA2);
+
+  r = NS_NewRunnableMethodWithArgs<int, int, int, int>(
+      rpt, &ThreadUtilsObject::Test4i, 41, 42, 43, 44);
+  r->Run();
+  EXPECT_EQ(count += 5, rpt->mCount);
+  EXPECT_EQ(41, rpt->mA0);
+  EXPECT_EQ(42, rpt->mA1);
+  EXPECT_EQ(43, rpt->mA2);
+  EXPECT_EQ(44, rpt->mA3);
+
+  // More interesting types of arguments.
+
+  // Passing a short to make sure forwarding works with an inexact type match.
+  short int si = 11;
+  r = NS_NewRunnableMethodWithArgs<int>(rpt, &ThreadUtilsObject::Test1i, si);
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(si, rpt->mA0);
+
+  // Raw pointer, possible cv-qualified.
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type,
+                                StorePtrPassByPtr<int>>::value,
+                "ns::detail::ParameterStorage<int*>::Type should be StorePtrPassByPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const>::Type,
+                                StorePtrPassByPtr<int>>::value,
+                "ns::detail::ParameterStorage<int* const>::Type should be StorePtrPassByPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* volatile>::Type,
+                                StorePtrPassByPtr<int>>::value,
+                "ns::detail::ParameterStorage<int* volatile>::Type should be StorePtrPassByPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const volatile>::Type,
+                                StorePtrPassByPtr<int>>::value,
+                "ns::detail::ParameterStorage<int* const volatile>::Type should be StorePtrPassByPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::stored_type,
+                                int*>::value,
+                "ns::detail::ParameterStorage<int*>::Type::stored_type should be int*");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::passed_type,
+                                int*>::value,
+                "ns::detail::ParameterStorage<int*>::Type::passed_type should be int*");
+  {
+    int i = 12;
+    r = NS_NewRunnableMethodWithArgs<int*>(rpt, &ThreadUtilsObject::Test1pi, &i);
+    r->Run();
+    EXPECT_EQ(count += 2, rpt->mCount);
+    EXPECT_EQ(i, rpt->mA0);
+  }
+
+  // Raw pointer to const.
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type,
+                                StoreConstPtrPassByConstPtr<int>>::value,
+                "ns::detail::ParameterStorage<const int*>::Type should be StoreConstPtrPassByConstPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const>::Type,
+                                StoreConstPtrPassByConstPtr<int>>::value,
+                "ns::detail::ParameterStorage<const int* const>::Type should be StoreConstPtrPassByConstPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* volatile>::Type,
+                                StoreConstPtrPassByConstPtr<int>>::value,
+                "ns::detail::ParameterStorage<const int* volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const volatile>::Type,
+                                StoreConstPtrPassByConstPtr<int>>::value,
+                "ns::detail::ParameterStorage<const int* const volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::stored_type,
+                                const int*>::value,
+                "ns::detail::ParameterStorage<const int*>::Type::stored_type should be const int*");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::passed_type,
+                                const int*>::value,
+                "ns::detail::ParameterStorage<const int*>::Type::passed_type should be const int*");
+  {
+    int i = 1201;
+    r = NS_NewRunnableMethodWithArgs<const int*>(rpt, &ThreadUtilsObject::Test1pci, &i);
+    r->Run();
+    EXPECT_EQ(count += 2, rpt->mCount);
+    EXPECT_EQ(i, rpt->mA0);
+  }
+
+  // Raw pointer to copy.
+  static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::stored_type,
+                                int>::value,
+                "StoreCopyPassByPtr<int>::stored_type should be int");
+  static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::passed_type,
+                                int*>::value,
+                "StoreCopyPassByPtr<int>::passed_type should be int*");
+  {
+    int i = 1202;
+    r = NS_NewRunnableMethodWithArgs<StoreCopyPassByPtr<int>>(
+        rpt, &ThreadUtilsObject::Test1pi, i);
+    r->Run();
+    EXPECT_EQ(count += 2, rpt->mCount);
+    EXPECT_EQ(i, rpt->mA0);
+  }
+
+  // Raw pointer to const copy.
+  static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::stored_type,
+                                int>::value,
+                "StoreCopyPassByConstPtr<int>::stored_type should be int");
+  static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::passed_type,
+                                const int*>::value,
+                "StoreCopyPassByConstPtr<int>::passed_type should be const int*");
+  {
+    int i = 1203;
+    r = NS_NewRunnableMethodWithArgs<StoreCopyPassByConstPtr<int>>(
+        rpt, &ThreadUtilsObject::Test1pci, i);
+    r->Run();
+    EXPECT_EQ(count += 2, rpt->mCount);
+    EXPECT_EQ(i, rpt->mA0);
+  }
+
+  // nsRefPtr to pointer.
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StorensRefPtrPassByPtr<SpyWithISupports>>::Type,
+                                StorensRefPtrPassByPtr<SpyWithISupports>>::value,
+                "ns::detail::ParameterStorage<StorensRefPtrPassByPtr<SpyWithISupports>>::Type should be StorensRefPtrPassByPtr<SpyWithISupports>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<SpyWithISupports*>::Type,
+                                StorensRefPtrPassByPtr<SpyWithISupports>>::value,
+                "ns::detail::ParameterStorage<SpyWithISupports*>::Type should be StorensRefPtrPassByPtr<SpyWithISupports>");
+  static_assert(mozilla::IsSame<StorensRefPtrPassByPtr<SpyWithISupports>::stored_type,
+                                nsRefPtr<SpyWithISupports>>::value,
+                "StorensRefPtrPassByPtr<SpyWithISupports>::stored_type should be nsRefPtr<SpyWithISupports>");
+  static_assert(mozilla::IsSame<StorensRefPtrPassByPtr<SpyWithISupports>::passed_type,
+                                SpyWithISupports*>::value,
+                "StorensRefPtrPassByPtr<SpyWithISupports>::passed_type should be SpyWithISupports*");
+  // (more nsRefPtr tests below)
+
+  // Lvalue reference.
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type,
+                                StoreRefPassByLRef<int>>::value,
+                "ns::detail::ParameterStorage<int&>::Type should be StoreRefPassByLRef<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
+                                StoreRefPassByLRef<int>::stored_type>::value,
+                "ns::detail::ParameterStorage<int&>::Type::stored_type should be StoreRefPassByLRef<int>::stored_type");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
+                                int&>::value,
+                "ns::detail::ParameterStorage<int&>::Type::stored_type should be int&");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::passed_type,
+                                int&>::value,
+                "ns::detail::ParameterStorage<int&>::Type::passed_type should be int&");
+  {
+    int i = 13;
+    r = NS_NewRunnableMethodWithArgs<int&>(rpt, &ThreadUtilsObject::Test1ri, i);
+    r->Run();
+    EXPECT_EQ(count += 2, rpt->mCount);
+    EXPECT_EQ(i, rpt->mA0);
+  }
+
+  // Rvalue reference -- Actually storing a copy and then moving it.
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type,
+                                StoreCopyPassByRRef<int>>::value,
+                "ns::detail::ParameterStorage<int&&>::Type should be StoreCopyPassByRRef<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
+                                StoreCopyPassByRRef<int>::stored_type>::value,
+                "ns::detail::ParameterStorage<int&&>::Type::stored_type should be StoreCopyPassByRRef<int>::stored_type");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
+                                int>::value,
+                "ns::detail::ParameterStorage<int&&>::Type::stored_type should be int");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::passed_type,
+                                int&&>::value,
+                "ns::detail::ParameterStorage<int&&>::Type::passed_type should be int&&");
+  {
+    int i = 14;
+    r = NS_NewRunnableMethodWithArgs<int&&>(
+          rpt, &ThreadUtilsObject::Test1rri, mozilla::Move(i));
+  }
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(14, rpt->mA0);
+
+  // Null unique pointer, by semi-implicit store&move with "T&&" syntax.
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type,
+                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::value,
+                "ns::detail::ParameterStorage<UniquePtr<int>&&>::Type should be StoreCopyPassByRRef<UniquePtr<int>>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
+                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+                "ns::detail::ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
+                                mozilla::UniquePtr<int>>::value,
+                "ns::detail::ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be UniquePtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::passed_type,
+                                mozilla::UniquePtr<int>&&>::value,
+                "ns::detail::ParameterStorage<UniquePtr<int>&&>::Type::passed_type should be UniquePtr<int>&&");
+  {
+    mozilla::UniquePtr<int> upi;
+    r = NS_NewRunnableMethodWithArgs<mozilla::UniquePtr<int>&&>(
+          rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+  }
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(-1, rpt->mA0);
+  rpt->mA0 = 0;
+
+  // Null unique pointer, by explicit store&move with "StoreCopyPassByRRef<T>" syntax.
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+                "ns::detail::ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+                                StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+                "ns::detail::ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+                                mozilla::UniquePtr<int>>::value,
+                "ns::detail::ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be UniquePtr<int>");
+  static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::passed_type,
+                                mozilla::UniquePtr<int>&&>::value,
+                "ns::detail::ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::passed_type should be UniquePtr<int>&&");
+  {
+    mozilla::UniquePtr<int> upi;
+    r = NS_NewRunnableMethodWithArgs
+        <StoreCopyPassByRRef<mozilla::UniquePtr<int>>>(
+          rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+  }
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(-1, rpt->mA0);
+
+  // Unique pointer as xvalue.
+  {
+    mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
+    r = NS_NewRunnableMethodWithArgs<mozilla::UniquePtr<int>&&>(
+          rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+  }
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(1, rpt->mA0);
+
+  {
+    mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
+    r = NS_NewRunnableMethodWithArgs
+        <StoreCopyPassByRRef<mozilla::UniquePtr<int>>>
+        (rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+  }
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(1, rpt->mA0);
+
+  // Unique pointer as prvalue.
+  r = NS_NewRunnableMethodWithArgs<mozilla::UniquePtr<int>&&>(
+        rpt, &ThreadUtilsObject::Test1upi, mozilla::MakeUnique<int>(2));
+  r->Run();
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(2, rpt->mA0);
+
+  // Unique pointer as lvalue to lref.
+  {
+    mozilla::UniquePtr<int> upi;
+    r = NS_NewRunnableMethodWithArgs<mozilla::UniquePtr<int>&>(
+          rpt, &ThreadUtilsObject::Test1rupi, upi);
+    // Passed as lref, so Run() must be called while local upi is still alive!
+    r->Run();
+  }
+  EXPECT_EQ(count += 2, rpt->mCount);
+  EXPECT_EQ(-1, rpt->mA0);
+
+  // Verify copy/move assumptions.
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by value\n", __LINE__); }
+  { // Block around nsCOMPtr lifetime.
+    nsCOMPtr<nsIRunnable> r;
+    { // Block around Spy lifetime.
+      if (gDebug) { printf("%d - Spy s(10)\n", __LINE__); }
+      Spy s(10);
+      EXPECT_EQ(1, gConstructions);
+      EXPECT_EQ(1, gAlive);
+      if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<StoreCopyPassByValue<Spy>>(&TestByValue, s)\n", __LINE__); }
+      r = NS_NewRunnableMethodWithArgs<StoreCopyPassByValue<Spy>>(
+          rpt, &ThreadUtilsObject::TestByValue, s);
+      EXPECT_EQ(2, gAlive);
+      EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+      Spy::ClearActions();
+      if (gDebug) { printf("%d - End block with Spy s(10)\n", __LINE__); }
+    }
+    EXPECT_EQ(1, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+    EXPECT_EQ(10, rpt->mSpy.mID);
+    EXPECT_LE(1, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by value\n", __LINE__); }
+  {
+    if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<StoreCopyPassByValue<Spy>>(&TestByValue, Spy(11))\n", __LINE__); }
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethodWithArgs<StoreCopyPassByValue<Spy>>(
+        rpt, &ThreadUtilsObject::TestByValue, Spy(11));
+    EXPECT_EQ(1, gAlive);
+    EXPECT_EQ(1, gConstructions);
+    EXPECT_LE(1, gMoveConstructions);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+    EXPECT_EQ(11, rpt->mSpy.mID);
+    EXPECT_LE(1, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  { // Store copy from xvalue, pass by value.
+    nsCOMPtr<nsIRunnable> r;
+    {
+      Spy s(12);
+      EXPECT_EQ(1, gConstructions);
+      EXPECT_EQ(1, gAlive);
+      Spy::ClearActions();
+      r = NS_NewRunnableMethodWithArgs<StoreCopyPassByValue<Spy>>(
+          rpt, &ThreadUtilsObject::TestByValue, mozilla::Move(s));
+      EXPECT_LE(1, gMoveConstructions);
+      EXPECT_EQ(1, gAlive);
+      EXPECT_EQ(1, gZombies);
+      Spy::ClearActions();
+    }
+    EXPECT_EQ(1, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    EXPECT_EQ(0, gZombies);
+    Spy::ClearActions();
+    r->Run();
+    EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+    EXPECT_EQ(12, rpt->mSpy.mID);
+    EXPECT_LE(1, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+  }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+  // Won't test xvalues anymore, prvalues are enough to verify all rvalues.
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by const lvalue ref\n", __LINE__); }
+  { // Block around nsCOMPtr lifetime.
+    nsCOMPtr<nsIRunnable> r;
+    { // Block around Spy lifetime.
+      if (gDebug) { printf("%d - Spy s(20)\n", __LINE__); }
+      Spy s(20);
+      EXPECT_EQ(1, gConstructions);
+      EXPECT_EQ(1, gAlive);
+      if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, s)\n", __LINE__); }
+      r = NS_NewRunnableMethodWithArgs<StoreCopyPassByConstLRef<Spy>>(
+          rpt, &ThreadUtilsObject::TestByConstLRef, s);
+      EXPECT_EQ(2, gAlive);
+      EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+      Spy::ClearActions();
+      if (gDebug) { printf("%d - End block with Spy s(20)\n", __LINE__); }
+    }
+    EXPECT_EQ(1, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_EQ(0, gCopyConstructions); // No copies in call.
+    EXPECT_EQ(20, rpt->mSpy.mID);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by const lvalue ref\n", __LINE__); }
+  {
+    if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, Spy(21))\n", __LINE__); }
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethodWithArgs<StoreCopyPassByConstLRef<Spy>>(
+        rpt, &ThreadUtilsObject::TestByConstLRef, Spy(21));
+    EXPECT_EQ(1, gAlive);
+    EXPECT_EQ(1, gConstructions);
+    EXPECT_LE(1, gMoveConstructions);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_EQ(0, gCopyConstructions); // No copies in call.
+    EXPECT_EQ(21, rpt->mSpy.mID);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by rvalue ref\n", __LINE__); }
+  { // Block around nsCOMPtr lifetime.
+    nsCOMPtr<nsIRunnable> r;
+    { // Block around Spy lifetime.
+      if (gDebug) { printf("%d - Spy s(30)\n", __LINE__); }
+      Spy s(30);
+      EXPECT_EQ(1, gConstructions);
+      EXPECT_EQ(1, gAlive);
+      if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<StoreCopyPassByRRef<Spy>>(&TestByRRef, s)\n", __LINE__); }
+      r = NS_NewRunnableMethodWithArgs<StoreCopyPassByRRef<Spy>>(
+          rpt, &ThreadUtilsObject::TestByRRef, s);
+      EXPECT_EQ(2, gAlive);
+      EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+      Spy::ClearActions();
+      if (gDebug) { printf("%d - End block with Spy s(30)\n", __LINE__); }
+    }
+    EXPECT_EQ(1, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gMoves); // Move in call.
+    EXPECT_EQ(30, rpt->mSpy.mID);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
+    EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by rvalue ref\n", __LINE__); }
+  {
+    if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<StoreCopyPassByRRef<Spy>>(&TestByRRef, Spy(31))\n", __LINE__); }
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethodWithArgs<StoreCopyPassByRRef<Spy>>(
+        rpt, &ThreadUtilsObject::TestByRRef, Spy(31));
+    EXPECT_EQ(1, gAlive);
+    EXPECT_EQ(1, gConstructions);
+    EXPECT_LE(1, gMoveConstructions);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gMoves); // Move in call.
+    EXPECT_EQ(31, rpt->mSpy.mID);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
+    EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store lvalue ref, pass lvalue ref\n", __LINE__); }
+  {
+    if (gDebug) { printf("%d - Spy s(40)\n", __LINE__); }
+    Spy s(40);
+    EXPECT_EQ(1, gConstructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<Spy&>(&TestByLRef, s)\n", __LINE__); }
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethodWithArgs<Spy&>(
+        rpt, &ThreadUtilsObject::TestByLRef, s);
+    EXPECT_EQ(0, gAllConstructions);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gAssignments); // Assignment from reference in call.
+    EXPECT_EQ(40, rpt->mSpy.mID);
+    EXPECT_EQ(&s, rpt->mSpyPtr);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store nsRefPtr, pass by pointer\n", __LINE__); }
+  { // Block around nsCOMPtr lifetime.
+    nsCOMPtr<nsIRunnable> r;
+    SpyWithISupports* ptr = 0;
+    { // Block around nsRefPtr<Spy> lifetime.
+      if (gDebug) { printf("%d - nsRefPtr<SpyWithISupports> s(new SpyWithISupports(45))\n", __LINE__); }
+      nsRefPtr<SpyWithISupports> s(new SpyWithISupports(45));
+      ptr = s.get();
+      EXPECT_EQ(1, gConstructions);
+      EXPECT_EQ(1, gAlive);
+      if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<StorensRefPtrPassByPtr<Spy>>(&TestByRRef, s.get())\n", __LINE__); }
+      r = NS_NewRunnableMethodWithArgs<StorensRefPtrPassByPtr<SpyWithISupports>>(
+          rpt, &ThreadUtilsObject::TestByPointer, s.get());
+      EXPECT_LE(0, gAllConstructions);
+      EXPECT_EQ(1, gAlive);
+      Spy::ClearActions();
+      if (gDebug) { printf("%d - End block with nsRefPtr<Spy> s\n", __LINE__); }
+    }
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+    EXPECT_EQ(45, rpt->mSpy.mID);
+    EXPECT_EQ(ptr, rpt->mSpyPtr);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store pointer to lvalue, pass by pointer\n", __LINE__); }
+  {
+    if (gDebug) { printf("%d - Spy s(55)\n", __LINE__); }
+    Spy s(55);
+    EXPECT_EQ(1, gConstructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<Spy*>(&TestByPointer, s)\n", __LINE__); }
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethodWithArgs<Spy*>(
+        rpt, &ThreadUtilsObject::TestByPointer, &s);
+    EXPECT_EQ(0, gAllConstructions);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+    EXPECT_EQ(55, rpt->mSpy.mID);
+    EXPECT_EQ(&s, rpt->mSpyPtr);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+
+  Spy::ClearAll();
+  if (gDebug) { printf("%d - Test: Store pointer to const lvalue, pass by pointer\n", __LINE__); }
+  {
+    if (gDebug) { printf("%d - Spy s(60)\n", __LINE__); }
+    Spy s(60);
+    EXPECT_EQ(1, gConstructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs<Spy*>(&TestByPointer, s)\n", __LINE__); }
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethodWithArgs<const Spy*>(
+        rpt, &ThreadUtilsObject::TestByPointerToConst, &s);
+    EXPECT_EQ(0, gAllConstructions);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive);
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - Run()\n", __LINE__); }
+    r->Run();
+    EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+    EXPECT_EQ(60, rpt->mSpy.mID);
+    EXPECT_EQ(&s, rpt->mSpyPtr);
+    EXPECT_EQ(0, gDestructions);
+    EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+    Spy::ClearActions();
+    if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+  }
+  if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+  EXPECT_EQ(1, gDestructions);
+  EXPECT_EQ(0, gAlive);
+#endif // XPCOM_GLUE_AVOID_NSPR
+}
--- a/xpcom/glue/tests/gtest/moz.build
+++ b/xpcom/glue/tests/gtest/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'TestArray.cpp',
     'TestFileUtils.cpp',
     'TestGCPostBarriers.cpp',
+    'TestThreadUtils.cpp',
 ]
 
 LOCAL_INCLUDES = [
     '../..',
 ]
 
 FAIL_ON_WARNINGS = True