Bug 1605119 - Add NS_NewCancelableRunnableFunction. r=froydnj
Differential Revision: https://phabricator.services.mozilla.com/D57799
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
#include "nsComponentManagerUtils.h"
|
#include "nsComponentManagerUtils.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "mozilla/IdleTaskRunner.h"
|
#include "mozilla/IdleTaskRunner.h"
|
||||||
|
#include "mozilla/RefCounted.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
@@ -186,6 +187,10 @@ struct TestCopyMove {
|
|||||||
int* mMoveCounter;
|
int* mMoveCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TestRefCounted : RefCounted<TestRefCounted> {
|
||||||
|
MOZ_DECLARE_REFCOUNTED_TYPENAME(TestRefCounted);
|
||||||
|
};
|
||||||
|
|
||||||
static void Expect(const char* aContext, int aCounter, int aMaxExpected) {
|
static void Expect(const char* aContext, int aCounter, int aMaxExpected) {
|
||||||
EXPECT_LE(aCounter, aMaxExpected) << aContext;
|
EXPECT_LE(aCounter, aMaxExpected) << aContext;
|
||||||
}
|
}
|
||||||
@@ -198,24 +203,44 @@ static void ExpectRunnableName(Runnable* aRunnable, const char* aExpectedName) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TestNewRunnableFunction(bool aNamed) {
|
struct BasicRunnableFactory {
|
||||||
// Test NS_NewRunnableFunction with copyable-only function object.
|
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;
|
int copyCounter = 0;
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||||
{
|
{
|
||||||
TestCopyWithNoMove tracker(©Counter);
|
TestCopyWithNoMove tracker(©Counter);
|
||||||
trackedRunnable =
|
trackedRunnable = aNamed ? RunnableFactory::Create("unused", tracker)
|
||||||
aNamed ? NS_NewRunnableFunction("unused", tracker)
|
: RunnableFactory::Create(
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
|
"TestNewRunnableFunction", tracker);
|
||||||
// Original 'tracker' is destroyed here.
|
// Original 'tracker' is destroyed here.
|
||||||
}
|
}
|
||||||
// Verify that the runnable contains a non-destroyed function object.
|
// Verify that the runnable contains a non-destroyed function object.
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect(
|
Expect(
|
||||||
"NS_NewRunnableFunction with copyable-only (and no move) function, "
|
"RunnableFactory with copyable-only (and no move) function, "
|
||||||
"copies",
|
"copies",
|
||||||
copyCounter, 1);
|
copyCounter, 1);
|
||||||
}
|
}
|
||||||
@@ -227,37 +252,37 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
// Passing as rvalue, but using copy.
|
// Passing as rvalue, but using copy.
|
||||||
// (TestCopyWithDeletedMove wouldn't allow this.)
|
// (TestCopyWithDeletedMove wouldn't allow this.)
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction("unused",
|
aNamed ? RunnableFactory::Create("unused",
|
||||||
TestCopyWithNoMove(©Counter))
|
TestCopyWithNoMove(©Counter))
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||||
TestCopyWithNoMove(©Counter));
|
TestCopyWithNoMove(©Counter));
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect(
|
Expect(
|
||||||
"NS_NewRunnableFunction with copyable-only (and no move) function "
|
"RunnableFactory with copyable-only (and no move) function "
|
||||||
"rvalue, copies",
|
"rvalue, copies",
|
||||||
copyCounter, 1);
|
copyCounter, 1);
|
||||||
}
|
}
|
||||||
{
|
if constexpr (RunnableFactory::SupportsCopyWithDeletedMove) {
|
||||||
int copyCounter = 0;
|
int copyCounter = 0;
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||||
{
|
{
|
||||||
TestCopyWithDeletedMove tracker(©Counter);
|
TestCopyWithDeletedMove tracker(©Counter);
|
||||||
trackedRunnable =
|
trackedRunnable = aNamed ? RunnableFactory::Create("unused", tracker)
|
||||||
aNamed ? NS_NewRunnableFunction("unused", tracker)
|
: RunnableFactory::Create(
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
|
"TestNewRunnableFunction", tracker);
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect(
|
Expect(
|
||||||
"NS_NewRunnableFunction with copyable-only (and deleted move) "
|
"RunnableFactory with copyable-only (and deleted move) "
|
||||||
"function, copies",
|
"function, copies",
|
||||||
copyCounter, 1);
|
copyCounter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test NS_NewRunnableFunction with movable-only function object.
|
// Test RunnableFactory with movable-only function object.
|
||||||
{
|
{
|
||||||
int moveCounter = 0;
|
int moveCounter = 0;
|
||||||
{
|
{
|
||||||
@@ -265,14 +290,13 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
{
|
{
|
||||||
TestMove tracker(&moveCounter);
|
TestMove tracker(&moveCounter);
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction("unused", std::move(tracker))
|
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||||
std::move(tracker));
|
std::move(tracker));
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect("NS_NewRunnableFunction with movable-only function, moves",
|
Expect("RunnableFactory with movable-only function, moves", moveCounter, 1);
|
||||||
moveCounter, 1);
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int moveCounter = 0;
|
int moveCounter = 0;
|
||||||
@@ -280,17 +304,17 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||||
{
|
{
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction("unused", TestMove(&moveCounter))
|
aNamed ? RunnableFactory::Create("unused", TestMove(&moveCounter))
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||||
TestMove(&moveCounter));
|
TestMove(&moveCounter));
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect("NS_NewRunnableFunction with movable-only function rvalue, moves",
|
Expect("RunnableFactory with movable-only function rvalue, moves",
|
||||||
moveCounter, 1);
|
moveCounter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test NS_NewRunnableFunction with copyable&movable function object.
|
// Test RunnableFactory with copyable&movable function object.
|
||||||
{
|
{
|
||||||
int copyCounter = 0;
|
int copyCounter = 0;
|
||||||
int moveCounter = 0;
|
int moveCounter = 0;
|
||||||
@@ -299,16 +323,16 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
{
|
{
|
||||||
TestCopyMove tracker(©Counter, &moveCounter);
|
TestCopyMove tracker(©Counter, &moveCounter);
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction("unused", std::move(tracker))
|
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||||
std::move(tracker));
|
std::move(tracker));
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect("NS_NewRunnableFunction with copyable&movable function, copies",
|
Expect("RunnableFactory with copyable&movable function, copies",
|
||||||
copyCounter, 0);
|
copyCounter, 0);
|
||||||
Expect("NS_NewRunnableFunction with copyable&movable function, moves",
|
Expect("RunnableFactory with copyable&movable function, moves", moveCounter,
|
||||||
moveCounter, 1);
|
1);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int copyCounter = 0;
|
int copyCounter = 0;
|
||||||
@@ -317,23 +341,21 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||||
{
|
{
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction(
|
aNamed ? RunnableFactory::Create(
|
||||||
"unused", TestCopyMove(©Counter, &moveCounter))
|
"unused", TestCopyMove(©Counter, &moveCounter))
|
||||||
: NS_NewRunnableFunction(
|
: RunnableFactory::Create(
|
||||||
"TestNewRunnableFunction",
|
"TestNewRunnableFunction",
|
||||||
TestCopyMove(©Counter, &moveCounter));
|
TestCopyMove(©Counter, &moveCounter));
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect(
|
Expect("RunnableFactory with copyable&movable function rvalue, copies",
|
||||||
"NS_NewRunnableFunction with copyable&movable function rvalue, copies",
|
copyCounter, 0);
|
||||||
copyCounter, 0);
|
Expect("RunnableFactory with copyable&movable function rvalue, moves",
|
||||||
Expect(
|
moveCounter, 1);
|
||||||
"NS_NewRunnableFunction 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;
|
int copyCounter = 0;
|
||||||
{
|
{
|
||||||
@@ -342,15 +364,16 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
TestCopyWithNoMove tracker(©Counter);
|
TestCopyWithNoMove tracker(©Counter);
|
||||||
// Expect 2 copies (here -> local lambda -> runnable lambda).
|
// Expect 2 copies (here -> local lambda -> runnable lambda).
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction("unused",
|
aNamed
|
||||||
[tracker]() mutable { tracker(); })
|
? RunnableFactory::Create("unused",
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
[tracker]() mutable { tracker(); })
|
||||||
[tracker]() mutable { tracker(); });
|
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||||
|
[tracker]() mutable { tracker(); });
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect(
|
Expect(
|
||||||
"NS_NewRunnableFunction with copyable-only (and no move) capture, "
|
"RunnableFactory with copyable-only (and no move) capture, "
|
||||||
"copies",
|
"copies",
|
||||||
copyCounter, 2);
|
copyCounter, 2);
|
||||||
}
|
}
|
||||||
@@ -362,15 +385,16 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
TestCopyWithDeletedMove tracker(©Counter);
|
TestCopyWithDeletedMove tracker(©Counter);
|
||||||
// Expect 2 copies (here -> local lambda -> runnable lambda).
|
// Expect 2 copies (here -> local lambda -> runnable lambda).
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction("unused",
|
aNamed
|
||||||
[tracker]() mutable { tracker(); })
|
? RunnableFactory::Create("unused",
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
[tracker]() mutable { tracker(); })
|
||||||
[tracker]() mutable { tracker(); });
|
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||||
|
[tracker]() mutable { tracker(); });
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect(
|
Expect(
|
||||||
"NS_NewRunnableFunction with copyable-only (and deleted move) capture, "
|
"RunnableFactory with copyable-only (and deleted move) capture, "
|
||||||
"copies",
|
"copies",
|
||||||
copyCounter, 2);
|
copyCounter, 2);
|
||||||
}
|
}
|
||||||
@@ -378,7 +402,7 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
// Note: Not possible to use move-only captures.
|
// Note: Not possible to use move-only captures.
|
||||||
// (Until we can use C++14 generalized lambda 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 copyCounter = 0;
|
||||||
int moveCounter = 0;
|
int moveCounter = 0;
|
||||||
@@ -387,29 +411,30 @@ static void TestNewRunnableFunction(bool aNamed) {
|
|||||||
{
|
{
|
||||||
TestCopyMove tracker(©Counter, &moveCounter);
|
TestCopyMove tracker(©Counter, &moveCounter);
|
||||||
trackedRunnable =
|
trackedRunnable =
|
||||||
aNamed ? NS_NewRunnableFunction("unused",
|
aNamed
|
||||||
[tracker]() mutable { tracker(); })
|
? RunnableFactory::Create("unused",
|
||||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
[tracker]() mutable { tracker(); })
|
||||||
[tracker]() mutable { tracker(); });
|
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||||
|
[tracker]() mutable { tracker(); });
|
||||||
// Expect 1 copy (here -> local lambda) and 1 move (local -> runnable
|
// Expect 1 copy (here -> local lambda) and 1 move (local -> runnable
|
||||||
// lambda).
|
// lambda).
|
||||||
}
|
}
|
||||||
trackedRunnable->Run();
|
trackedRunnable->Run();
|
||||||
}
|
}
|
||||||
Expect("NS_NewRunnableFunction with copyable&movable capture, copies",
|
Expect("RunnableFactory with copyable&movable capture, copies", copyCounter,
|
||||||
copyCounter, 1);
|
1);
|
||||||
Expect("NS_NewRunnableFunction with copyable&movable capture, moves",
|
Expect("RunnableFactory with copyable&movable capture, moves", moveCounter,
|
||||||
moveCounter, 1);
|
1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ThreadUtils, NewRunnableFunction)
|
TEST(ThreadUtils, NewRunnableFunction)
|
||||||
{ TestNewRunnableFunction(/*aNamed*/ false); }
|
{ TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ false); }
|
||||||
|
|
||||||
TEST(ThreadUtils, NewNamedRunnableFunction)
|
TEST(ThreadUtils, NewNamedRunnableFunction)
|
||||||
{
|
{
|
||||||
// The named overload shall behave identical to the non-named counterpart.
|
// The named overload shall behave identical to the non-named counterpart.
|
||||||
TestNewRunnableFunction(/*aNamed*/ true);
|
TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ true);
|
||||||
|
|
||||||
// Test naming.
|
// 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) {
|
static void TestNewRunnableMethod(bool aNamed) {
|
||||||
memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool));
|
memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool));
|
||||||
// Scope the smart ptrs so that the runnables need to hold on to whatever they
|
// Scope the smart ptrs so that the runnables need to hold on to whatever they
|
||||||
|
|||||||
@@ -659,6 +659,47 @@ already_AddRefed<mozilla::Runnable> NS_NewRunnableFunction(
|
|||||||
aName, std::forward<Function>(aFunction)));
|
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 mozilla {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user