gecko-dev/xpcom/tests/gtest/TestThreadUtils.cpp

2187 строки
74 KiB
C++

/* 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 <type_traits>
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/IdleTaskRunner.h"
#include "mozilla/RefCounted.h"
#include "mozilla/UniquePtr.h"
#include "gtest/gtest.h"
using namespace mozilla;
enum {
TEST_CALL_VOID_ARG_VOID_RETURN,
TEST_CALL_VOID_ARG_VOID_RETURN_CONST,
TEST_CALL_VOID_ARG_NONVOID_RETURN,
TEST_CALL_NONVOID_ARG_VOID_RETURN,
TEST_CALL_NONVOID_ARG_NONVOID_RETURN,
TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT,
TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT,
#ifdef HAVE_STDCALL
TEST_STDCALL_VOID_ARG_VOID_RETURN,
TEST_STDCALL_VOID_ARG_NONVOID_RETURN,
TEST_STDCALL_NONVOID_ARG_VOID_RETURN,
TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN,
TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT,
#endif
TEST_CALL_NEWTHREAD_SUICIDAL,
MAX_TESTS
};
bool gRunnableExecuted[MAX_TESTS];
class nsFoo : public nsISupports {
NS_DECL_ISUPPORTS
nsresult DoFoo(bool* aBool) {
*aBool = true;
return NS_OK;
}
private:
virtual ~nsFoo() = default;
};
NS_IMPL_ISUPPORTS0(nsFoo)
class TestSuicide : public mozilla::Runnable {
public:
TestSuicide() : mozilla::Runnable("TestSuicide") {}
NS_IMETHOD Run() override {
// Runs first time on thread "Suicide", then dies on MainThread
if (!NS_IsMainThread()) {
mThread = do_GetCurrentThread();
NS_DispatchToMainThread(this);
return NS_OK;
}
MOZ_RELEASE_ASSERT(mThread);
mThread->Shutdown();
gRunnableExecuted[TEST_CALL_NEWTHREAD_SUICIDAL] = true;
return NS_OK;
}
private:
nsCOMPtr<nsIThread> mThread;
};
class nsBar : public nsISupports {
virtual ~nsBar() = default;
public:
NS_DECL_ISUPPORTS
void DoBar1(void) {
gRunnableExecuted[TEST_CALL_VOID_ARG_VOID_RETURN] = true;
}
void DoBar1Const(void) const {
gRunnableExecuted[TEST_CALL_VOID_ARG_VOID_RETURN_CONST] = true;
}
nsresult DoBar2(void) {
gRunnableExecuted[TEST_CALL_VOID_ARG_NONVOID_RETURN] = true;
return NS_OK;
}
void DoBar3(nsFoo* aFoo) {
aFoo->DoFoo(&gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN]);
}
nsresult DoBar4(nsFoo* aFoo) {
return aFoo->DoFoo(
&gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN]);
}
void DoBar5(nsFoo* aFoo) {
if (aFoo)
gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
}
nsresult DoBar6(char* aFoo) {
if (strlen(aFoo))
gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT] = true;
return NS_OK;
}
#ifdef HAVE_STDCALL
void __stdcall DoBar1std(void) {
gRunnableExecuted[TEST_STDCALL_VOID_ARG_VOID_RETURN] = true;
}
nsresult __stdcall DoBar2std(void) {
gRunnableExecuted[TEST_STDCALL_VOID_ARG_NONVOID_RETURN] = true;
return NS_OK;
}
void __stdcall DoBar3std(nsFoo* aFoo) {
aFoo->DoFoo(&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN]);
}
nsresult __stdcall DoBar4std(nsFoo* aFoo) {
return aFoo->DoFoo(
&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN]);
}
void __stdcall DoBar5std(nsFoo* aFoo) {
if (aFoo)
gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
}
nsresult __stdcall DoBar6std(char* aFoo) {
if (strlen(aFoo))
gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
return NS_OK;
}
#endif
};
NS_IMPL_ISUPPORTS0(nsBar)
struct TestCopyWithNoMove {
explicit TestCopyWithNoMove(int* aCopyCounter) : mCopyCounter(aCopyCounter) {}
TestCopyWithNoMove(const TestCopyWithNoMove& a)
: mCopyCounter(a.mCopyCounter) {
*mCopyCounter += 1;
};
// No 'move' declaration, allows passing object by rvalue copy.
// Destructor nulls member variable...
~TestCopyWithNoMove() { mCopyCounter = nullptr; }
// ... so we can check that the object is called when still alive.
void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); }
int* mCopyCounter;
};
struct TestCopyWithDeletedMove {
explicit TestCopyWithDeletedMove(int* aCopyCounter)
: mCopyCounter(aCopyCounter) {}
TestCopyWithDeletedMove(const TestCopyWithDeletedMove& a)
: mCopyCounter(a.mCopyCounter) {
*mCopyCounter += 1;
};
// Deleted move prevents passing by rvalue (even if copy would work)
TestCopyWithDeletedMove(TestCopyWithDeletedMove&&) = delete;
~TestCopyWithDeletedMove() { mCopyCounter = nullptr; }
void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); }
int* mCopyCounter;
};
struct TestMove {
explicit TestMove(int* aMoveCounter) : mMoveCounter(aMoveCounter) {}
TestMove(const TestMove&) = delete;
TestMove(TestMove&& a) : mMoveCounter(a.mMoveCounter) {
a.mMoveCounter = nullptr;
*mMoveCounter += 1;
}
~TestMove() { mMoveCounter = nullptr; }
void operator()() { MOZ_RELEASE_ASSERT(mMoveCounter); }
int* mMoveCounter;
};
struct TestCopyMove {
TestCopyMove(int* aCopyCounter, int* aMoveCounter)
: mCopyCounter(aCopyCounter), mMoveCounter(aMoveCounter) {}
TestCopyMove(const TestCopyMove& a)
: mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) {
*mCopyCounter += 1;
};
TestCopyMove(TestCopyMove&& a)
: mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) {
a.mMoveCounter = nullptr;
*mMoveCounter += 1;
}
~TestCopyMove() {
mCopyCounter = nullptr;
mMoveCounter = nullptr;
}
void operator()() {
MOZ_RELEASE_ASSERT(mCopyCounter);
MOZ_RELEASE_ASSERT(mMoveCounter);
}
int* mCopyCounter;
int* mMoveCounter;
};
struct TestRefCounted : RefCounted<TestRefCounted> {
MOZ_DECLARE_REFCOUNTED_TYPENAME(TestRefCounted);
};
static void Expect(const char* aContext, int aCounter, int aMaxExpected) {
EXPECT_LE(aCounter, aMaxExpected) << aContext;
}
static void ExpectRunnableName(Runnable* aRunnable, const char* aExpectedName) {
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
nsAutoCString name;
EXPECT_TRUE(NS_SUCCEEDED(aRunnable->GetName(name))) << "Runnable::GetName()";
EXPECT_TRUE(name.EqualsASCII(aExpectedName)) << "Verify Runnable name";
#endif
}
struct BasicRunnableFactory {
static constexpr bool SupportsCopyWithDeletedMove = true;
template <typename Function>
static auto Create(const char* aName, Function&& aFunc) {
return NS_NewRunnableFunction(aName, std::forward<Function>(aFunc));
}
};
struct CancelableRunnableFactory {
static constexpr bool SupportsCopyWithDeletedMove = false;
template <typename Function>
static auto Create(const char* aName, Function&& aFunc) {
return NS_NewCancelableRunnableFunction(aName,
std::forward<Function>(aFunc));
}
};
template <typename RunnableFactory>
static void TestRunnableFactory(bool aNamed) {
// Test RunnableFactory with copyable-only function object.
{
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyWithNoMove tracker(&copyCounter);
trackedRunnable = aNamed ? RunnableFactory::Create("unused", tracker)
: RunnableFactory::Create(
"TestNewRunnableFunction", tracker);
// Original 'tracker' is destroyed here.
}
// Verify that the runnable contains a non-destroyed function object.
trackedRunnable->Run();
}
Expect(
"RunnableFactory with copyable-only (and no move) function, "
"copies",
copyCounter, 1);
}
{
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
// Passing as rvalue, but using copy.
// (TestCopyWithDeletedMove wouldn't allow this.)
trackedRunnable =
aNamed ? RunnableFactory::Create("unused",
TestCopyWithNoMove(&copyCounter))
: RunnableFactory::Create("TestNewRunnableFunction",
TestCopyWithNoMove(&copyCounter));
}
trackedRunnable->Run();
}
Expect(
"RunnableFactory with copyable-only (and no move) function "
"rvalue, copies",
copyCounter, 1);
}
if constexpr (RunnableFactory::SupportsCopyWithDeletedMove) {
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyWithDeletedMove tracker(&copyCounter);
trackedRunnable = aNamed ? RunnableFactory::Create("unused", tracker)
: RunnableFactory::Create(
"TestNewRunnableFunction", tracker);
}
trackedRunnable->Run();
}
Expect(
"RunnableFactory with copyable-only (and deleted move) "
"function, copies",
copyCounter, 1);
}
// Test RunnableFactory with movable-only function object.
{
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestMove tracker(&moveCounter);
trackedRunnable =
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
: RunnableFactory::Create("TestNewRunnableFunction",
std::move(tracker));
}
trackedRunnable->Run();
}
Expect("RunnableFactory with movable-only function, moves", moveCounter, 1);
}
{
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
trackedRunnable =
aNamed ? RunnableFactory::Create("unused", TestMove(&moveCounter))
: RunnableFactory::Create("TestNewRunnableFunction",
TestMove(&moveCounter));
}
trackedRunnable->Run();
}
Expect("RunnableFactory with movable-only function rvalue, moves",
moveCounter, 1);
}
// Test RunnableFactory with copyable&movable function object.
{
int copyCounter = 0;
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyMove tracker(&copyCounter, &moveCounter);
trackedRunnable =
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
: RunnableFactory::Create("TestNewRunnableFunction",
std::move(tracker));
}
trackedRunnable->Run();
}
Expect("RunnableFactory with copyable&movable function, copies",
copyCounter, 0);
Expect("RunnableFactory with copyable&movable function, moves", moveCounter,
1);
}
{
int copyCounter = 0;
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
trackedRunnable =
aNamed ? RunnableFactory::Create(
"unused", TestCopyMove(&copyCounter, &moveCounter))
: RunnableFactory::Create(
"TestNewRunnableFunction",
TestCopyMove(&copyCounter, &moveCounter));
}
trackedRunnable->Run();
}
Expect("RunnableFactory with copyable&movable function rvalue, copies",
copyCounter, 0);
Expect("RunnableFactory with copyable&movable function rvalue, moves",
moveCounter, 1);
}
// Test RunnableFactory with copyable-only lambda capture.
{
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyWithNoMove tracker(&copyCounter);
// Expect 2 copies (here -> local lambda -> runnable lambda).
trackedRunnable =
aNamed
? RunnableFactory::Create("unused",
[tracker]() mutable { tracker(); })
: RunnableFactory::Create("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
}
trackedRunnable->Run();
}
Expect(
"RunnableFactory with copyable-only (and no move) capture, "
"copies",
copyCounter, 2);
}
{
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyWithDeletedMove tracker(&copyCounter);
// Expect 2 copies (here -> local lambda -> runnable lambda).
trackedRunnable =
aNamed
? RunnableFactory::Create("unused",
[tracker]() mutable { tracker(); })
: RunnableFactory::Create("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
}
trackedRunnable->Run();
}
Expect(
"RunnableFactory with copyable-only (and deleted move) capture, "
"copies",
copyCounter, 2);
}
// Note: Not possible to use move-only captures.
// (Until we can use C++14 generalized lambda captures)
// Test RunnableFactory with copyable&movable lambda capture.
{
int copyCounter = 0;
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyMove tracker(&copyCounter, &moveCounter);
trackedRunnable =
aNamed
? RunnableFactory::Create("unused",
[tracker]() mutable { tracker(); })
: RunnableFactory::Create("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
// Expect 1 copy (here -> local lambda) and 1 move (local -> runnable
// lambda).
}
trackedRunnable->Run();
}
Expect("RunnableFactory with copyable&movable capture, copies", copyCounter,
1);
Expect("RunnableFactory with copyable&movable capture, moves", moveCounter,
1);
}
}
TEST(ThreadUtils, NewRunnableFunction)
{ TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ false); }
TEST(ThreadUtils, NewNamedRunnableFunction)
{
// The named overload shall behave identical to the non-named counterpart.
TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ true);
// Test naming.
{
const char* expectedName = "NamedRunnable";
RefPtr<Runnable> NamedRunnable =
NS_NewRunnableFunction(expectedName, [] {});
ExpectRunnableName(NamedRunnable, expectedName);
}
}
TEST(ThreadUtils, NewCancelableRunnableFunction)
{ TestRunnableFactory<CancelableRunnableFactory>(/*aNamed*/ false); }
TEST(ThreadUtils, NewNamedCancelableRunnableFunction)
{
// The named overload shall behave identical to the non-named counterpart.
TestRunnableFactory<CancelableRunnableFactory>(/*aNamed*/ true);
// Test naming.
{
const char* expectedName = "NamedRunnable";
RefPtr<Runnable> NamedRunnable =
NS_NewCancelableRunnableFunction(expectedName, [] {});
ExpectRunnableName(NamedRunnable, expectedName);
}
// Test release on cancelation.
{
auto foo = MakeRefPtr<TestRefCounted>();
bool ran = false;
RefPtr<CancelableRunnable> func =
NS_NewCancelableRunnableFunction("unused", [foo, &ran] { ran = true; });
EXPECT_EQ(foo->refCount(), 2u);
func->Cancel();
EXPECT_EQ(foo->refCount(), 1u);
EXPECT_FALSE(ran);
}
}
static void TestNewRunnableMethod(bool aNamed) {
memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool));
// Scope the smart ptrs so that the runnables need to hold on to whatever they
// need
{
RefPtr<nsFoo> foo = new nsFoo();
RefPtr<nsBar> bar = new nsBar();
RefPtr<const nsBar> constBar = bar;
// This pointer will be freed at the end of the block
// Do not dereference this pointer in the runnable method!
RefPtr<nsFoo> rawFoo = new nsFoo();
// Read only string. Dereferencing in runnable method to check this works.
char* message = (char*)"Test message";
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod("unused", bar, &nsBar::DoBar1)
: NewRunnableMethod("nsBar::DoBar1", bar, &nsBar::DoBar1));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod("unused", constBar, &nsBar::DoBar1Const)
: NewRunnableMethod("nsBar::DoBar1Const", constBar,
&nsBar::DoBar1Const));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod("unused", bar, &nsBar::DoBar2)
: NewRunnableMethod("nsBar::DoBar2", bar, &nsBar::DoBar2));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar3,
foo)
: NewRunnableMethod<RefPtr<nsFoo>>("nsBar::DoBar3", bar,
&nsBar::DoBar3, foo));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar4,
foo)
: NewRunnableMethod<RefPtr<nsFoo>>("nsBar::DoBar4", bar,
&nsBar::DoBar4, foo));
NS_DispatchToMainThread(
aNamed
? NewRunnableMethod<nsFoo*>("unused", bar, &nsBar::DoBar5, rawFoo)
: NewRunnableMethod<nsFoo*>("nsBar::DoBar5", bar, &nsBar::DoBar5,
rawFoo));
NS_DispatchToMainThread(
aNamed
? NewRunnableMethod<char*>("unused", bar, &nsBar::DoBar6, message)
: NewRunnableMethod<char*>("nsBar::DoBar6", bar, &nsBar::DoBar6,
message));
#ifdef HAVE_STDCALL
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod("unused", bar, &nsBar::DoBar1std)
: NewRunnableMethod(bar, &nsBar::DoBar1std));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod("unused", bar, &nsBar::DoBar2std)
: NewRunnableMethod(bar, &nsBar::DoBar2std));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod<RefPtr<nsFoo>>("unused", bar,
&nsBar::DoBar3std, foo)
: NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar3std, foo));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod<RefPtr<nsFoo>>("unused", bar,
&nsBar::DoBar4std, foo)
: NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar4std, foo));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod<nsFoo*>("unused", bar, &nsBar::DoBar5std,
rawFoo)
: NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5std, rawFoo));
NS_DispatchToMainThread(
aNamed ? NewRunnableMethod<char*>("unused", bar, &nsBar::DoBar6std,
message)
: NewRunnableMethod<char*>(bar, &nsBar::DoBar6std, message));
#endif
}
// Spin the event loop
NS_ProcessPendingEvents(nullptr);
// Now test a suicidal event in NS_New(Named)Thread
nsCOMPtr<nsIThread> thread;
NS_NewNamedThread("SuicideThread", getter_AddRefs(thread), new TestSuicide());
ASSERT_TRUE(thread);
while (!gRunnableExecuted[TEST_CALL_NEWTHREAD_SUICIDAL]) {
NS_ProcessPendingEvents(nullptr);
}
for (uint32_t i = 0; i < MAX_TESTS; i++) {
EXPECT_TRUE(gRunnableExecuted[i]) << "Error in test " << i;
}
}
TEST(ThreadUtils, RunnableMethod)
{ TestNewRunnableMethod(/* aNamed */ false); }
TEST(ThreadUtils, NamedRunnableMethod)
{
// The named overloads shall behave identical to the non-named counterparts.
TestNewRunnableMethod(/* aNamed */ true);
// Test naming.
{
RefPtr<nsFoo> foo = new nsFoo();
const char* expectedName = "NamedRunnable";
bool unused;
RefPtr<Runnable> NamedRunnable =
NewRunnableMethod<bool*>(expectedName, foo, &nsFoo::DoFoo, &unused);
ExpectRunnableName(NamedRunnable, expectedName);
}
}
class IdleObjectWithoutSetDeadline final {
public:
NS_INLINE_DECL_REFCOUNTING(IdleObjectWithoutSetDeadline)
IdleObjectWithoutSetDeadline() : mRunnableExecuted(false) {}
void Method() { mRunnableExecuted = true; }
bool mRunnableExecuted;
private:
~IdleObjectWithoutSetDeadline() = default;
};
class IdleObjectParentWithSetDeadline {
public:
IdleObjectParentWithSetDeadline() : mSetDeadlineCalled(false) {}
void SetDeadline(TimeStamp aDeadline) { mSetDeadlineCalled = true; }
bool mSetDeadlineCalled;
};
class IdleObjectInheritedSetDeadline final
: public IdleObjectParentWithSetDeadline {
public:
NS_INLINE_DECL_REFCOUNTING(IdleObjectInheritedSetDeadline)
IdleObjectInheritedSetDeadline() : mRunnableExecuted(false) {}
void Method() { mRunnableExecuted = true; }
bool mRunnableExecuted;
private:
~IdleObjectInheritedSetDeadline() = default;
};
class IdleObject final {
public:
NS_INLINE_DECL_REFCOUNTING(IdleObject)
IdleObject() {
for (uint32_t index = 0; index < ArrayLength(mRunnableExecuted); ++index) {
mRunnableExecuted[index] = false;
mSetIdleDeadlineCalled = false;
}
}
void SetDeadline(TimeStamp aTimeStamp) { mSetIdleDeadlineCalled = true; }
void CheckExecutedMethods(const char* aKey, uint32_t aNumExecuted) {
uint32_t index;
for (index = 0; index < aNumExecuted; ++index) {
ASSERT_TRUE(mRunnableExecuted[index])
<< aKey << ": Method" << index << " should've executed";
}
for (; index < ArrayLength(mRunnableExecuted); ++index) {
ASSERT_FALSE(mRunnableExecuted[index])
<< aKey << ": Method" << index << " shouldn't have executed";
}
}
void Method0() {
CheckExecutedMethods("Method0", 0);
mRunnableExecuted[0] = true;
mSetIdleDeadlineCalled = false;
}
void Method1() {
CheckExecutedMethods("Method1", 1);
ASSERT_TRUE(mSetIdleDeadlineCalled);
mRunnableExecuted[1] = true;
mSetIdleDeadlineCalled = false;
}
void Method2() {
CheckExecutedMethods("Method2", 2);
ASSERT_TRUE(mSetIdleDeadlineCalled);
mRunnableExecuted[2] = true;
mSetIdleDeadlineCalled = false;
NS_DispatchToCurrentThread(
NewRunnableMethod("IdleObject::Method3", this, &IdleObject::Method3));
}
void Method3() {
CheckExecutedMethods("Method3", 3);
NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer), Method4, this, 10,
nsITimer::TYPE_ONE_SHOT, "IdleObject::Method3");
NS_DispatchToCurrentThreadQueue(
NewIdleRunnableMethodWithTimer("IdleObject::Method5", this,
&IdleObject::Method5),
50, EventQueuePriority::Idle);
NS_DispatchToCurrentThreadQueue(
NewRunnableMethod("IdleObject::Method6", this, &IdleObject::Method6),
100, EventQueuePriority::Idle);
PR_Sleep(PR_MillisecondsToInterval(200));
mRunnableExecuted[3] = true;
mSetIdleDeadlineCalled = false;
}
static void Method4(nsITimer* aTimer, void* aClosure) {
RefPtr<IdleObject> self = static_cast<IdleObject*>(aClosure);
self->CheckExecutedMethods("Method4", 4);
self->mRunnableExecuted[4] = true;
self->mSetIdleDeadlineCalled = false;
}
void Method5() {
CheckExecutedMethods("Method5", 5);
ASSERT_TRUE(mSetIdleDeadlineCalled);
mRunnableExecuted[5] = true;
mSetIdleDeadlineCalled = false;
}
void Method6() {
CheckExecutedMethods("Method6", 6);
mRunnableExecuted[6] = true;
mSetIdleDeadlineCalled = false;
}
void Method7() {
CheckExecutedMethods("Method7", 7);
ASSERT_TRUE(mSetIdleDeadlineCalled);
mRunnableExecuted[7] = true;
mSetIdleDeadlineCalled = false;
}
private:
nsCOMPtr<nsITimer> mTimer;
bool mRunnableExecuted[8];
bool mSetIdleDeadlineCalled;
~IdleObject() = default;
};
TEST(ThreadUtils, IdleRunnableMethod)
{
{
RefPtr<IdleObject> idle = new IdleObject();
RefPtr<IdleObjectWithoutSetDeadline> idleNoSetDeadline =
new IdleObjectWithoutSetDeadline();
RefPtr<IdleObjectInheritedSetDeadline> idleInheritedSetDeadline =
new IdleObjectInheritedSetDeadline();
NS_DispatchToCurrentThread(
NewRunnableMethod("IdleObject::Method0", idle, &IdleObject::Method0));
NS_DispatchToCurrentThreadQueue(
NewIdleRunnableMethod("IdleObject::Method1", idle,
&IdleObject::Method1),
EventQueuePriority::Idle);
NS_DispatchToCurrentThreadQueue(
NewIdleRunnableMethodWithTimer("IdleObject::Method2", idle,
&IdleObject::Method2),
60000, EventQueuePriority::Idle);
NS_DispatchToCurrentThreadQueue(
NewIdleRunnableMethod("IdleObject::Method7", idle,
&IdleObject::Method7),
EventQueuePriority::Idle);
NS_DispatchToCurrentThreadQueue(
NewIdleRunnableMethod<const char*, uint32_t>(
"IdleObject::CheckExecutedMethods", idle,
&IdleObject::CheckExecutedMethods, "final", 8),
EventQueuePriority::Idle);
NS_DispatchToCurrentThreadQueue(
NewIdleRunnableMethod("IdleObjectWithoutSetDeadline::Method",
idleNoSetDeadline,
&IdleObjectWithoutSetDeadline::Method),
EventQueuePriority::Idle);
NS_DispatchToCurrentThreadQueue(
NewIdleRunnableMethod("IdleObjectInheritedSetDeadline::Method",
idleInheritedSetDeadline,
&IdleObjectInheritedSetDeadline::Method),
EventQueuePriority::Idle);
NS_ProcessPendingEvents(nullptr);
ASSERT_TRUE(idleNoSetDeadline->mRunnableExecuted);
ASSERT_TRUE(idleInheritedSetDeadline->mRunnableExecuted);
ASSERT_TRUE(idleInheritedSetDeadline->mSetDeadlineCalled);
}
}
TEST(ThreadUtils, IdleTaskRunner)
{
using namespace mozilla;
// Repeating.
int cnt1 = 0;
RefPtr<IdleTaskRunner> runner1 = IdleTaskRunner::Create(
[&cnt1](TimeStamp) {
cnt1++;
return true;
},
"runner1", 10, 3, true, nullptr);
// Non-repeating but callback always return false so it's still repeating.
int cnt2 = 0;
RefPtr<IdleTaskRunner> runner2 = IdleTaskRunner::Create(
[&cnt2](TimeStamp) {
cnt2++;
return false;
},
"runner2", 10, 3, false, nullptr);
// Repeating until cnt3 >= 2 by returning 'true' in MayStopProcessing
// callback. The strategy is to stop repeating as early as possible so that we
// are more probable to catch the bug if it didn't stop as expected.
int cnt3 = 0;
RefPtr<IdleTaskRunner> runner3 = IdleTaskRunner::Create(
[&cnt3](TimeStamp) {
cnt3++;
return true;
},
"runner3", 10, 3, true, [&cnt3] { return cnt3 >= 2; });
// Non-repeating can callback return true so the callback will
// be only run once.
int cnt4 = 0;
RefPtr<IdleTaskRunner> runner4 = IdleTaskRunner::Create(
[&cnt4](TimeStamp) {
cnt4++;
return true;
},
"runner4", 10, 3, false, nullptr);
// Firstly we wait until the two repeating tasks reach their limits.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cnt1 >= 100; }));
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cnt2 >= 100; }));
// At any point ==> 0 <= cnt3 <= 2 since MayStopProcessing() would return
// true when cnt3 >= 2.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
if (cnt3 > 2) {
EXPECT_TRUE(false) << "MaybeContinueProcess() doesn't work.";
return true; // Stop on failure.
}
return cnt3 == 2; // Stop finish if we have reached its max value.
}));
// At any point ==> 0 <= cnt4 <= 1 since this is a non-repeating
// idle runner.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
// At any point: 0 <= cnt4 <= 1
if (cnt4 > 1) {
EXPECT_TRUE(false) << "The 'mRepeating' flag doesn't work.";
return true; // Stop on failure.
}
return cnt4 == 1;
}));
// The repeating timers require an explicit Cancel() call.
runner1->Cancel();
runner2->Cancel();
}
// {9e70a320-be02-11d1-8031-006008159b5a}
#define NS_IFOO_IID \
{ \
0x9e70a320, 0xbe02, 0x11d1, { \
0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a \
} \
}
TEST(ThreadUtils, TypeTraits)
{
static_assert(!mozilla::IsRefcountedSmartPointer<int>::value,
"IsRefcountedSmartPointer<int> should be false");
static_assert(mozilla::IsRefcountedSmartPointer<RefPtr<int>>::value,
"IsRefcountedSmartPointer<RefPtr<...>> should be true");
static_assert(mozilla::IsRefcountedSmartPointer<const RefPtr<int>>::value,
"IsRefcountedSmartPointer<const RefPtr<...>> should be true");
static_assert(
mozilla::IsRefcountedSmartPointer<volatile RefPtr<int>>::value,
"IsRefcountedSmartPointer<volatile RefPtr<...>> should be true");
static_assert(
mozilla::IsRefcountedSmartPointer<const volatile RefPtr<int>>::value,
"IsRefcountedSmartPointer<const volatile RefPtr<...>> should be true");
static_assert(mozilla::IsRefcountedSmartPointer<nsCOMPtr<int>>::value,
"IsRefcountedSmartPointer<nsCOMPtr<...>> should be true");
static_assert(mozilla::IsRefcountedSmartPointer<const nsCOMPtr<int>>::value,
"IsRefcountedSmartPointer<const nsCOMPtr<...>> should be true");
static_assert(
mozilla::IsRefcountedSmartPointer<volatile nsCOMPtr<int>>::value,
"IsRefcountedSmartPointer<volatile nsCOMPtr<...>> should be true");
static_assert(
mozilla::IsRefcountedSmartPointer<const volatile nsCOMPtr<int>>::value,
"IsRefcountedSmartPointer<const volatile nsCOMPtr<...>> should be true");
static_assert(std::is_same_v<int, mozilla::RemoveSmartPointer<int>::Type>,
"RemoveSmartPointer<int>::Type should be int");
static_assert(std::is_same_v<int*, mozilla::RemoveSmartPointer<int*>::Type>,
"RemoveSmartPointer<int*>::Type should be int*");
static_assert(
std::is_same_v<UniquePtr<int>,
mozilla::RemoveSmartPointer<UniquePtr<int>>::Type>,
"RemoveSmartPointer<UniquePtr<int>>::Type should be UniquePtr<int>");
static_assert(
std::is_same_v<int, mozilla::RemoveSmartPointer<RefPtr<int>>::Type>,
"RemoveSmartPointer<RefPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int, mozilla::RemoveSmartPointer<const RefPtr<int>>::Type>,
"RemoveSmartPointer<const RefPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int,
mozilla::RemoveSmartPointer<volatile RefPtr<int>>::Type>,
"RemoveSmartPointer<volatile RefPtr<int>>::Type should be int");
static_assert(
std::is_same_v<
int, mozilla::RemoveSmartPointer<const volatile RefPtr<int>>::Type>,
"RemoveSmartPointer<const volatile RefPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int, mozilla::RemoveSmartPointer<nsCOMPtr<int>>::Type>,
"RemoveSmartPointer<nsCOMPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int,
mozilla::RemoveSmartPointer<const nsCOMPtr<int>>::Type>,
"RemoveSmartPointer<const nsCOMPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int,
mozilla::RemoveSmartPointer<volatile nsCOMPtr<int>>::Type>,
"RemoveSmartPointer<volatile nsCOMPtr<int>>::Type should be int");
static_assert(
std::is_same_v<
int, mozilla::RemoveSmartPointer<const volatile nsCOMPtr<int>>::Type>,
"RemoveSmartPointer<const volatile nsCOMPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int, mozilla::RemoveRawOrSmartPointer<int>::Type>,
"RemoveRawOrSmartPointer<int>::Type should be int");
static_assert(
std::is_same_v<UniquePtr<int>,
mozilla::RemoveRawOrSmartPointer<UniquePtr<int>>::Type>,
"RemoveRawOrSmartPointer<UniquePtr<int>>::Type should be UniquePtr<int>");
static_assert(
std::is_same_v<int, mozilla::RemoveRawOrSmartPointer<int*>::Type>,
"RemoveRawOrSmartPointer<int*>::Type should be int");
static_assert(
std::is_same_v<const int,
mozilla::RemoveRawOrSmartPointer<const int*>::Type>,
"RemoveRawOrSmartPointer<const int*>::Type should be const int");
static_assert(
std::is_same_v<volatile int,
mozilla::RemoveRawOrSmartPointer<volatile int*>::Type>,
"RemoveRawOrSmartPointer<volatile int*>::Type should be volatile int");
static_assert(
std::is_same_v<const volatile int, mozilla::RemoveRawOrSmartPointer<
const volatile int*>::Type>,
"RemoveRawOrSmartPointer<const volatile int*>::Type should be const "
"volatile int");
static_assert(
std::is_same_v<int, mozilla::RemoveRawOrSmartPointer<RefPtr<int>>::Type>,
"RemoveRawOrSmartPointer<RefPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int,
mozilla::RemoveRawOrSmartPointer<const RefPtr<int>>::Type>,
"RemoveRawOrSmartPointer<const RefPtr<int>>::Type should be int");
static_assert(
std::is_same_v<
int, mozilla::RemoveRawOrSmartPointer<volatile RefPtr<int>>::Type>,
"RemoveRawOrSmartPointer<volatile RefPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int, mozilla::RemoveRawOrSmartPointer<
const volatile RefPtr<int>>::Type>,
"RemoveRawOrSmartPointer<const volatile RefPtr<int>>::Type should be "
"int");
static_assert(
std::is_same_v<int,
mozilla::RemoveRawOrSmartPointer<nsCOMPtr<int>>::Type>,
"RemoveRawOrSmartPointer<nsCOMPtr<int>>::Type should be int");
static_assert(
std::is_same_v<
int, mozilla::RemoveRawOrSmartPointer<const nsCOMPtr<int>>::Type>,
"RemoveRawOrSmartPointer<const nsCOMPtr<int>>::Type should be int");
static_assert(
std::is_same_v<
int, mozilla::RemoveRawOrSmartPointer<volatile nsCOMPtr<int>>::Type>,
"RemoveRawOrSmartPointer<volatile nsCOMPtr<int>>::Type should be int");
static_assert(
std::is_same_v<int, mozilla::RemoveRawOrSmartPointer<
const volatile nsCOMPtr<int>>::Type>,
"RemoveRawOrSmartPointer<const volatile nsCOMPtr<int>>::Type should be "
"int");
}
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 ThreadUtilsObjectNonRefCountedBase {
virtual void MethodFromNonRefCountedBase() {}
};
struct ThreadUtilsObject : public IThreadUtilsObject,
public ThreadUtilsObjectNonRefCountedBase {
// 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<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 = std::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() = default;
// '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() = default;
// '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)
{
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<StoreRefPtrPassByPtr<int>>::value,
"StoreRefPtrPassByPtr<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");
RefPtr<ThreadUtilsObject> rpt(new ThreadUtilsObject);
int count = 0;
// Test legacy functions.
nsCOMPtr<nsIRunnable> r1 =
NewRunnableMethod("TestThreadUtils::ThreadUtilsObject::Test0", rpt,
&ThreadUtilsObject::Test0);
r1->Run();
EXPECT_EQ(count += 1, rpt->mCount);
r1 = NewRunnableMethod<int>("TestThreadUtils::ThreadUtilsObject::Test1i", rpt,
&ThreadUtilsObject::Test1i, 11);
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(11, rpt->mA0);
// Test calling a method from a non-ref-counted base.
r1 = NewRunnableMethod(
"TestThreadUtils::ThreadUtilsObjectNonRefCountedBase::"
"MethodFromNonRefCountedBase",
rpt, &ThreadUtilsObject::MethodFromNonRefCountedBase);
r1->Run();
EXPECT_EQ(count, rpt->mCount);
// Test variadic function with simple POD arguments.
r1 = NewRunnableMethod("TestThreadUtils::ThreadUtilsObject::Test0", rpt,
&ThreadUtilsObject::Test0);
r1->Run();
EXPECT_EQ(count += 1, rpt->mCount);
static_assert(std::is_same_v<::detail::ParameterStorage<int>::Type,
StoreCopyPassByConstLRef<int>>,
"detail::ParameterStorage<int>::Type should be "
"StoreCopyPassByConstLRef<int>");
static_assert(std::is_same_v<
::detail::ParameterStorage<StoreCopyPassByValue<int>>::Type,
StoreCopyPassByValue<int>>,
"detail::ParameterStorage<StoreCopyPassByValue<int>>::Type "
"should be StoreCopyPassByValue<int>");
r1 = NewRunnableMethod<int>("TestThreadUtils::ThreadUtilsObject::Test1i", rpt,
&ThreadUtilsObject::Test1i, 12);
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(12, rpt->mA0);
r1 = NewRunnableMethod<int, int>("TestThreadUtils::ThreadUtilsObject::Test2i",
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<int, int, int>(
"TestThreadUtils::ThreadUtilsObject::Test3i", 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<int, int, int, int>(
"TestThreadUtils::ThreadUtilsObject::Test4i", 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<int>("TestThreadUtils::ThreadUtilsObject::Test1i", rpt,
&ThreadUtilsObject::Test1i, si);
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(si, rpt->mA0);
// Raw pointer, possible cv-qualified.
static_assert(
std::is_same_v<::detail::ParameterStorage<int*>::Type,
StorePtrPassByPtr<int>>,
"detail::ParameterStorage<int*>::Type should be StorePtrPassByPtr<int>");
static_assert(std::is_same_v<::detail::ParameterStorage<int* const>::Type,
StorePtrPassByPtr<int>>,
"detail::ParameterStorage<int* const>::Type should be "
"StorePtrPassByPtr<int>");
static_assert(std::is_same_v<::detail::ParameterStorage<int* volatile>::Type,
StorePtrPassByPtr<int>>,
"detail::ParameterStorage<int* volatile>::Type should be "
"StorePtrPassByPtr<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<int* const volatile>::Type,
StorePtrPassByPtr<int>>,
"detail::ParameterStorage<int* const volatile>::Type should be "
"StorePtrPassByPtr<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<int*>::Type::stored_type, int*>,
"detail::ParameterStorage<int*>::Type::stored_type should be int*");
static_assert(
std::is_same_v<::detail::ParameterStorage<int*>::Type::passed_type, int*>,
"detail::ParameterStorage<int*>::Type::passed_type should be int*");
{
int i = 12;
r1 = NewRunnableMethod<int*>("TestThreadUtils::ThreadUtilsObject::Test1pi",
rpt, &ThreadUtilsObject::Test1pi, &i);
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(i, rpt->mA0);
}
// Raw pointer to const.
static_assert(std::is_same_v<::detail::ParameterStorage<const int*>::Type,
StoreConstPtrPassByConstPtr<int>>,
"detail::ParameterStorage<const int*>::Type should be "
"StoreConstPtrPassByConstPtr<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<const int* const>::Type,
StoreConstPtrPassByConstPtr<int>>,
"detail::ParameterStorage<const int* const>::Type should be "
"StoreConstPtrPassByConstPtr<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<const int* volatile>::Type,
StoreConstPtrPassByConstPtr<int>>,
"detail::ParameterStorage<const int* volatile>::Type should be "
"StoreConstPtrPassByConstPtr<int>");
static_assert(std::is_same_v<
::detail::ParameterStorage<const int* const volatile>::Type,
StoreConstPtrPassByConstPtr<int>>,
"detail::ParameterStorage<const int* const volatile>::Type "
"should be StoreConstPtrPassByConstPtr<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<const int*>::Type::stored_type,
const int*>,
"detail::ParameterStorage<const int*>::Type::stored_type should be const "
"int*");
static_assert(
std::is_same_v<::detail::ParameterStorage<const int*>::Type::passed_type,
const int*>,
"detail::ParameterStorage<const int*>::Type::passed_type should be const "
"int*");
{
int i = 1201;
r1 = NewRunnableMethod<const int*>(
"TestThreadUtils::ThreadUtilsObject::Test1pci", rpt,
&ThreadUtilsObject::Test1pci, &i);
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(i, rpt->mA0);
}
// Raw pointer to copy.
static_assert(std::is_same_v<StoreCopyPassByPtr<int>::stored_type, int>,
"StoreCopyPassByPtr<int>::stored_type should be int");
static_assert(std::is_same_v<StoreCopyPassByPtr<int>::passed_type, int*>,
"StoreCopyPassByPtr<int>::passed_type should be int*");
{
int i = 1202;
r1 = NewRunnableMethod<StoreCopyPassByPtr<int>>(
"TestThreadUtils::ThreadUtilsObject::Test1pi", rpt,
&ThreadUtilsObject::Test1pi, i);
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(i, rpt->mA0);
}
// Raw pointer to const copy.
static_assert(std::is_same_v<StoreCopyPassByConstPtr<int>::stored_type, int>,
"StoreCopyPassByConstPtr<int>::stored_type should be int");
static_assert(
std::is_same_v<StoreCopyPassByConstPtr<int>::passed_type, const int*>,
"StoreCopyPassByConstPtr<int>::passed_type should be const int*");
{
int i = 1203;
r1 = NewRunnableMethod<StoreCopyPassByConstPtr<int>>(
"TestThreadUtils::ThreadUtilsObject::Test1pci", rpt,
&ThreadUtilsObject::Test1pci, i);
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(i, rpt->mA0);
}
// nsRefPtr to pointer.
static_assert(
std::is_same_v<::detail::ParameterStorage<
StoreRefPtrPassByPtr<SpyWithISupports>>::Type,
StoreRefPtrPassByPtr<SpyWithISupports>>,
"ParameterStorage<StoreRefPtrPassByPtr<SpyWithISupports>>::Type should "
"be StoreRefPtrPassByPtr<SpyWithISupports>");
static_assert(
std::is_same_v<::detail::ParameterStorage<SpyWithISupports*>::Type,
StoreRefPtrPassByPtr<SpyWithISupports>>,
"ParameterStorage<SpyWithISupports*>::Type should be "
"StoreRefPtrPassByPtr<SpyWithISupports>");
static_assert(
std::is_same_v<StoreRefPtrPassByPtr<SpyWithISupports>::stored_type,
RefPtr<SpyWithISupports>>,
"StoreRefPtrPassByPtr<SpyWithISupports>::stored_type should be "
"RefPtr<SpyWithISupports>");
static_assert(
std::is_same_v<StoreRefPtrPassByPtr<SpyWithISupports>::passed_type,
SpyWithISupports*>,
"StoreRefPtrPassByPtr<SpyWithISupports>::passed_type should be "
"SpyWithISupports*");
// (more nsRefPtr tests below)
// nsRefPtr for ref-countable classes that do not derive from ISupports.
static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedFinal>::value,
"ThreadUtilsRefCountedFinal has AddRef() and Release()");
static_assert(
std::is_same_v<
::detail::ParameterStorage<ThreadUtilsRefCountedFinal*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsRefCountedFinal>>,
"ParameterStorage<ThreadUtilsRefCountedFinal*>::Type should be "
"StoreRefPtrPassByPtr<ThreadUtilsRefCountedFinal>");
static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedBase>::value,
"ThreadUtilsRefCountedBase has AddRef() and Release()");
static_assert(
std::is_same_v<
::detail::ParameterStorage<ThreadUtilsRefCountedBase*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsRefCountedBase>>,
"ParameterStorage<ThreadUtilsRefCountedBase*>::Type should be "
"StoreRefPtrPassByPtr<ThreadUtilsRefCountedBase>");
static_assert(
::detail::HasRefCountMethods<ThreadUtilsRefCountedDerived>::value,
"ThreadUtilsRefCountedDerived has AddRef() and Release()");
static_assert(
std::is_same_v<
::detail::ParameterStorage<ThreadUtilsRefCountedDerived*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsRefCountedDerived>>,
"ParameterStorage<ThreadUtilsRefCountedDerived*>::Type should be "
"StoreRefPtrPassByPtr<ThreadUtilsRefCountedDerived>");
static_assert(!::detail::HasRefCountMethods<ThreadUtilsNonRefCounted>::value,
"ThreadUtilsNonRefCounted doesn't have AddRef() and Release()");
static_assert(!std::is_same_v<
::detail::ParameterStorage<ThreadUtilsNonRefCounted*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsNonRefCounted>>,
"ParameterStorage<ThreadUtilsNonRefCounted*>::Type should NOT "
"be StoreRefPtrPassByPtr<ThreadUtilsNonRefCounted>");
// Lvalue reference.
static_assert(
std::is_same_v<::detail::ParameterStorage<int&>::Type,
StoreRefPassByLRef<int>>,
"ParameterStorage<int&>::Type should be StoreRefPassByLRef<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<int&>::Type::stored_type,
StoreRefPassByLRef<int>::stored_type>,
"ParameterStorage<int&>::Type::stored_type should be "
"StoreRefPassByLRef<int>::stored_type");
static_assert(
std::is_same_v<::detail::ParameterStorage<int&>::Type::stored_type, int&>,
"ParameterStorage<int&>::Type::stored_type should be int&");
static_assert(
std::is_same_v<::detail::ParameterStorage<int&>::Type::passed_type, int&>,
"ParameterStorage<int&>::Type::passed_type should be int&");
{
int i = 13;
r1 = NewRunnableMethod<int&>("TestThreadUtils::ThreadUtilsObject::Test1ri",
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(
std::is_same_v<::detail::ParameterStorage<int&&>::Type,
StoreCopyPassByRRef<int>>,
"ParameterStorage<int&&>::Type should be StoreCopyPassByRRef<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<int&&>::Type::stored_type,
StoreCopyPassByRRef<int>::stored_type>,
"ParameterStorage<int&&>::Type::stored_type should be "
"StoreCopyPassByRRef<int>::stored_type");
static_assert(
std::is_same_v<::detail::ParameterStorage<int&&>::Type::stored_type, int>,
"ParameterStorage<int&&>::Type::stored_type should be int");
static_assert(
std::is_same_v<::detail::ParameterStorage<int&&>::Type::passed_type,
int&&>,
"ParameterStorage<int&&>::Type::passed_type should be int&&");
{
int i = 14;
r1 = NewRunnableMethod<int&&>(
"TestThreadUtils::ThreadUtilsObject::Test1rri", rpt,
&ThreadUtilsObject::Test1rri, std::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(std::is_same_v<
::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>>,
"ParameterStorage<UniquePtr<int>&&>::Type should be "
"StoreCopyPassByRRef<UniquePtr<int>>");
static_assert(
std::is_same_v<::detail::ParameterStorage<
mozilla::UniquePtr<int>&&>::Type::stored_type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>,
"ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be "
"StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
static_assert(
std::is_same_v<::detail::ParameterStorage<
mozilla::UniquePtr<int>&&>::Type::stored_type,
mozilla::UniquePtr<int>>,
"ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be "
"UniquePtr<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<
mozilla::UniquePtr<int>&&>::Type::passed_type,
mozilla::UniquePtr<int>&&>,
"ParameterStorage<UniquePtr<int>&&>::Type::passed_type should be "
"UniquePtr<int>&&");
{
mozilla::UniquePtr<int> upi;
r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
"TestThreadUtils::ThreadUtilsObject::Test1upi", rpt,
&ThreadUtilsObject::Test1upi, std::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<T>"
// syntax.
static_assert(
std::is_same_v<::detail::ParameterStorage<StoreCopyPassByRRef<
mozilla::UniquePtr<int>>>::Type::stored_type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>,
"ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_"
"type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
static_assert(
std::is_same_v<::detail::ParameterStorage<StoreCopyPassByRRef<
mozilla::UniquePtr<int>>>::Type::stored_type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>,
"ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_"
"type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
static_assert(
std::is_same_v<::detail::ParameterStorage<StoreCopyPassByRRef<
mozilla::UniquePtr<int>>>::Type::stored_type,
mozilla::UniquePtr<int>>,
"ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_"
"type should be UniquePtr<int>");
static_assert(
std::is_same_v<::detail::ParameterStorage<StoreCopyPassByRRef<
mozilla::UniquePtr<int>>>::Type::passed_type,
mozilla::UniquePtr<int>&&>,
"ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::passed_"
"type should be UniquePtr<int>&&");
{
mozilla::UniquePtr<int> upi;
r1 = NewRunnableMethod<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>(
"TestThreadUtils::ThreadUtilsObject::Test1upi", rpt,
&ThreadUtilsObject::Test1upi, std::move(upi));
}
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(-1, rpt->mA0);
// Unique pointer as xvalue.
{
mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
"TestThreadUtils::ThreadUtilsObject::Test1upi", rpt,
&ThreadUtilsObject::Test1upi, std::move(upi));
}
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(1, rpt->mA0);
{
mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
r1 = NewRunnableMethod<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>(
"TestThreadUtils::ThreadUtilsObject::Test1upi", rpt,
&ThreadUtilsObject::Test1upi, std::move(upi));
}
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(1, rpt->mA0);
// Unique pointer as prvalue.
r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
"TestThreadUtils::ThreadUtilsObject::Test1upi", rpt,
&ThreadUtilsObject::Test1upi, mozilla::MakeUnique<int>(2));
r1->Run();
EXPECT_EQ(count += 2, rpt->mCount);
EXPECT_EQ(2, rpt->mA0);
// Unique pointer as lvalue to lref.
{
mozilla::UniquePtr<int> upi;
r1 = NewRunnableMethod<mozilla::UniquePtr<int>&>(
"TestThreadUtils::ThreadUtilsObject::Test1rupi", 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<nsIRunnable> 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<StoreCopyPassByValue<Spy>>(&TestByValue, s)\n",
__LINE__);
}
r2 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
"TestThreadUtils::ThreadUtilsObject::TestByValue", 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<StoreCopyPassByValue<Spy>>(&TestByValue, "
"Spy(11))\n",
__LINE__);
}
nsCOMPtr<nsIRunnable> r3 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
"TestThreadUtils::ThreadUtilsObject::TestByValue", 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<nsIRunnable> r4;
{
Spy s(12);
EXPECT_EQ(1, gConstructions);
EXPECT_EQ(1, gAlive);
Spy::ClearActions();
r4 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
"TestThreadUtils::ThreadUtilsObject::TestByValue", rpt,
&ThreadUtilsObject::TestByValue, std::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<nsIRunnable> 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<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef,"
" s)\n",
__LINE__);
}
r5 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
"TestThreadUtils::ThreadUtilsObject::TestByConstLRef", 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<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, "
"Spy(21))\n",
__LINE__);
}
nsCOMPtr<nsIRunnable> r6 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
"TestThreadUtils::ThreadUtilsObject::TestByConstLRef", 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<nsIRunnable> 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<StoreCopyPassByRRef<Spy>>(&TestByRRef, s)\n",
__LINE__);
}
r7 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
"TestThreadUtils::ThreadUtilsObject::TestByRRef", 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<StoreCopyPassByRRef<Spy>>(&TestByRRef, "
"Spy(31))\n",
__LINE__);
}
nsCOMPtr<nsIRunnable> r8 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
"TestThreadUtils::ThreadUtilsObject::TestByRRef", 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<Spy&>(&TestByLRef, s)\n", __LINE__);
}
nsCOMPtr<nsIRunnable> r9 = NewRunnableMethod<Spy&>(
"TestThreadUtils::ThreadUtilsObject::TestByLRef", 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<nsIRunnable> r10;
SpyWithISupports* ptr = 0;
{ // Block around RefPtr<Spy> lifetime.
if (gDebug) {
printf("%d - RefPtr<SpyWithISupports> s(new SpyWithISupports(45))\n",
__LINE__);
}
RefPtr<SpyWithISupports> s(new SpyWithISupports(45));
ptr = s.get();
EXPECT_EQ(1, gConstructions);
EXPECT_EQ(1, gAlive);
if (gDebug) {
printf(
"%d - r10 = "
"NewRunnableMethod<StoreRefPtrPassByPtr<Spy>>(&TestByRRef, "
"s.get())\n",
__LINE__);
}
r10 = NewRunnableMethod<StoreRefPtrPassByPtr<SpyWithISupports>>(
"TestThreadUtils::ThreadUtilsObject::TestByPointer", rpt,
&ThreadUtilsObject::TestByPointer, s.get());
EXPECT_LE(0, gAllConstructions);
EXPECT_EQ(1, gAlive);
Spy::ClearActions();
if (gDebug) {
printf("%d - End block with RefPtr<Spy> 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<Spy*>(&TestByPointer, s)\n",
__LINE__);
}
nsCOMPtr<nsIRunnable> r11 = NewRunnableMethod<Spy*>(
"TestThreadUtils::ThreadUtilsObject::TestByPointer", 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<Spy*>(&TestByPointer, s)\n",
__LINE__);
}
nsCOMPtr<nsIRunnable> r12 = NewRunnableMethod<const Spy*>(
"TestThreadUtils::ThreadUtilsObject::TestByPointerToConst", 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);
}