From 19883e7ee76dc4a3797b66405ff1782345682fb4 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Sun, 1 Mar 2015 18:12:00 +0100 Subject: [PATCH] Bug 1131445 - variadic NS_NewRunnableMethodWithArgs with storage&passing argument type decorators. r=waldo --- mfbt/TypeTraits.h | 36 + mfbt/tests/TestTypeTraits.cpp | 23 + xpcom/glue/nsThreadUtils.h | 474 ++++++++--- xpcom/glue/tests/gtest/TestThreadUtils.cpp | 880 +++++++++++++++++++++ xpcom/glue/tests/gtest/moz.build | 1 + 5 files changed, 1325 insertions(+), 89 deletions(-) create mode 100644 xpcom/glue/tests/gtest/TestThreadUtils.cpp diff --git a/mfbt/TypeTraits.h b/mfbt/TypeTraits.h index 779bc29cd54..d3e3182116e 100644 --- a/mfbt/TypeTraits.h +++ b/mfbt/TypeTraits.h @@ -958,6 +958,42 @@ struct RemoveExtent /* 20.9.7.5 Pointer modifications [meta.trans.ptr] */ +namespace detail { + +template +struct RemovePointerHelper +{ + typedef T Type; +}; + +template +struct RemovePointerHelper +{ + 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::Type is int; + * mozilla::RemovePointer::Type is int; + * mozilla::RemovePointer::Type is int; + * mozilla::RemovePointer::Type is int; + * mozilla::RemovePointer::Type is const long; + * mozilla::RemovePointer::Type is void; + * mozilla::RemovePointer::Type is void (S::*)(); + * mozilla::RemovePointer::Type is void(); + * mozilla::RemovePointer::Type is bool S::*. + */ +template +struct RemovePointer + : detail::RemovePointerHelper::Type> +{}; + /* 20.9.7.6 Other transformations [meta.trans.other] */ /** diff --git a/mfbt/tests/TestTypeTraits.cpp b/mfbt/tests/TestTypeTraits.cpp index 67460dbe5cf..3041dfe821a 100644 --- a/mfbt/tests/TestTypeTraits.cpp +++ b/mfbt/tests/TestTypeTraits.cpp @@ -23,6 +23,7 @@ using mozilla::IsUnsigned; using mozilla::MakeSigned; using mozilla::MakeUnsigned; using mozilla::RemoveExtent; +using mozilla::RemovePointer; static_assert(!IsArray::value, "bool not an array"); @@ -430,6 +431,28 @@ static_assert(IsSame::Type, volatile int>::value, static_assert(IsSame::Type, long[17]>::value, "removing extent from multidimensional array must return element type"); +struct TestRemovePointer { bool m; void f(); }; +static_assert(IsSame::Type, int>::value, + "removing pointer from int must return int"); +static_assert(IsSame::Type, int>::value, + "removing pointer from int* must return int"); +static_assert(IsSame::Type, int>::value, + "removing pointer from int* const must return int"); +static_assert(IsSame::Type, int>::value, + "removing pointer from int* volatile must return int"); +static_assert(IsSame::Type, const long>::value, + "removing pointer from const long* must return const long"); +static_assert(IsSame::Type, void>::value, + "removing pointer from void* const must return void"); +static_assert(IsSame::Type, + void (TestRemovePointer::*)()>::value, + "removing pointer from void (S::*)() must return void (S::*)()"); +static_assert(IsSame::Type, void()>::value, + "removing pointer from void (*)() must return void()"); +static_assert(IsSame::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) diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h index f0257157778..726293b18d5 100644 --- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -268,67 +268,41 @@ public: typedef typename ReturnTypeEnforcer::ReturnTypeIsSafe check; }; -template +template struct nsRunnableMethodReceiver { nsRefPtr mObj; - Arg mArg; - nsRunnableMethodReceiver(ClassType* aObj, Arg aArg) - : mObj(aObj) - , mArg(aArg) - { - } - ~nsRunnableMethodReceiver() { Revoke(); } - void Revoke() { mObj = nullptr; } -}; - -template -struct nsRunnableMethodReceiver -{ - nsRefPtr 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 -struct nsRunnableMethodReceiver +struct nsRunnableMethodReceiver { ClassType* MOZ_NON_OWNING_REF mObj; explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} + ClassType* Get() const { return mObj; } void Revoke() { mObj = nullptr; } }; template struct nsRunnableMethodTraits; -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef A arg_type; - typedef nsRunnableMethod base_type; -}; - -template -struct nsRunnableMethodTraits -{ - typedef C class_type; - typedef R return_type; - typedef void arg_type; typedef nsRunnableMethod base_type; }; #ifdef NS_HAVE_STDCALL -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef A arg_type; typedef nsRunnableMethod base_type; }; @@ -337,57 +311,361 @@ struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef void arg_type; typedef nsRunnableMethod base_type; }; #endif -template + +// IsParameterStorageClass::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 +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 +struct StoreCopyPassByValue +{ + typedef T stored_type; + typedef T passed_type; + stored_type m; + template + StoreCopyPassByValue(A&& a) : m(mozilla::Forward(a)) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreCopyPassByConstLRef +{ + typedef T stored_type; + typedef const T& passed_type; + stored_type m; + template + StoreCopyPassByConstLRef(A&& a) : m(mozilla::Forward(a)) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreCopyPassByLRef +{ + typedef T stored_type; + typedef T& passed_type; + stored_type m; + template + StoreCopyPassByLRef(A&& a) : m(mozilla::Forward(a)) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreCopyPassByRRef +{ + typedef T stored_type; + typedef T&& passed_type; + stored_type m; + template + StoreCopyPassByRRef(A&& a) : m(mozilla::Forward(a)) {} + passed_type PassAsParameter() { return mozilla::Move(m); } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreRefPassByLRef +{ + typedef T& stored_type; + typedef T& passed_type; + stored_type m; + template + StoreRefPassByLRef(A& a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreConstRefPassByConstLRef +{ + typedef const T& stored_type; + typedef const T& passed_type; + stored_type m; + template + StoreConstRefPassByConstLRef(const A& a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StorensRefPtrPassByPtr +{ + typedef nsRefPtr stored_type; + typedef T* passed_type; + stored_type m; + template + StorensRefPtrPassByPtr(A a) : m(a) {} + passed_type PassAsParameter() { return m.get(); } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StorePtrPassByPtr +{ + typedef T* stored_type; + typedef T* passed_type; + stored_type m; + template + StorePtrPassByPtr(A a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreConstPtrPassByConstPtr +{ + typedef const T* stored_type; + typedef const T* passed_type; + stored_type m; + template + StoreConstPtrPassByConstPtr(A a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreCopyPassByConstPtr +{ + typedef T stored_type; + typedef const T* passed_type; + stored_type m; + template + StoreCopyPassByConstPtr(A&& a) : m(mozilla::Forward(a)) {} + passed_type PassAsParameter() { return &m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +template +struct StoreCopyPassByPtr +{ + typedef T stored_type; + typedef T* passed_type; + stored_type m; + template + StoreCopyPassByPtr(A&& a) : m(mozilla::Forward(a)) {} + passed_type PassAsParameter() { return &m; } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + +namespace detail { + +template +struct NonnsISupportsPointerStorageClass + : mozilla::Conditional::value, + StoreConstPtrPassByConstPtr< + typename mozilla::RemoveConst::Type>, + StorePtrPassByPtr> +{}; + +template +struct PointerStorageClass + : mozilla::Conditional::value, + StorensRefPtrPassByPtr, + typename NonnsISupportsPointerStorageClass< + TWithoutPointer + >::Type> +{}; + +template +struct LValueReferenceStorageClass + : mozilla::Conditional::value, + StoreConstRefPassByConstLRef< + typename mozilla::RemoveConst::Type>, + StoreRefPassByLRef> +{}; + +template +struct NonLValueReferenceStorageClass + : mozilla::Conditional::value, + StoreCopyPassByRRef< + typename mozilla::RemoveReference::Type>, + StoreCopyPassByValue> +{}; + +template +struct NonPointerStorageClass + : mozilla::Conditional::value, + typename LValueReferenceStorageClass< + typename mozilla::RemoveReference::Type + >::Type, + typename NonLValueReferenceStorageClass::Type> +{}; + +template +struct NonParameterStorageClass + : mozilla::Conditional::value, + typename PointerStorageClass< + typename mozilla::RemovePointer::Type + >::Type, + typename NonPointerStorageClass::Type> +{}; + +// Choose storage&passing strategy based on preferred storage type: +// - If IsParameterStorageClass::value is true, use as-is. +// - nsISupports* -> StorensRefPtrPassByPtr : Store nsRefPtr, pass T* +// - const T* -> StoreConstPtrPassByConstPtr : Store const T*, pass const T* +// - T* -> StorePtrPassByPtr : Store T*, pass T*. +// - const T& -> StoreConstRefPassByConstLRef: Store const T&, pass const T&. +// - T& -> StoreRefPassByLRef : Store T&, pass T&. +// - T&& -> StoreCopyPassByRRef : Store T, pass Move(T). +// - Other T -> StoreCopyPassByValue : Store T, pass T. +// Other available explicit options: +// - StoreCopyPassByConstLRef : Store T, pass const T&. +// - StoreCopyPassByLRef : Store T, pass T& (of copy!) +// - StoreCopyPassByConstPtr : Store T, pass const T* +// - StoreCopyPassByPtr : Store T, pass T* (of copy!) +// Or create your own class with PassAsParameter() method, optional +// clean-up in destructor, and with associated IsParameterStorageClass<>. +template +struct ParameterStorage + : mozilla::Conditional::value, + T, + typename NonParameterStorageClass::Type> +{}; + +} /* namespace detail */ + +// struct used to store arguments and later apply them to a method. +template struct nsRunnableMethodArguments; + +// Specializations for 0-4 arguments, add more as required. +// TODO Use tuple instead; And/or use lambdas. +template <> +struct nsRunnableMethodArguments<> +{ + template void apply(C* o, M m) + { + ((*o).*m)(); + } +}; +template +struct nsRunnableMethodArguments +{ + typename ::detail::ParameterStorage::Type m0; + template + nsRunnableMethodArguments(A0&& a0) + : m0(mozilla::Forward(a0)) + {} + template void apply(C* o, M m) + { + ((*o).*m)(m0.PassAsParameter()); + } +}; +template +struct nsRunnableMethodArguments +{ + typename ::detail::ParameterStorage::Type m0; + typename ::detail::ParameterStorage::Type m1; + template + nsRunnableMethodArguments(A0&& a0, A1&& a1) + : m0(mozilla::Forward(a0)) + , m1(mozilla::Forward(a1)) + {} + template void apply(C* o, M m) + { + ((*o).*m)(m0.PassAsParameter(), m1.PassAsParameter()); + } +}; +template +struct nsRunnableMethodArguments +{ + typename ::detail::ParameterStorage::Type m0; + typename ::detail::ParameterStorage::Type m1; + typename ::detail::ParameterStorage::Type m2; + template + nsRunnableMethodArguments(A0&& a0, A1&& a1, A2&& a2) + : m0(mozilla::Forward(a0)) + , m1(mozilla::Forward(a1)) + , m2(mozilla::Forward(a2)) + {} + template void apply(C* o, M m) + { + ((*o).*m)(m0.PassAsParameter(), m1.PassAsParameter(), m2.PassAsParameter()); + } +}; +template +struct nsRunnableMethodArguments +{ + typename ::detail::ParameterStorage::Type m0; + typename ::detail::ParameterStorage::Type m1; + typename ::detail::ParameterStorage::Type m2; + typename ::detail::ParameterStorage::Type m3; + template + nsRunnableMethodArguments(A0&& a0, A1&& a1, A2&& a2, A3&& a3) + : m0(mozilla::Forward(a0)) + , m1(mozilla::Forward(a1)) + , m2(mozilla::Forward(a2)) + , m3(mozilla::Forward(a3)) + {} + template void apply(C* o, M m) + { + ((*o).*m)(m0.PassAsParameter(), m1.PassAsParameter(), + m2.PassAsParameter(), m3.PassAsParameter()); + } +}; + +template class nsRunnableMethodImpl : public nsRunnableMethodTraits::base_type { - typedef typename nsRunnableMethodTraits::class_type ClassType; - nsRunnableMethodReceiver mReceiver; + typedef typename nsRunnableMethodTraits::class_type + ClassType; + nsRunnableMethodReceiver mReceiver; Method mMethod; + nsRunnableMethodArguments 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 -class nsRunnableMethodImpl - : public nsRunnableMethodTraits::base_type -{ - typedef typename nsRunnableMethodTraits::class_type ClassType; - nsRunnableMethodReceiver mReceiver; - Method mMethod; - -public: - nsRunnableMethodImpl(ClassType* aObj, Method aMethod) + virtual ~nsRunnableMethodImpl() { Revoke(); }; + template + explicit nsRunnableMethodImpl(ClassType* aObj, Method aMethod, + Args&&... aArgs) : mReceiver(aObj) , mMethod(aMethod) + , mArgs(mozilla::Forward(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 nsRunnableMethodTraits::base_type* NS_NewRunnableMethod(PtrType aPtr, Method aMethod) { - return new nsRunnableMethodImpl(aPtr, aMethod); -} - -template -struct dependent_type -{ - typedef T type; -}; - - -// Similar to NS_NewRunnableMethod. Call like so: -// Type myArg; -// nsCOMPtr event = -// NS_NewRunnableMethodWithArg(myObject, &MyClass::HandleEvent, myArg); -template -typename nsRunnableMethodTraits::base_type* -NS_NewRunnableMethodWithArg(PtrType&& aPtr, Method aMethod, - typename dependent_type::type aArg) -{ - return new nsRunnableMethodImpl(aPtr, aMethod, aArg); + return new nsRunnableMethodImpl(aPtr, aMethod); } template typename nsRunnableMethodTraits::base_type* NS_NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod) { - return new nsRunnableMethodImpl(aPtr, aMethod); + return new nsRunnableMethodImpl(aPtr, aMethod); +} + +// Similar to NS_NewRunnableMethod. Call like so: +// nsCOMPtr event = +// NS_NewRunnableMethodWithArg(myObject, &MyClass::HandleEvent, myArg); +// 'Type' is the stored type for the argument, see ParameterStorage for details. +template +typename nsRunnableMethodTraits::base_type* +NS_NewRunnableMethodWithArg(PtrType&& aPtr, Method aMethod, Arg&& aArg) +{ + return new nsRunnableMethodImpl( + aPtr, aMethod, mozilla::Forward(aArg)); +} + +// Similar to NS_NewRunnableMethod. Call like so: +// nsCOMPtr event = +// NS_NewRunnableMethodWithArg(myObject, &MyClass::HandleEvent, myArg1,...); +// 'Types' are the stored type for each argument, see ParameterStorage for details. +template +typename nsRunnableMethodTraits::base_type* +NS_NewRunnableMethodWithArgs(PtrType&& aPtr, Method aMethod, Args&&... aArgs) +{ + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return new nsRunnableMethodImpl( + aPtr, aMethod, mozilla::Forward(aArgs)...); +} + +template +typename nsRunnableMethodTraits::base_type* +NS_NewNonOwningRunnableMethodWithArgs(PtrType&& aPtr, Method aMethod, + Args&&... aArgs) +{ + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return new nsRunnableMethodImpl( + aPtr, aMethod, mozilla::Forward(aArgs)...); } #endif // XPCOM_GLUE_AVOID_NSPR diff --git a/xpcom/glue/tests/gtest/TestThreadUtils.cpp b/xpcom/glue/tests/gtest/TestThreadUtils.cpp new file mode 100644 index 00000000000..7190e621554 --- /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 +#include +#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 aup) + { + mCount += 2; mA0 = aup ? *aup : -1; + } + void Test1rupi(mozilla::UniquePtr& aup) + { + mCount += 2; mA0 = aup ? *aup : -1; + } + void Test1rrupi(mozilla::UniquePtr&& 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) { mCount += 2; } + void Test1rups(mozilla::UniquePtr&) { mCount += 2; } + void Test1rrups(mozilla::UniquePtr&&) { 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::value, + "'int' should not be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreCopyPassByValue should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreCopyPassByConstLRef should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreCopyPassByLRef should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreCopyPassByRRef should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreRefPassByLRef should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreConstRefPassByConstLRef should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StorensRefPtrPassByPtr should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StorePtrPassByPtr should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreConstPtrPassByConstPtr should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreCopyPassByConstPtr should be recognized as Storage Class"); + static_assert(IsParameterStorageClass>::value, + "StoreCopyPassByPtr should be recognized as Storage Class"); + + nsRefPtr rpt(new ThreadUtilsObject); + int count = 0; + + // Test legacy functions. + + nsCOMPtr r = + NS_NewRunnableMethod(rpt, &ThreadUtilsObject::Test0); + r->Run(); + EXPECT_EQ(count += 1, rpt->mCount); + + r = NS_NewRunnableMethodWithArg(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::Type, + StoreCopyPassByValue>::value, + "ns::detail::ParameterStorage::Type should be StoreCopyPassByValue"); + static_assert( + mozilla::IsSame< ::detail::ParameterStorage>::Type, + StoreCopyPassByValue>::value, + "ns::detail::ParameterStorage>::Type should be StoreCopyPassByValue"); + + r = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test1i, 12); + r->Run(); + EXPECT_EQ(count += 2, rpt->mCount); + EXPECT_EQ(12, rpt->mA0); + + r = NS_NewRunnableMethodWithArgs( + 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( + 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( + 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(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::Type, + StorePtrPassByPtr>::value, + "ns::detail::ParameterStorage::Type should be StorePtrPassByPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StorePtrPassByPtr>::value, + "ns::detail::ParameterStorage::Type should be StorePtrPassByPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StorePtrPassByPtr>::value, + "ns::detail::ParameterStorage::Type should be StorePtrPassByPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StorePtrPassByPtr>::value, + "ns::detail::ParameterStorage::Type should be StorePtrPassByPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, + int*>::value, + "ns::detail::ParameterStorage::Type::stored_type should be int*"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, + int*>::value, + "ns::detail::ParameterStorage::Type::passed_type should be int*"); + { + int i = 12; + r = NS_NewRunnableMethodWithArgs(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::Type, + StoreConstPtrPassByConstPtr>::value, + "ns::detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StoreConstPtrPassByConstPtr>::value, + "ns::detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StoreConstPtrPassByConstPtr>::value, + "ns::detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StoreConstPtrPassByConstPtr>::value, + "ns::detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, + const int*>::value, + "ns::detail::ParameterStorage::Type::stored_type should be const int*"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, + const int*>::value, + "ns::detail::ParameterStorage::Type::passed_type should be const int*"); + { + int i = 1201; + r = NS_NewRunnableMethodWithArgs(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::stored_type, + int>::value, + "StoreCopyPassByPtr::stored_type should be int"); + static_assert(mozilla::IsSame::passed_type, + int*>::value, + "StoreCopyPassByPtr::passed_type should be int*"); + { + int i = 1202; + r = NS_NewRunnableMethodWithArgs>( + 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::stored_type, + int>::value, + "StoreCopyPassByConstPtr::stored_type should be int"); + static_assert(mozilla::IsSame::passed_type, + const int*>::value, + "StoreCopyPassByConstPtr::passed_type should be const int*"); + { + int i = 1203; + r = NS_NewRunnableMethodWithArgs>( + 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>::Type, + StorensRefPtrPassByPtr>::value, + "ns::detail::ParameterStorage>::Type should be StorensRefPtrPassByPtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StorensRefPtrPassByPtr>::value, + "ns::detail::ParameterStorage::Type should be StorensRefPtrPassByPtr"); + static_assert(mozilla::IsSame::stored_type, + nsRefPtr>::value, + "StorensRefPtrPassByPtr::stored_type should be nsRefPtr"); + static_assert(mozilla::IsSame::passed_type, + SpyWithISupports*>::value, + "StorensRefPtrPassByPtr::passed_type should be SpyWithISupports*"); + // (more nsRefPtr tests below) + + // Lvalue reference. + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, + StoreRefPassByLRef>::value, + "ns::detail::ParameterStorage::Type should be StoreRefPassByLRef"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, + StoreRefPassByLRef::stored_type>::value, + "ns::detail::ParameterStorage::Type::stored_type should be StoreRefPassByLRef::stored_type"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, + int&>::value, + "ns::detail::ParameterStorage::Type::stored_type should be int&"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, + int&>::value, + "ns::detail::ParameterStorage::Type::passed_type should be int&"); + { + int i = 13; + r = NS_NewRunnableMethodWithArgs(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::Type, + StoreCopyPassByRRef>::value, + "ns::detail::ParameterStorage::Type should be StoreCopyPassByRRef"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, + StoreCopyPassByRRef::stored_type>::value, + "ns::detail::ParameterStorage::Type::stored_type should be StoreCopyPassByRRef::stored_type"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, + int>::value, + "ns::detail::ParameterStorage::Type::stored_type should be int"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, + int&&>::value, + "ns::detail::ParameterStorage::Type::passed_type should be int&&"); + { + int i = 14; + r = NS_NewRunnableMethodWithArgs( + 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&&>::Type, + StoreCopyPassByRRef>>::value, + "ns::detail::ParameterStorage&&>::Type should be StoreCopyPassByRRef>"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage&&>::Type::stored_type, + StoreCopyPassByRRef>::stored_type>::value, + "ns::detail::ParameterStorage&&>::Type::stored_type should be StoreCopyPassByRRef>::stored_type"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage&&>::Type::stored_type, + mozilla::UniquePtr>::value, + "ns::detail::ParameterStorage&&>::Type::stored_type should be UniquePtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage&&>::Type::passed_type, + mozilla::UniquePtr&&>::value, + "ns::detail::ParameterStorage&&>::Type::passed_type should be UniquePtr&&"); + { + mozilla::UniquePtr upi; + r = NS_NewRunnableMethodWithArgs&&>( + 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" syntax. + static_assert(mozilla::IsSame< ::detail::ParameterStorage>>::Type::stored_type, + StoreCopyPassByRRef>::stored_type>::value, + "ns::detail::ParameterStorage>>::Type::stored_type should be StoreCopyPassByRRef>::stored_type"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage>>::Type::stored_type, + StoreCopyPassByRRef>::stored_type>::value, + "ns::detail::ParameterStorage>>::Type::stored_type should be StoreCopyPassByRRef>::stored_type"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage>>::Type::stored_type, + mozilla::UniquePtr>::value, + "ns::detail::ParameterStorage>>::Type::stored_type should be UniquePtr"); + static_assert(mozilla::IsSame< ::detail::ParameterStorage>>::Type::passed_type, + mozilla::UniquePtr&&>::value, + "ns::detail::ParameterStorage>>::Type::passed_type should be UniquePtr&&"); + { + mozilla::UniquePtr upi; + r = NS_NewRunnableMethodWithArgs + >>( + 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 upi = mozilla::MakeUnique(1); + r = NS_NewRunnableMethodWithArgs&&>( + rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); + } + r->Run(); + EXPECT_EQ(count += 2, rpt->mCount); + EXPECT_EQ(1, rpt->mA0); + + { + mozilla::UniquePtr upi = mozilla::MakeUnique(1); + r = NS_NewRunnableMethodWithArgs + >> + (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&&>( + rpt, &ThreadUtilsObject::Test1upi, mozilla::MakeUnique(2)); + r->Run(); + EXPECT_EQ(count += 2, rpt->mCount); + EXPECT_EQ(2, rpt->mA0); + + // Unique pointer as lvalue to lref. + { + mozilla::UniquePtr upi; + r = NS_NewRunnableMethodWithArgs&>( + 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 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>(&TestByValue, s)\n", __LINE__); } + r = NS_NewRunnableMethodWithArgs>( + 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>(&TestByValue, Spy(11))\n", __LINE__); } + nsCOMPtr r = + NS_NewRunnableMethodWithArgs>( + 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 r; + { + Spy s(12); + EXPECT_EQ(1, gConstructions); + EXPECT_EQ(1, gAlive); + Spy::ClearActions(); + r = NS_NewRunnableMethodWithArgs>( + 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 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>(&TestByConstLRef, s)\n", __LINE__); } + r = NS_NewRunnableMethodWithArgs>( + 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>(&TestByConstLRef, Spy(21))\n", __LINE__); } + nsCOMPtr r = + NS_NewRunnableMethodWithArgs>( + 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 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>(&TestByRRef, s)\n", __LINE__); } + r = NS_NewRunnableMethodWithArgs>( + 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>(&TestByRRef, Spy(31))\n", __LINE__); } + nsCOMPtr r = + NS_NewRunnableMethodWithArgs>( + 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(&TestByLRef, s)\n", __LINE__); } + nsCOMPtr r = + NS_NewRunnableMethodWithArgs( + 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 r; + SpyWithISupports* ptr = 0; + { // Block around nsRefPtr lifetime. + if (gDebug) { printf("%d - nsRefPtr s(new SpyWithISupports(45))\n", __LINE__); } + nsRefPtr s(new SpyWithISupports(45)); + ptr = s.get(); + EXPECT_EQ(1, gConstructions); + EXPECT_EQ(1, gAlive); + if (gDebug) { printf("%d - r = NS_NewRunnableMethodWithArgs>(&TestByRRef, s.get())\n", __LINE__); } + r = NS_NewRunnableMethodWithArgs>( + rpt, &ThreadUtilsObject::TestByPointer, s.get()); + EXPECT_LE(0, gAllConstructions); + EXPECT_EQ(1, gAlive); + Spy::ClearActions(); + if (gDebug) { printf("%d - End block with nsRefPtr 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(&TestByPointer, s)\n", __LINE__); } + nsCOMPtr r = + NS_NewRunnableMethodWithArgs( + 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(&TestByPointer, s)\n", __LINE__); } + nsCOMPtr r = + NS_NewRunnableMethodWithArgs( + 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 +} diff --git a/xpcom/glue/tests/gtest/moz.build b/xpcom/glue/tests/gtest/moz.build index d97889ef2e5..d2f5f51af03 100644 --- a/xpcom/glue/tests/gtest/moz.build +++ b/xpcom/glue/tests/gtest/moz.build @@ -8,6 +8,7 @@ UNIFIED_SOURCES += [ 'TestArray.cpp', 'TestFileUtils.cpp', 'TestGCPostBarriers.cpp', + 'TestThreadUtils.cpp', ] LOCAL_INCLUDES = [