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

1857 строки
71 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 "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/IdleTaskRunner.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() {}
};
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() {}
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; };
// 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; };
// 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; }
~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; };
TestCopyMove(TestCopyMove&& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { a.mMoveCounter = nullptr; ++mMoveCounter; }
~TestCopyMove() { mCopyCounter = nullptr; mMoveCounter = nullptr; }
void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); MOZ_RELEASE_ASSERT(mMoveCounter); }
int* mCopyCounter;
int* mMoveCounter;
};
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
}
static void TestNewRunnableFunction(bool aNamed)
{
// Test NS_NewRunnableFunction with copyable-only function object.
{
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyWithNoMove tracker(&copyCounter);
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused", tracker)
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
// Original 'tracker' is destroyed here.
}
// Verify that the runnable contains a non-destroyed function object.
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction 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
? NS_NewRunnableFunction("unused", TestCopyWithNoMove(&copyCounter))
: NS_NewRunnableFunction("TestNewRunnableFunction",
TestCopyWithNoMove(&copyCounter));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with copyable-only (and no move) function rvalue, copies",
copyCounter, 1);
}
{
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyWithDeletedMove tracker(&copyCounter);
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused", tracker)
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with copyable-only (and deleted move) function, copies",
copyCounter, 1);
}
// Test NS_NewRunnableFunction with movable-only function object.
{
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestMove tracker(&moveCounter);
trackedRunnable =
aNamed
? NS_NewRunnableFunction("unused", Move(tracker))
: NS_NewRunnableFunction("TestNewRunnableFunction", Move(tracker));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with movable-only function, moves",
moveCounter, 1);
}
{
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused", TestMove(&moveCounter))
: NS_NewRunnableFunction("TestNewRunnableFunction",
TestMove(&moveCounter));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with movable-only function rvalue, moves",
moveCounter, 1);
}
// Test NS_NewRunnableFunction with copyable&movable function object.
{
int copyCounter = 0;
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyMove tracker(&copyCounter, &moveCounter);
trackedRunnable =
aNamed
? NS_NewRunnableFunction("unused", Move(tracker))
: NS_NewRunnableFunction("TestNewRunnableFunction", Move(tracker));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with copyable&movable function, copies",
copyCounter, 0);
Expect("NS_NewRunnableFunction with copyable&movable function, moves",
moveCounter, 1);
}
{
int copyCounter = 0;
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
trackedRunnable =
aNamed
? NS_NewRunnableFunction("unused",
TestCopyMove(&copyCounter, &moveCounter))
: NS_NewRunnableFunction("TestNewRunnableFunction",
TestCopyMove(&copyCounter, &moveCounter));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with copyable&movable function rvalue, copies",
copyCounter, 0);
Expect("NS_NewRunnableFunction with copyable&movable function rvalue, moves",
moveCounter, 1);
}
// Test NS_NewRunnableFunction with copyable-only lambda capture.
{
int copyCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyWithNoMove tracker(&copyCounter);
// Expect 2 copies (here -> local lambda -> runnable lambda).
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused",
[tracker]() mutable { tracker(); })
: NS_NewRunnableFunction("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction 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 ? NS_NewRunnableFunction("unused",
[tracker]() mutable { tracker(); })
: NS_NewRunnableFunction("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction 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 NS_NewRunnableFunction with copyable&movable lambda capture.
{
int copyCounter = 0;
int moveCounter = 0;
{
nsCOMPtr<nsIRunnable> trackedRunnable;
{
TestCopyMove tracker(&copyCounter, &moveCounter);
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused",
[tracker]() mutable { tracker(); })
: NS_NewRunnableFunction("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
// Expect 1 copy (here -> local lambda) and 1 move (local -> runnable lambda).
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with copyable&movable capture, copies",
copyCounter, 1);
Expect("NS_NewRunnableFunction with copyable&movable capture, moves",
moveCounter, 1);
}
}
TEST(ThreadUtils, NewRunnableFunction)
{
TestNewRunnableFunction(/*aNamed*/false);
}
TEST(ThreadUtils, NewNamedRunnableFunction)
{
// The named overload shall behave identical to the non-named counterpart.
TestNewRunnableFunction(/*aNamed*/true);
// Test naming.
{
const char* expectedName = "NamedRunnable";
RefPtr<Runnable> NamedRunnable =
NS_NewRunnableFunction(expectedName, [] {});
ExpectRunnableName(NamedRunnable, expectedName);
}
}
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() {}
};
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() {}
};
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_IdleDispatchToCurrentThread(
NewIdleRunnableMethodWithTimer("IdleObject::Method5", this, &IdleObject::Method5), 50);
NS_IdleDispatchToCurrentThread(
NewRunnableMethod("IdleObject::Method6", this, &IdleObject::Method6),
100);
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() {}
};
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_IdleDispatchToCurrentThread(
NewIdleRunnableMethod("IdleObject::Method1", idle, &IdleObject::Method1));
NS_IdleDispatchToCurrentThread(
NewIdleRunnableMethodWithTimer("IdleObject::Method2", idle, &IdleObject::Method2), 60000);
NS_IdleDispatchToCurrentThread(
NewIdleRunnableMethod("IdleObject::Method7", idle, &IdleObject::Method7));
NS_IdleDispatchToCurrentThread(NewIdleRunnableMethod<const char*, uint32_t>(
"IdleObject::CheckExecutedMethods", idle, &IdleObject::CheckExecutedMethods, "final", 8));
NS_IdleDispatchToCurrentThread(NewIdleRunnableMethod(
"IdleObjectWithoutSetDeadline::Method",
idleNoSetDeadline, &IdleObjectWithoutSetDeadline::Method));
NS_IdleDispatchToCurrentThread(NewIdleRunnableMethod(
"IdleObjectInheritedSetDeadline::Method",
idleInheritedSetDeadline, &IdleObjectInheritedSetDeadline::Method));
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 timer with no "exit" condition requires an explicit
// Cancel() call.
runner1->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(mozilla::IsSame<int, mozilla::RemoveSmartPointer<int>::Type>::value,
"RemoveSmartPointer<int>::Type should be int");
static_assert(mozilla::IsSame<int*, mozilla::RemoveSmartPointer<int*>::Type>::value,
"RemoveSmartPointer<int*>::Type should be int*");
static_assert(mozilla::IsSame<UniquePtr<int>, mozilla::RemoveSmartPointer<UniquePtr<int>>::Type>::value,
"RemoveSmartPointer<UniquePtr<int>>::Type should be UniquePtr<int>");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<RefPtr<int>>::Type>::value,
"RemoveSmartPointer<RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const RefPtr<int>>::Type>::value,
"RemoveSmartPointer<const RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<volatile RefPtr<int>>::Type>::value,
"RemoveSmartPointer<volatile RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const volatile RefPtr<int>>::Type>::value,
"RemoveSmartPointer<const volatile RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<nsCOMPtr<int>>::Type>::value,
"RemoveSmartPointer<nsCOMPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const nsCOMPtr<int>>::Type>::value,
"RemoveSmartPointer<const nsCOMPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<volatile nsCOMPtr<int>>::Type>::value,
"RemoveSmartPointer<volatile nsCOMPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveSmartPointer<const volatile nsCOMPtr<int>>::Type>::value,
"RemoveSmartPointer<const volatile nsCOMPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<int>::Type>::value,
"RemoveRawOrSmartPointer<int>::Type should be int");
static_assert(mozilla::IsSame<UniquePtr<int>, mozilla::RemoveRawOrSmartPointer<UniquePtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<UniquePtr<int>>::Type should be UniquePtr<int>");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<int*>::Type>::value,
"RemoveRawOrSmartPointer<int*>::Type should be int");
static_assert(mozilla::IsSame<const int, mozilla::RemoveRawOrSmartPointer<const int*>::Type>::value,
"RemoveRawOrSmartPointer<const int*>::Type should be const int");
static_assert(mozilla::IsSame<volatile int, mozilla::RemoveRawOrSmartPointer<volatile int*>::Type>::value,
"RemoveRawOrSmartPointer<volatile int*>::Type should be volatile int");
static_assert(mozilla::IsSame<const volatile int, mozilla::RemoveRawOrSmartPointer<const volatile int*>::Type>::value,
"RemoveRawOrSmartPointer<const volatile int*>::Type should be const volatile int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<RefPtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const RefPtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<const RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<volatile RefPtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<volatile RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const volatile RefPtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<const volatile RefPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<nsCOMPtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<nsCOMPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const nsCOMPtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<const nsCOMPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<volatile nsCOMPtr<int>>::Type>::value,
"RemoveRawOrSmartPointer<volatile nsCOMPtr<int>>::Type should be int");
static_assert(mozilla::IsSame<int, mozilla::RemoveRawOrSmartPointer<const volatile nsCOMPtr<int>>::Type>::value,
"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 = 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)
{
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(
mozilla::IsSame< ::detail::ParameterStorage<int>::Type,
StoreCopyPassByConstLRef<int>>::value,
"detail::ParameterStorage<int>::Type should be StoreCopyPassByConstLRef<int>");
static_assert(
mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByValue<int>>::Type,
StoreCopyPassByValue<int>>::value,
"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(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type,
StorePtrPassByPtr<int>>::value,
"detail::ParameterStorage<int*>::Type should be StorePtrPassByPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const>::Type,
StorePtrPassByPtr<int>>::value,
"detail::ParameterStorage<int* const>::Type should be StorePtrPassByPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* volatile>::Type,
StorePtrPassByPtr<int>>::value,
"detail::ParameterStorage<int* volatile>::Type should be StorePtrPassByPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const volatile>::Type,
StorePtrPassByPtr<int>>::value,
"detail::ParameterStorage<int* const volatile>::Type should be StorePtrPassByPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::stored_type,
int*>::value,
"detail::ParameterStorage<int*>::Type::stored_type should be int*");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::passed_type,
int*>::value,
"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(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type,
StoreConstPtrPassByConstPtr<int>>::value,
"detail::ParameterStorage<const int*>::Type should be StoreConstPtrPassByConstPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const>::Type,
StoreConstPtrPassByConstPtr<int>>::value,
"detail::ParameterStorage<const int* const>::Type should be StoreConstPtrPassByConstPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* volatile>::Type,
StoreConstPtrPassByConstPtr<int>>::value,
"detail::ParameterStorage<const int* volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const volatile>::Type,
StoreConstPtrPassByConstPtr<int>>::value,
"detail::ParameterStorage<const int* const volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::stored_type,
const int*>::value,
"detail::ParameterStorage<const int*>::Type::stored_type should be const int*");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::passed_type,
const int*>::value,
"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(mozilla::IsSame<StoreCopyPassByPtr<int>::stored_type,
int>::value,
"StoreCopyPassByPtr<int>::stored_type should be int");
static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::passed_type,
int*>::value,
"StoreCopyPassByPtr<int>::passed_type should be int*");
{
int i = 1202;
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(mozilla::IsSame<StoreCopyPassByConstPtr<int>::stored_type,
int>::value,
"StoreCopyPassByConstPtr<int>::stored_type should be int");
static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::passed_type,
const int*>::value,
"StoreCopyPassByConstPtr<int>::passed_type should be const int*");
{
int i = 1203;
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(mozilla::IsSame< ::detail::ParameterStorage<StoreRefPtrPassByPtr<SpyWithISupports>>::Type,
StoreRefPtrPassByPtr<SpyWithISupports>>::value,
"ParameterStorage<StoreRefPtrPassByPtr<SpyWithISupports>>::Type should be StoreRefPtrPassByPtr<SpyWithISupports>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<SpyWithISupports*>::Type,
StoreRefPtrPassByPtr<SpyWithISupports>>::value,
"ParameterStorage<SpyWithISupports*>::Type should be StoreRefPtrPassByPtr<SpyWithISupports>");
static_assert(mozilla::IsSame<StoreRefPtrPassByPtr<SpyWithISupports>::stored_type,
RefPtr<SpyWithISupports>>::value,
"StoreRefPtrPassByPtr<SpyWithISupports>::stored_type should be RefPtr<SpyWithISupports>");
static_assert(mozilla::IsSame<StoreRefPtrPassByPtr<SpyWithISupports>::passed_type,
SpyWithISupports*>::value,
"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(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedFinal*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsRefCountedFinal>>::value,
"ParameterStorage<ThreadUtilsRefCountedFinal*>::Type should be StoreRefPtrPassByPtr<ThreadUtilsRefCountedFinal>");
static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedBase>::value,
"ThreadUtilsRefCountedBase has AddRef() and Release()");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedBase*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsRefCountedBase>>::value,
"ParameterStorage<ThreadUtilsRefCountedBase*>::Type should be StoreRefPtrPassByPtr<ThreadUtilsRefCountedBase>");
static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedDerived>::value,
"ThreadUtilsRefCountedDerived has AddRef() and Release()");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedDerived*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsRefCountedDerived>>::value,
"ParameterStorage<ThreadUtilsRefCountedDerived*>::Type should be StoreRefPtrPassByPtr<ThreadUtilsRefCountedDerived>");
static_assert(!::detail::HasRefCountMethods<ThreadUtilsNonRefCounted>::value,
"ThreadUtilsNonRefCounted doesn't have AddRef() and Release()");
static_assert(!mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsNonRefCounted*>::Type,
StoreRefPtrPassByPtr<ThreadUtilsNonRefCounted>>::value,
"ParameterStorage<ThreadUtilsNonRefCounted*>::Type should NOT be StoreRefPtrPassByPtr<ThreadUtilsNonRefCounted>");
// Lvalue reference.
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type,
StoreRefPassByLRef<int>>::value,
"ParameterStorage<int&>::Type should be StoreRefPassByLRef<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
StoreRefPassByLRef<int>::stored_type>::value,
"ParameterStorage<int&>::Type::stored_type should be StoreRefPassByLRef<int>::stored_type");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
int&>::value,
"ParameterStorage<int&>::Type::stored_type should be int&");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::passed_type,
int&>::value,
"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(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type,
StoreCopyPassByRRef<int>>::value,
"ParameterStorage<int&&>::Type should be StoreCopyPassByRRef<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
StoreCopyPassByRRef<int>::stored_type>::value,
"ParameterStorage<int&&>::Type::stored_type should be StoreCopyPassByRRef<int>::stored_type");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
int>::value,
"ParameterStorage<int&&>::Type::stored_type should be int");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::passed_type,
int&&>::value,
"ParameterStorage<int&&>::Type::passed_type should be int&&");
{
int i = 14;
r1 =
NewRunnableMethod<int&&>("TestThreadUtils::ThreadUtilsObject::Test1rri",
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<mozilla::UniquePtr<int>&&>::Type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::value,
"ParameterStorage<UniquePtr<int>&&>::Type should be StoreCopyPassByRRef<UniquePtr<int>>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
"ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
mozilla::UniquePtr<int>>::value,
"ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be UniquePtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::passed_type,
mozilla::UniquePtr<int>&&>::value,
"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,
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<T>" syntax.
static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
"ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
"ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
mozilla::UniquePtr<int>>::value,
"ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be UniquePtr<int>");
static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::passed_type,
mozilla::UniquePtr<int>&&>::value,
"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,
mozilla::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,
mozilla::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,
mozilla::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,
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<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);
}