Bug 1131445 - variadic NS_NewRunnableMethodWithArgs with storage&passing argument type decorators. r=waldo

This commit is contained in:
Gerald Squelart 2015-03-01 18:12:00 +01:00
parent a9c43bbea9
commit 19883e7ee7
5 changed files with 1325 additions and 89 deletions

View File

@ -958,6 +958,42 @@ struct RemoveExtent<T[N]>
/* 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] */
/**

View File

@ -23,6 +23,7 @@ using mozilla::IsUnsigned;
using mozilla::MakeSigned;
using mozilla::MakeUnsigned;
using mozilla::RemoveExtent;
using mozilla::RemovePointer;
static_assert(!IsArray<bool>::value,
"bool not an array");
@ -430,6 +431,28 @@ static_assert(IsSame<RemoveExtent<volatile int[5]>::Type, volatile int>::value,
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)

View File

@ -268,67 +268,41 @@ public:
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)
{
}
~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)
{
}
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;
};
@ -337,57 +311,361 @@ 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)
, mMethod(aMethod)
{
}
NS_IMETHOD Run()
{
if (MOZ_LIKELY(mReceiver.mObj)) {
((*mReceiver.mObj).*mMethod)(mReceiver.mArg);
}
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)
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)();
if (MOZ_LIKELY(mReceiver.Get())) {
mArgs.apply(mReceiver.Get(), mMethod);
}
return NS_OK;
}
void Revoke() { mReceiver.Revoke(); }
};
@ -405,33 +683,51 @@ 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

View File

@ -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
}

View File

@ -8,6 +8,7 @@ UNIFIED_SOURCES += [
'TestArray.cpp',
'TestFileUtils.cpp',
'TestGCPostBarriers.cpp',
'TestThreadUtils.cpp',
]
LOCAL_INCLUDES = [