Bug 1605119 - Add NS_NewCancelableRunnableFunction. r=froydnj

Differential Revision: https://phabricator.services.mozilla.com/D57799

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Simon Giesecke 2019-12-19 17:13:06 +00:00
parent cc63a32cca
commit 4170b9c2da
2 changed files with 161 additions and 63 deletions

View File

@ -5,6 +5,7 @@
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/IdleTaskRunner.h"
#include "mozilla/RefCounted.h"
#include "mozilla/UniquePtr.h"
#include "gtest/gtest.h"
@ -186,6 +187,10 @@ struct TestCopyMove {
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;
}
@ -198,24 +203,44 @@ static void ExpectRunnableName(Runnable* aRunnable, const char* aExpectedName) {
#endif
}
static void TestNewRunnableFunction(bool aNamed) {
// Test NS_NewRunnableFunction with copyable-only function object.
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 ? NS_NewRunnableFunction("unused", tracker)
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
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(
"NS_NewRunnableFunction with copyable-only (and no move) function, "
"RunnableFactory with copyable-only (and no move) function, "
"copies",
copyCounter, 1);
}
@ -227,37 +252,37 @@ static void TestNewRunnableFunction(bool aNamed) {
// Passing as rvalue, but using copy.
// (TestCopyWithDeletedMove wouldn't allow this.)
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused",
TestCopyWithNoMove(&copyCounter))
: NS_NewRunnableFunction("TestNewRunnableFunction",
TestCopyWithNoMove(&copyCounter));
aNamed ? RunnableFactory::Create("unused",
TestCopyWithNoMove(&copyCounter))
: RunnableFactory::Create("TestNewRunnableFunction",
TestCopyWithNoMove(&copyCounter));
}
trackedRunnable->Run();
}
Expect(
"NS_NewRunnableFunction with copyable-only (and no move) function "
"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 ? NS_NewRunnableFunction("unused", tracker)
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
trackedRunnable = aNamed ? RunnableFactory::Create("unused", tracker)
: RunnableFactory::Create(
"TestNewRunnableFunction", tracker);
}
trackedRunnable->Run();
}
Expect(
"NS_NewRunnableFunction with copyable-only (and deleted move) "
"RunnableFactory with copyable-only (and deleted move) "
"function, copies",
copyCounter, 1);
}
// Test NS_NewRunnableFunction with movable-only function object.
// Test RunnableFactory with movable-only function object.
{
int moveCounter = 0;
{
@ -265,14 +290,13 @@ static void TestNewRunnableFunction(bool aNamed) {
{
TestMove tracker(&moveCounter);
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused", std::move(tracker))
: NS_NewRunnableFunction("TestNewRunnableFunction",
std::move(tracker));
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
: RunnableFactory::Create("TestNewRunnableFunction",
std::move(tracker));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with movable-only function, moves",
moveCounter, 1);
Expect("RunnableFactory with movable-only function, moves", moveCounter, 1);
}
{
int moveCounter = 0;
@ -280,17 +304,17 @@ static void TestNewRunnableFunction(bool aNamed) {
nsCOMPtr<nsIRunnable> trackedRunnable;
{
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused", TestMove(&moveCounter))
: NS_NewRunnableFunction("TestNewRunnableFunction",
TestMove(&moveCounter));
aNamed ? RunnableFactory::Create("unused", TestMove(&moveCounter))
: RunnableFactory::Create("TestNewRunnableFunction",
TestMove(&moveCounter));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with movable-only function rvalue, moves",
Expect("RunnableFactory with movable-only function rvalue, moves",
moveCounter, 1);
}
// Test NS_NewRunnableFunction with copyable&movable function object.
// Test RunnableFactory with copyable&movable function object.
{
int copyCounter = 0;
int moveCounter = 0;
@ -299,16 +323,16 @@ static void TestNewRunnableFunction(bool aNamed) {
{
TestCopyMove tracker(&copyCounter, &moveCounter);
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused", std::move(tracker))
: NS_NewRunnableFunction("TestNewRunnableFunction",
std::move(tracker));
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
: RunnableFactory::Create("TestNewRunnableFunction",
std::move(tracker));
}
trackedRunnable->Run();
}
Expect("NS_NewRunnableFunction with copyable&movable function, copies",
Expect("RunnableFactory with copyable&movable function, copies",
copyCounter, 0);
Expect("NS_NewRunnableFunction with copyable&movable function, moves",
moveCounter, 1);
Expect("RunnableFactory with copyable&movable function, moves", moveCounter,
1);
}
{
int copyCounter = 0;
@ -317,23 +341,21 @@ static void TestNewRunnableFunction(bool aNamed) {
nsCOMPtr<nsIRunnable> trackedRunnable;
{
trackedRunnable =
aNamed ? NS_NewRunnableFunction(
aNamed ? RunnableFactory::Create(
"unused", TestCopyMove(&copyCounter, &moveCounter))
: NS_NewRunnableFunction(
: RunnableFactory::Create(
"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);
Expect("RunnableFactory with copyable&movable function rvalue, copies",
copyCounter, 0);
Expect("RunnableFactory with copyable&movable function rvalue, moves",
moveCounter, 1);
}
// Test NS_NewRunnableFunction with copyable-only lambda capture.
// Test RunnableFactory with copyable-only lambda capture.
{
int copyCounter = 0;
{
@ -342,15 +364,16 @@ static void TestNewRunnableFunction(bool aNamed) {
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(); });
aNamed
? RunnableFactory::Create("unused",
[tracker]() mutable { tracker(); })
: RunnableFactory::Create("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
}
trackedRunnable->Run();
}
Expect(
"NS_NewRunnableFunction with copyable-only (and no move) capture, "
"RunnableFactory with copyable-only (and no move) capture, "
"copies",
copyCounter, 2);
}
@ -362,15 +385,16 @@ static void TestNewRunnableFunction(bool aNamed) {
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(); });
aNamed
? RunnableFactory::Create("unused",
[tracker]() mutable { tracker(); })
: RunnableFactory::Create("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
}
trackedRunnable->Run();
}
Expect(
"NS_NewRunnableFunction with copyable-only (and deleted move) capture, "
"RunnableFactory with copyable-only (and deleted move) capture, "
"copies",
copyCounter, 2);
}
@ -378,7 +402,7 @@ static void TestNewRunnableFunction(bool aNamed) {
// 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.
// Test RunnableFactory with copyable&movable lambda capture.
{
int copyCounter = 0;
int moveCounter = 0;
@ -387,29 +411,30 @@ static void TestNewRunnableFunction(bool aNamed) {
{
TestCopyMove tracker(&copyCounter, &moveCounter);
trackedRunnable =
aNamed ? NS_NewRunnableFunction("unused",
[tracker]() mutable { tracker(); })
: NS_NewRunnableFunction("TestNewRunnableFunction",
[tracker]() mutable { tracker(); });
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("NS_NewRunnableFunction with copyable&movable capture, copies",
copyCounter, 1);
Expect("NS_NewRunnableFunction with copyable&movable capture, moves",
moveCounter, 1);
Expect("RunnableFactory with copyable&movable capture, copies", copyCounter,
1);
Expect("RunnableFactory with copyable&movable capture, moves", moveCounter,
1);
}
}
TEST(ThreadUtils, NewRunnableFunction)
{ TestNewRunnableFunction(/*aNamed*/ false); }
{ TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ false); }
TEST(ThreadUtils, NewNamedRunnableFunction)
{
// The named overload shall behave identical to the non-named counterpart.
TestNewRunnableFunction(/*aNamed*/ true);
TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ true);
// Test naming.
{
@ -420,6 +445,38 @@ TEST(ThreadUtils, NewNamedRunnableFunction)
}
}
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

View File

@ -659,6 +659,47 @@ already_AddRefed<mozilla::Runnable> NS_NewRunnableFunction(
aName, std::forward<Function>(aFunction)));
}
// Creates a new object implementing nsIRunnable and nsICancelableRunnable,
// which runs a given function on Run and clears the stored function object on a
// call to `Cancel` (and thus destroys all objects it holds).
template <typename Function>
already_AddRefed<mozilla::CancelableRunnable> NS_NewCancelableRunnableFunction(
const char* aName, Function&& aFunc) {
class FuncCancelableRunnable final : public mozilla::CancelableRunnable {
public:
static_assert(std::is_void_v<decltype(
std::declval<std::remove_reference_t<Function>>()())>);
NS_INLINE_DECL_REFCOUNTING_INHERITED(FuncCancelableRunnable,
CancelableRunnable)
explicit FuncCancelableRunnable(const char* aName, Function&& aFunc)
: CancelableRunnable{aName},
mFunc{mozilla::Some(std::forward<Function>(aFunc))} {}
NS_IMETHOD Run() override {
MOZ_ASSERT(mFunc);
(*mFunc)();
return NS_OK;
}
nsresult Cancel() override {
mFunc.reset();
return NS_OK;
}
private:
~FuncCancelableRunnable() = default;
mozilla::Maybe<std::remove_reference_t<Function>> mFunc;
};
return mozilla::MakeAndAddRef<FuncCancelableRunnable>(
aName, std::forward<Function>(aFunc));
}
namespace mozilla {
namespace detail {