/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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() override { return mRefCnt; } NS_IMETHOD_(int32_t) ID() 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() override { return mRefCnt; } NS_IMETHOD_(int32_t) ID() 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) class ThreadUtilsRefCountedFinal final { public: ThreadUtilsRefCountedFinal() : m_refCount(0) {} ~ThreadUtilsRefCountedFinal() {} // 'AddRef' and 'Release' methods with different return types, to verify // that the return type doesn't influence storage selection. long AddRef(void) { return ++m_refCount; } void Release(void) { --m_refCount; } private: long m_refCount; }; class ThreadUtilsRefCountedBase { public: ThreadUtilsRefCountedBase() : m_refCount(0) {} virtual ~ThreadUtilsRefCountedBase() {} // 'AddRef' and 'Release' methods with different return types, to verify // that the return type doesn't influence storage selection. virtual void AddRef(void) { ++m_refCount; } virtual MozExternalRefCountType Release(void) { return --m_refCount; } private: MozExternalRefCountType m_refCount; }; class ThreadUtilsRefCountedDerived : public ThreadUtilsRefCountedBase {}; class ThreadUtilsNonRefCounted {}; } // 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"); RefPtr rpt(new ThreadUtilsObject); int count = 0; // Test legacy functions. nsCOMPtr r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test0); r1->Run(); EXPECT_EQ(count += 1, rpt->mCount); r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1i, 11); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(11, rpt->mA0); // Test variadic function with simple POD arguments. r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test0); r1->Run(); EXPECT_EQ(count += 1, rpt->mCount); static_assert( mozilla::IsSame< ::detail::ParameterStorage::Type, StoreCopyPassByValue>::value, "detail::ParameterStorage::Type should be StoreCopyPassByValue"); static_assert( mozilla::IsSame< ::detail::ParameterStorage>::Type, StoreCopyPassByValue>::value, "detail::ParameterStorage>::Type should be StoreCopyPassByValue"); r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1i, 12); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(12, rpt->mA0); r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test2i, 21, 22); r1->Run(); EXPECT_EQ(count += 3, rpt->mCount); EXPECT_EQ(21, rpt->mA0); EXPECT_EQ(22, rpt->mA1); r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test3i, 31, 32, 33); r1->Run(); EXPECT_EQ(count += 4, rpt->mCount); EXPECT_EQ(31, rpt->mA0); EXPECT_EQ(32, rpt->mA1); EXPECT_EQ(33, rpt->mA2); r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test4i, 41, 42, 43, 44); r1->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; r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1i, si); r1->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, "detail::ParameterStorage::Type should be StorePtrPassByPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StorePtrPassByPtr>::value, "detail::ParameterStorage::Type should be StorePtrPassByPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StorePtrPassByPtr>::value, "detail::ParameterStorage::Type should be StorePtrPassByPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StorePtrPassByPtr>::value, "detail::ParameterStorage::Type should be StorePtrPassByPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, int*>::value, "detail::ParameterStorage::Type::stored_type should be int*"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, int*>::value, "detail::ParameterStorage::Type::passed_type should be int*"); { int i = 12; r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1pi, &i); r1->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, "detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StoreConstPtrPassByConstPtr>::value, "detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StoreConstPtrPassByConstPtr>::value, "detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StoreConstPtrPassByConstPtr>::value, "detail::ParameterStorage::Type should be StoreConstPtrPassByConstPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, const int*>::value, "detail::ParameterStorage::Type::stored_type should be const int*"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, const int*>::value, "detail::ParameterStorage::Type::passed_type should be const int*"); { int i = 1201; r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1pci, &i); r1->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; r1 = NewRunnableMethod>( rpt, &ThreadUtilsObject::Test1pi, i); r1->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; r1 = NewRunnableMethod>( rpt, &ThreadUtilsObject::Test1pci, i); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(i, rpt->mA0); } // nsRefPtr to pointer. static_assert(mozilla::IsSame< ::detail::ParameterStorage>::Type, StorensRefPtrPassByPtr>::value, "ParameterStorage>::Type should be StorensRefPtrPassByPtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StorensRefPtrPassByPtr>::value, "ParameterStorage::Type should be StorensRefPtrPassByPtr"); static_assert(mozilla::IsSame::stored_type, RefPtr>::value, "StorensRefPtrPassByPtr::stored_type should be RefPtr"); static_assert(mozilla::IsSame::passed_type, SpyWithISupports*>::value, "StorensRefPtrPassByPtr::passed_type should be SpyWithISupports*"); // (more nsRefPtr tests below) // nsRefPtr for ref-countable classes that do not derive from ISupports. static_assert(::detail::HasRefCountMethods::value, "ThreadUtilsRefCountedFinal has AddRef() and Release()"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StorensRefPtrPassByPtr>::value, "ParameterStorage::Type should be StorensRefPtrPassByPtr"); static_assert(::detail::HasRefCountMethods::value, "ThreadUtilsRefCountedBase has AddRef() and Release()"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StorensRefPtrPassByPtr>::value, "ParameterStorage::Type should be StorensRefPtrPassByPtr"); static_assert(::detail::HasRefCountMethods::value, "ThreadUtilsRefCountedDerived has AddRef() and Release()"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StorensRefPtrPassByPtr>::value, "ParameterStorage::Type should be StorensRefPtrPassByPtr"); static_assert(!::detail::HasRefCountMethods::value, "ThreadUtilsNonRefCounted doesn't have AddRef() and Release()"); static_assert(!mozilla::IsSame< ::detail::ParameterStorage::Type, StorensRefPtrPassByPtr>::value, "ParameterStorage::Type should NOT be StorensRefPtrPassByPtr"); // Lvalue reference. static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type, StoreRefPassByLRef>::value, "ParameterStorage::Type should be StoreRefPassByLRef"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, StoreRefPassByLRef::stored_type>::value, "ParameterStorage::Type::stored_type should be StoreRefPassByLRef::stored_type"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, int&>::value, "ParameterStorage::Type::stored_type should be int&"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, int&>::value, "ParameterStorage::Type::passed_type should be int&"); { int i = 13; r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1ri, i); r1->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, "ParameterStorage::Type should be StoreCopyPassByRRef"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, StoreCopyPassByRRef::stored_type>::value, "ParameterStorage::Type::stored_type should be StoreCopyPassByRRef::stored_type"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::stored_type, int>::value, "ParameterStorage::Type::stored_type should be int"); static_assert(mozilla::IsSame< ::detail::ParameterStorage::Type::passed_type, int&&>::value, "ParameterStorage::Type::passed_type should be int&&"); { int i = 14; r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test1rri, mozilla::Move(i)); } r1->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, "ParameterStorage&&>::Type should be StoreCopyPassByRRef>"); static_assert(mozilla::IsSame< ::detail::ParameterStorage&&>::Type::stored_type, StoreCopyPassByRRef>::stored_type>::value, "ParameterStorage&&>::Type::stored_type should be StoreCopyPassByRRef>::stored_type"); static_assert(mozilla::IsSame< ::detail::ParameterStorage&&>::Type::stored_type, mozilla::UniquePtr>::value, "ParameterStorage&&>::Type::stored_type should be UniquePtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage&&>::Type::passed_type, mozilla::UniquePtr&&>::value, "ParameterStorage&&>::Type::passed_type should be UniquePtr&&"); { mozilla::UniquePtr upi; r1 = NewRunnableMethod&&>( rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } r1->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, "ParameterStorage>>::Type::stored_type should be StoreCopyPassByRRef>::stored_type"); static_assert(mozilla::IsSame< ::detail::ParameterStorage>>::Type::stored_type, StoreCopyPassByRRef>::stored_type>::value, "ParameterStorage>>::Type::stored_type should be StoreCopyPassByRRef>::stored_type"); static_assert(mozilla::IsSame< ::detail::ParameterStorage>>::Type::stored_type, mozilla::UniquePtr>::value, "ParameterStorage>>::Type::stored_type should be UniquePtr"); static_assert(mozilla::IsSame< ::detail::ParameterStorage>>::Type::passed_type, mozilla::UniquePtr&&>::value, "ParameterStorage>>::Type::passed_type should be UniquePtr&&"); { mozilla::UniquePtr upi; r1 = NewRunnableMethod >>( rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(-1, rpt->mA0); // Unique pointer as xvalue. { mozilla::UniquePtr upi = mozilla::MakeUnique(1); r1 = NewRunnableMethod&&>( rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(1, rpt->mA0); { mozilla::UniquePtr upi = mozilla::MakeUnique(1); r1 = NewRunnableMethod >> (rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(1, rpt->mA0); // Unique pointer as prvalue. r1 = NewRunnableMethod&&>( rpt, &ThreadUtilsObject::Test1upi, mozilla::MakeUnique(2)); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(2, rpt->mA0); // Unique pointer as lvalue to lref. { mozilla::UniquePtr upi; r1 = NewRunnableMethod&>( rpt, &ThreadUtilsObject::Test1rupi, upi); // Passed as lref, so Run() must be called while local upi is still alive! r1->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 r2; { // 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 - r2 = NewRunnableMethod>(&TestByValue, s)\n", __LINE__); } r2 = NewRunnableMethod>( 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__); } r2->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 - r3 = NewRunnableMethod>(&TestByValue, Spy(11))\n", __LINE__); } nsCOMPtr r3 = NewRunnableMethod>( 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__); } r3->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 r4; { Spy s(12); EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); Spy::ClearActions(); r4 = NewRunnableMethod>( 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(); r4->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 r5; { // 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 - r5 = NewRunnableMethod>(&TestByConstLRef, s)\n", __LINE__); } r5 = NewRunnableMethod>( 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__); } r5->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 - r6 = NewRunnableMethod>(&TestByConstLRef, Spy(21))\n", __LINE__); } nsCOMPtr r6 = NewRunnableMethod>( 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__); } r6->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 r7; { // 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 - r7 = NewRunnableMethod>(&TestByRRef, s)\n", __LINE__); } r7 = NewRunnableMethod>( 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__); } r7->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 - r8 = NewRunnableMethod>(&TestByRRef, Spy(31))\n", __LINE__); } nsCOMPtr r8 = NewRunnableMethod>( 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__); } r8->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 - r9 = NewRunnableMethod(&TestByLRef, s)\n", __LINE__); } nsCOMPtr r9 = NewRunnableMethod( 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__); } r9->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 r10; SpyWithISupports* ptr = 0; { // Block around RefPtr lifetime. if (gDebug) { printf("%d - RefPtr s(new SpyWithISupports(45))\n", __LINE__); } RefPtr s(new SpyWithISupports(45)); ptr = s.get(); EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); if (gDebug) { printf("%d - r10 = NewRunnableMethod>(&TestByRRef, s.get())\n", __LINE__); } r10 = NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByPointer, s.get()); EXPECT_LE(0, gAllConstructions); EXPECT_EQ(1, gAlive); Spy::ClearActions(); if (gDebug) { printf("%d - End block with RefPtr s\n", __LINE__); } } EXPECT_EQ(0, gDestructions); EXPECT_EQ(1, gAlive); Spy::ClearActions(); if (gDebug) { printf("%d - Run()\n", __LINE__); } r10->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 - r11 = NewRunnableMethod(&TestByPointer, s)\n", __LINE__); } nsCOMPtr r11 = NewRunnableMethod( 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__); } r11->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 - r12 = NewRunnableMethod(&TestByPointer, s)\n", __LINE__); } nsCOMPtr r12 = NewRunnableMethod( 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__); } r12->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 }